diff --git a/.github/actions/build-adapter-provider/action.yml b/.github/actions/build-adapter-provider/action.yml index 60b9d08a37a0..b5fe54e83800 100644 --- a/.github/actions/build-adapter-provider/action.yml +++ b/.github/actions/build-adapter-provider/action.yml @@ -9,7 +9,7 @@ inputs: runs: using: composite steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: bins-wasi-preview1-component-adapter path: crates/wasi-preview1-component-adapter/provider/artefacts diff --git a/.github/actions/install-cargo-vet/action.yml b/.github/actions/install-cargo-vet/action.yml index 39ef0c0e63c4..823480e60ae2 100644 --- a/.github/actions/install-cargo-vet/action.yml +++ b/.github/actions/install-cargo-vet/action.yml @@ -10,7 +10,7 @@ inputs: runs: using: composite steps: - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ${{ runner.tool_cache }}/cargo-vet key: cargo-vet-bin-${{ inputs.version }} diff --git a/.github/actions/install-rust/action.yml b/.github/actions/install-rust/action.yml index 7c8cf2945817..f7622e22723c 100644 --- a/.github/actions/install-rust/action.yml +++ b/.github/actions/install-rust/action.yml @@ -79,4 +79,4 @@ runs: - name: Install the WASI target shell: bash - run: rustup target add wasm32-wasip1 wasm32-unknown-unknown + run: rustup target add wasm32-wasip2 wasm32-wasip1 wasm32-unknown-unknown diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml index cc03ff43d69e..ccf5a7b72409 100644 --- a/.github/workflows/cargo-audit.yml +++ b/.github/workflows/cargo-audit.yml @@ -8,10 +8,10 @@ jobs: env: CARGO_AUDIT_VERSION: 0.22.1 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ${{ runner.tool_cache }}/cargo-audit key: cargo-audit-${{ env.CARGO_AUDIT_VERSION }} diff --git a/.github/workflows/ci-cron-trigger.yml b/.github/workflows/ci-cron-trigger.yml index 2dfaab44b52d..de2780ad9c75 100644 --- a/.github/workflows/ci-cron-trigger.yml +++ b/.github/workflows/ci-cron-trigger.yml @@ -32,7 +32,7 @@ jobs: name: Trigger release branch CI runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true fetch-depth: 0 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 82e1804bf26e..9bb3688e99b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,7 +40,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -53,7 +53,7 @@ jobs: name: Check JS runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: npm install working-directory: ./crates/explorer - run: npm run lint @@ -68,7 +68,7 @@ jobs: name: Clang format runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - run: | @@ -89,7 +89,7 @@ jobs: if: needs.determine.outputs.audit runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -118,7 +118,7 @@ jobs: outputs: outcome: ${{ steps.vet.outcome }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -179,7 +179,7 @@ jobs: run-dwarf: ${{ steps.calculate.outputs.run-dwarf }} platform-checks: ${{ steps.calculate.outputs.platform-checks }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - id: calculate env: GH_TOKEN: ${{ github.token }} @@ -231,6 +231,9 @@ jobs: if grep -q pulley names.log; then echo test-nightly=true >> $GITHUB_OUTPUT fi + if grep -q gc names.log; then + echo test-gc-zeal=true >> $GITHUB_OUTPUT + fi fi matrix="$(node ./ci/build-test-matrix.js ./commits.log ./names.log $run_full)" echo "test-matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT @@ -244,6 +247,7 @@ jobs: echo test-capi=true >> $GITHUB_OUTPUT echo test-nightly=true >> $GITHUB_OUTPUT echo test-miri=true >> $GITHUB_OUTPUT + echo test-gc-zeal=true >> $GITHUB_OUTPUT echo audit=true >> $GITHUB_OUTPUT echo preview1-adapter=true >> $GITHUB_OUTPUT echo run-dwarf=true >> $GITHUB_OUTPUT @@ -264,7 +268,7 @@ jobs: RUSTDOCFLAGS: -Dwarnings --cfg docsrs OPENVINO_SKIP_LINKING: 1 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -281,7 +285,7 @@ jobs: - run: cmake --build target/c-api --target doc # install mdbook, build the docs, and test the docs - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ${{ runner.tool_cache }}/mdbook key: cargo-mdbook-${{ env.MDBOOK_VERSION }}-langtabs-${{ env.MDBOOK_LANGTABS_VERSION }} @@ -313,7 +317,7 @@ jobs: mv crates/c-api/html gh-pages/c-api mv target/doc gh-pages/api tar czf gh-pages.tar.gz gh-pages - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: gh-pages path: gh-pages.tar.gz @@ -387,6 +391,7 @@ jobs: -p wasmtime-cli --no-default-features --features compile,cranelift,component-model -p wasmtime-cli --no-default-features --features objdump -p wasmtime-cli --no-default-features --features wizer + -p wasmtime-cli --no-default-features --features gdbstub -p wasmtime-cli --all-features -p wasmtime-cli --features component-model @@ -413,6 +418,7 @@ jobs: - name: wasmtime-wasi-http checks: | -p wasmtime-wasi-http --no-default-features + -p wasmtime-wasi-http --no-default-features --features p2 -p wasmtime-wasi-http --no-default-features --features p3 -p wasmtime-wasi-http --no-default-features --features p3 --all-targets @@ -424,6 +430,15 @@ jobs: -p wasmtime-wasi --no-default-features --features p2 -p wasmtime-wasi --no-default-features --features p3 + - name: wasmtime-wasi-tls + checks: | + -p wasmtime-wasi-tls --no-default-features + -p wasmtime-wasi-tls --no-default-features --features p2 + -p wasmtime-wasi-tls --no-default-features --features p3 + -p wasmtime-wasi-tls --no-default-features --features rustls + -p wasmtime-wasi-tls --no-default-features --features nativetls + -p wasmtime-wasi-tls --no-default-features --features openssl + - name: wasmtime-wizer checks: | -p wasmtime-wizer --no-default-features @@ -434,7 +449,7 @@ jobs: -p wasmtime-wizer --no-default-features --all-features runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -453,7 +468,7 @@ jobs: needs: determine if: needs.determine.outputs.run-full steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -467,7 +482,7 @@ jobs: env: CARGO_NDK_VERSION: 2.12.2 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -484,7 +499,7 @@ jobs: env: CARGO_NDK_VERSION: 2.12.2 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -552,9 +567,10 @@ jobs: os: ubuntu-latest test: > cargo check -p wasmtime --no-default-features --features runtime,component-model && - cargo check -p wasmtime --no-default-features --features runtime,gc,component-model,async,debug-builtins && + cargo check -p wasmtime --no-default-features --features runtime,gc,component-model,pulley,async,debug,debug-builtins,demangle,anyhow && cargo check -p cranelift-control --no-default-features && cargo check -p cranelift-assembler-x64 --lib && + cargo check -p cranelift-codegen --no-default-features --features x86,arm64 && cargo check -p pulley-interpreter --features encode,decode,disas,interp && cargo check -p wasmtime-wasi-io --no-default-features # Use `cross` for illumos to have a C compiler/linker available. @@ -588,7 +604,7 @@ jobs: test: cargo check -p wasmtime --no-default-features --features runtime,gc,component-model,async env: ${{ matrix.env || fromJSON('{}') }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -612,7 +628,7 @@ jobs: name: Nightly tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -651,6 +667,29 @@ jobs: - run: cargo fuzz check --dev --fuzz-dir ./cranelift/assembler-x64/fuzz - run: cargo fuzz check --dev --fuzz-dir ./crates/wizer/fuzz + # Run GC-related tests with `cfg(gc_zeal)` set + gc_zeal: + needs: determine + if: needs.determine.outputs.test-gc-zeal + name: GC Zeal Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: true + - uses: ./.github/actions/install-rust + - name: Run GC tests with cfg(gc_zeal) enabled + env: + WASMTIME_TEST_GC_KEYWORDS: "gc drc any eq struct array i31 extern exn ref rec" + RUSTFLAGS: "--cfg gc_zeal" + run: | + set -e + + cargo test --test all -- $WASMTIME_TEST_GC_KEYWORDS + + # The wast test runner will read `WASMTIME_TEST_GC_KEYWORDS` on its own. + cargo test --test wast + # Perform all tests of the c-api test_capi: needs: determine @@ -693,7 +732,7 @@ jobs: flags: -DCMAKE_C_FLAGS=-fsanitize=address -DCMAKE_CXX_FLAGS=-fsanitize=address -DBUILD_SHARED_LIBS=ON -DBUILD_TESTS=ON name: Linux (ASAN) steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -730,7 +769,7 @@ jobs: fail-fast: ${{ github.event_name != 'pull_request' }} matrix: ${{ fromJson(needs.determine.outputs.test-matrix) }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -738,7 +777,7 @@ jobs: toolchain: ${{ matrix.rust }} # Install targets in order to build various tests throughout the repo - - run: rustup target add wasm32-wasip1 wasm32-unknown-unknown ${{ matrix.target }} + - run: rustup target add wasm32-wasip1 wasm32-unknown-unknown wasm32-wasip2 ${{ matrix.target }} - run: echo CARGO_BUILD_TARGET=${{ matrix.target }} >> $GITHUB_ENV if: matrix.target != '' @@ -768,13 +807,13 @@ jobs: echo CARGO_TARGET_${upcase}_LINKER=${{ matrix.gcc }} >> $GITHUB_ENV if: matrix.gcc != '' - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ${{ runner.tool_cache }}/qemu key: qemu-${{ matrix.target }}-${{ env.QEMU_BUILD_VERSION }}-patchcpuinfo if: matrix.qemu != '' - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ${{ runner.tool_cache }}/sde key: sde-${{ env.SDE_BUILD_VERSION }} @@ -917,7 +956,7 @@ jobs: needs: determine if: needs.determine.outputs.run-full steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -936,14 +975,14 @@ jobs: if: (runner.os == 'Windows') && (matrix.feature == 'winml') # Install Rust targets. - - run: rustup target add wasm32-wasip1 + - run: rustup target add wasm32-wasip1 wasm32-wasip2 # Run the tests! - run: cargo test -p wasmtime-wasi-nn --features ${{ matrix.feature }} - # Test `wasmtime-wasi-tls-nativetls` & `wasmtime-wasi-tls-openssl` in their - # own job. This is because they depends on OpenSSL, which is not easily - # available on all platforms. + # Test `wasmtime-wasi-tls` in its own job, as not all of its providers are + # compatible with all targets. The primary culprit is the OpenSSL dependency, + # which is not easily available on all platforms. # # The Windows base image has OpenSSL installed by default, but not in a way # that is automatically discoverable by `openssl-sys`. We need to configure @@ -951,7 +990,7 @@ jobs: # Additionally, the GH actions Windows image does not ship with a CA cert # bundle, so we use the one from cUrl. test_wasi_tls: - name: Test wasi-tls using native-tls & openssl providers + name: Test wasi-tls needs: determine if: needs.determine.outputs.run-full runs-on: ${{ matrix.os }} @@ -959,7 +998,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -975,8 +1014,7 @@ jobs: "SSL_CERT_FILE=$sslCertFile" | Out-File -FilePath $env:GITHUB_ENV -Append "OPENSSL_DIR=$opensslDir" | Out-File -FilePath $env:GITHUB_ENV -Append "OPENSSL_LIB_DIR=$opensslLibDir" | Out-File -FilePath $env:GITHUB_ENV -Append - - run: cargo test -p wasmtime-wasi-tls-openssl - - run: cargo test -p wasmtime-wasi-tls-nativetls + - run: cargo test -p wasmtime-wasi-tls --all-features # Test the `wasmtime-fuzzing` crate. Split out from the main tests because # `--all-features` brings in OCaml, which is a pain to get setup for all @@ -987,7 +1025,7 @@ jobs: name: Test wasmtime-fuzzing runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -1003,12 +1041,12 @@ jobs: name: Test DWARF debugging runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust - run: | - rustup target add wasm32-wasip1 wasm32-unknown-unknown + rustup target add wasm32-wasip2 wasm32-wasip1 wasm32-unknown-unknown cd /tmp curl --retry 5 --retry-all-errors -OL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-30/wasi-sdk-30.0-x86_64-linux.tar.gz tar -xzf wasi-sdk-30.0-x86_64-linux.tar.gz @@ -1024,6 +1062,30 @@ jobs: LLDB: lldb-18 WASI_SDK_PATH: /tmp/wasi-sdk + # Test guest-debug (gdbstub) functionality with LLDB. + # Requires wasi-sdk >= 32 for LLDB with the Wasm remote-debug plugin. + test_guest_debug: + needs: determine + if: needs.determine.outputs.run-dwarf + name: Test guest debugging + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + with: + submodules: true + - uses: ./.github/actions/install-rust + - run: | + rustup target add wasm32-wasip2 wasm32-wasip1 wasm32-unknown-unknown + cd /tmp + curl --retry 5 --retry-all-errors -OL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-x86_64-linux.tar.gz + tar -xzf wasi-sdk-32.0-x86_64-linux.tar.gz + mv wasi-sdk-32.0-x86_64-linux wasi-sdk + - run: | + cargo test --features gdbstub --test all -- --ignored --test-threads 1 guest_debug:: + env: + LLDB: /tmp/wasi-sdk/bin/lldb + WASI_SDK_PATH: /tmp/wasi-sdk + build-preview1-component-adapter: name: Build wasi-preview1-component-adapter needs: determine @@ -1033,11 +1095,11 @@ jobs: deployments: write contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust - - run: rustup target add wasm32-wasip1 wasm32-unknown-unknown + - run: rustup target add wasm32-wasip2 wasm32-wasip1 wasm32-unknown-unknown - name: Install wasm-tools run: | @@ -1050,7 +1112,7 @@ jobs: env: VERSION: ${{ github.sha }} - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: bins-wasi-preview1-component-adapter path: target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.*.wasm @@ -1060,7 +1122,7 @@ jobs: needs: build-preview1-component-adapter runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -1075,7 +1137,7 @@ jobs: if: needs.determine.outputs.run-full runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -1102,7 +1164,7 @@ jobs: MIN_PLATFORM_TEST_DISABLE_WASI: 1 # Add the `wasmtime-platform.h` file as a release artifact - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: wasmtime-platform-header path: examples/min-platform/embedding/wasmtime-platform.h @@ -1113,11 +1175,11 @@ jobs: name: Run benchmarks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust - - run: rustup target add wasm32-wasip1 + - run: rustup target add wasm32-wasip2 wasm32-wasip1 - run: cargo test --benches --release # Verify that cranelift's code generation is deterministic @@ -1127,7 +1189,7 @@ jobs: name: Meta deterministic check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -1139,7 +1201,7 @@ jobs: if: github.repository == 'bytecodealliance/wasmtime' && needs.determine.outputs.run-full runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust @@ -1170,7 +1232,7 @@ jobs: strategy: matrix: include: - - crate: "wasmtime" + - crate: "wasmtime --features component-model-async-bytes" - crate: "wasmtime-cli" - crate: "wasmtime-environ --all-features" - crate: "pulley-interpreter --all-features" @@ -1186,14 +1248,14 @@ jobs: CARGO_NEXTEST_VERSION: 0.9.88 MIRIFLAGS: -Zmiri-permissive-provenance steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - uses: ./.github/actions/install-rust with: toolchain: wasmtime-ci-pinned-nightly - run: rustup component add rust-src miri - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ${{ runner.tool_cache }}/cargo-nextest key: cargo-nextest-bin-${{ env.CARGO_NEXTEST_VERSION }} @@ -1220,7 +1282,7 @@ jobs: matrix: ${{ fromJson(needs.determine.outputs.build-matrix) }} env: ${{ matrix.env || fromJSON('{}') }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true @@ -1248,7 +1310,7 @@ jobs: # unconditionally to this workflow's files so we have a copy of them. - run: ./ci/build-tarballs.sh "${{ matrix.build }}" "${{ matrix.target }}" - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: bins-${{ matrix.build }} path: dist @@ -1301,6 +1363,7 @@ jobs: - build-preview1-component-adapter-provider - test-min-platform-example - check_js + - gc_zeal if: always() steps: # Calculate the exit status of the whole CI workflow. @@ -1334,7 +1397,7 @@ jobs: && startsWith(github.ref, 'refs/heads/release-') && github.repository == 'bytecodealliance/wasmtime' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true fetch-depth: 0 @@ -1382,7 +1445,7 @@ jobs: permissions: issues: write steps: - - uses: actions/github-script@v7 + - uses: actions/github-script@v8 with: script: | github.rest.issues.create({ diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index f3544251cbfd..f3967b3c1d51 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -74,7 +74,7 @@ jobs: cargo build --release - name: Checkout patch from bytecodealliance/wasmtime (pushed and triggering on this perf repo) - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: true path: wasmtime_commit @@ -89,7 +89,7 @@ jobs: cp target/release/libwasmtime_bench_api.so /tmp/wasmtime_commit.so - name: Checkout main from bytecodealliance/wasmtime - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: 'main' repository: 'bytecodealliance/wasmtime' diff --git a/.github/workflows/publish-artifacts.yml b/.github/workflows/publish-artifacts.yml index 3f634d645f17..6d2f01be889b 100644 --- a/.github/workflows/publish-artifacts.yml +++ b/.github/workflows/publish-artifacts.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'bytecodealliance/wasmtime' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: ./.github/actions/fetch-run-id - run: | gh run download ${COMMIT_RUN_ID} diff --git a/.github/workflows/publish-to-cratesio.yml b/.github/workflows/publish-to-cratesio.yml index 2f712967878a..c26b6107ae02 100644 --- a/.github/workflows/publish-to-cratesio.yml +++ b/.github/workflows/publish-to-cratesio.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest environment: publish steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - run: rustup update stable && rustup default stable diff --git a/.github/workflows/release-process.yml b/.github/workflows/release-process.yml index c8436ff2f8b5..5d13cacd180c 100644 --- a/.github/workflows/release-process.yml +++ b/.github/workflows/release-process.yml @@ -41,7 +41,7 @@ jobs: name: Run the release process runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - name: Setup diff --git a/.gitignore b/.gitignore index 28a72405b61a..9e232c8bbb5d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ publish vendor examples/build examples/.cache +crates/c-api/build *.coredump *.smt2 cranelift/isle/veri/veri_engine/test_output diff --git a/ADOPTERS.md b/ADOPTERS.md index e8b74e428ef3..ea4bc9982d17 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -10,7 +10,7 @@ Wasmtime is used in many different production use-cases. This list has grown sig | [Cosmonic](https://www.cosmonic.com) | [@ricochet](https://github.com/ricochet) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Cosmonic is a distributed app platform built on CNCF wasmCloud for wasmCloud apps. | | [DFINITY](https://dfinity.org/) | [@ulan](https://github.com/ulan) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Internet Computer blockchain uses Wasmtime to run canister smart contracts. | | [Embark Studios](https://www.embark-studios.com/) | [@repi](https://github.com/repi) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Rust game engine | -| [Fastly](https://fastly.com/) | [@fitzgen](https://github.com/fitzgen) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | The Compute@Edge platform helps you compile your custom code to WebAssembly and runs it at the Fastly edge using the WebAssembly System Interface for each compute request. | +| [Fastly](https://fastly.com/) | [@sunfishcode](https://github.com/dgohman-fastly) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | The Fastly Compute platform helps you compile your custom code to WebAssembly and runs it at the Fastly edge using the WebAssembly System Interface for each compute request. | | [Fermyon](https://fermyon.com) | [@tschneidereit](https://github.com/tschneidereit) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Fermyon Cloud is a cloud application platform for WebAssembly-based serverless functions and microservices. | | [Huawei](https://www.huawei.com) | [@mohrobati](https://github.com/mohrobati) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Huawei Cloud uses Wasmtime to run WebAssembly functions in both serverless cloud and on the edge. | | [InfinyOn](https://infinyon.com/) | [@sehz](https://github.com/sehz) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | InfinyOn leverages the power of WebAssembly SmartModules to execute real-time data transformations. | diff --git a/Cargo.lock b/Cargo.lock index 22dce197301d..968177838e83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,6 +147,12 @@ dependencies = [ "arbitrary", ] +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.89" @@ -250,15 +256,6 @@ version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" -[[package]] -name = "bitmaps" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" -dependencies = [ - "typenum", -] - [[package]] name = "block-buffer" version = "0.10.2" @@ -290,7 +287,7 @@ dependencies = [ [[package]] name = "byte-array-literals" -version = "43.0.0" +version = "44.0.2" [[package]] name = "byteorder" @@ -638,7 +635,7 @@ dependencies = [ "test-programs-artifacts", "tokio", "wasm-compose", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmtime", "wasmtime-wasi", ] @@ -693,7 +690,7 @@ dependencies = [ [[package]] name = "cranelift" -version = "0.130.0" +version = "0.131.2" dependencies = [ "cranelift-codegen", "cranelift-frontend", @@ -706,7 +703,7 @@ dependencies = [ [[package]] name = "cranelift-assembler-x64" -version = "0.130.0" +version = "0.131.2" dependencies = [ "arbitrary", "arbtest", @@ -724,14 +721,14 @@ dependencies = [ [[package]] name = "cranelift-assembler-x64-meta" -version = "0.130.0" +version = "0.131.2" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.130.0" +version = "0.131.2" dependencies = [ "cranelift-entity", "wasmtime-internal-core", @@ -739,7 +736,7 @@ dependencies = [ [[package]] name = "cranelift-bitset" -version = "0.130.0" +version = "0.131.2" dependencies = [ "arbitrary", "serde", @@ -749,7 +746,7 @@ dependencies = [ [[package]] name = "cranelift-codegen" -version = "0.130.0" +version = "0.131.2" dependencies = [ "anyhow", "bumpalo", @@ -785,7 +782,7 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.130.0" +version = "0.131.2" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -796,18 +793,18 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.130.0" +version = "0.131.2" [[package]] name = "cranelift-control" -version = "0.130.0" +version = "0.131.2" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.130.0" +version = "0.131.2" dependencies = [ "cranelift-bitset", "serde", @@ -849,7 +846,7 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.130.0" +version = "0.131.2" dependencies = [ "cranelift-codegen", "env_logger 0.11.5", @@ -874,7 +871,7 @@ dependencies = [ [[package]] name = "cranelift-interpreter" -version = "0.130.0" +version = "0.131.2" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -888,7 +885,7 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.130.0" +version = "0.131.2" dependencies = [ "codespan-reporting", "log", @@ -897,7 +894,7 @@ dependencies = [ [[package]] name = "cranelift-jit" -version = "0.130.0" +version = "0.131.2" dependencies = [ "anyhow", "cranelift", @@ -919,7 +916,7 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.130.0" +version = "0.131.2" dependencies = [ "anyhow", "cranelift-codegen", @@ -931,7 +928,7 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.130.0" +version = "0.131.2" dependencies = [ "cranelift-codegen", "libc", @@ -940,7 +937,7 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.130.0" +version = "0.131.2" dependencies = [ "anyhow", "cranelift-codegen", @@ -949,13 +946,13 @@ dependencies = [ "cranelift-frontend", "cranelift-module", "log", - "object 0.38.1", + "object 0.39.0", "target-lexicon", ] [[package]] name = "cranelift-reader" -version = "0.130.0" +version = "0.131.2" dependencies = [ "anyhow", "cranelift-codegen", @@ -965,7 +962,7 @@ dependencies = [ [[package]] name = "cranelift-serde" -version = "0.130.0" +version = "0.131.2" dependencies = [ "clap", "cranelift-codegen", @@ -975,7 +972,7 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.130.0" +version = "0.131.2" [[package]] name = "cranelift-tools" @@ -1235,7 +1232,7 @@ checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" [[package]] name = "embedding" -version = "43.0.0" +version = "44.0.2" dependencies = [ "dlmalloc", "raw-cpuid", @@ -1345,6 +1342,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[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.3.0" @@ -1536,6 +1542,21 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[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-macro" version = "0.3.31" @@ -1591,6 +1612,30 @@ dependencies = [ "serde_json", ] +[[package]] +name = "gdbstub" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bafc7e33650ab9f05dcc16325f05d56b8d10393114e31a19a353b86fa60cfe7" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "log", + "managed", + "num-traits", + "pastey", +] + +[[package]] +name = "gdbstub_arch" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c02bfe7bd65f42bcda751456869dfa1eb2bd1c36e309b9ec27f4888d41cf258" +dependencies = [ + "gdbstub", + "num-traits", +] + [[package]] name = "generic-array" version = "0.14.5" @@ -2002,20 +2047,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "im-rc" -version = "15.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" -dependencies = [ - "bitmaps", - "rand_core 0.6.3", - "rand_xoshiro", - "sized-chunks", - "typenum", - "version_check", -] - [[package]] name = "indexmap" version = "1.9.1" @@ -2179,14 +2210,14 @@ dependencies = [ [[package]] name = "json-from-wast" -version = "0.245.0" +version = "0.246.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e70808c1eac9590387b9acbd107f5412293c0b56f7e9173c9de8538ae4a876f" +checksum = "ffd63d655751d8840f4e2a138eab213c56fc668bf9c2861c2f7b30058b650c8d" dependencies = [ "anyhow", "serde", "serde_derive", - "wast 245.0.0", + "wast 246.0.1", ] [[package]] @@ -2328,6 +2359,12 @@ dependencies = [ "libc", ] +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "matchers" version = "0.2.0" @@ -2379,10 +2416,10 @@ dependencies = [ [[package]] name = "min-platform-host" -version = "43.0.0" +version = "44.0.2" dependencies = [ "libloading", - "object 0.38.1", + "object 0.39.0", "wasmtime", ] @@ -2415,13 +2452,26 @@ dependencies = [ [[package]] name = "mutatis" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31be5d2bfb418a4362eec78ca556ce9a15542a00d389860c3bf3adc132edb1d9" +checksum = "645a17bdae66c8ee0fdba0004ab864cbd711bec41aae4c466ce0d35e266c946e" dependencies = [ + "log", + "mutatis-derive", "rand 0.8.5", ] +[[package]] +name = "mutatis-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e2c58c651085c4319cd3554f44541dcf1be8daa0481c9089de50926750623e7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -2525,9 +2575,9 @@ dependencies = [ [[package]] name = "object" -version = "0.38.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271638cd5fa9cca89c4c304675ca658efc4e64a66c716b7cfe1afb4b9611dbbc" +checksum = "63944c133d03f44e75866bbd160b95af0ec3f6a13d936d69d31c81078cbc5baf" dependencies = [ "crc32fast", "hashbrown 0.16.1", @@ -2681,6 +2731,12 @@ dependencies = [ "ureq", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "password-hash" version = "0.4.2" @@ -2698,6 +2754,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +[[package]] +name = "pastey" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec" + [[package]] name = "pbkdf2" version = "0.11.0" @@ -2818,7 +2880,7 @@ dependencies = [ [[package]] name = "pulley-interpreter" -version = "43.0.0" +version = "44.0.2" dependencies = [ "anyhow", "arbitrary", @@ -2842,7 +2904,7 @@ dependencies = [ [[package]] name = "pulley-macros" -version = "43.0.0" +version = "44.0.2" dependencies = [ "proc-macro2", "quote", @@ -2938,15 +3000,6 @@ dependencies = [ "rand_core 0.6.3", ] -[[package]] -name = "rand_xoshiro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" -dependencies = [ - "rand_core 0.6.3", -] - [[package]] name = "raw-cpuid" version = "11.5.0" @@ -3161,11 +3214,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.4" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "log", + "once_cell", "ring", "rustls-pki-types", "rustls-webpki", @@ -3184,9 +3238,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "ring", "rustls-pki-types", @@ -3412,16 +3466,6 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" -[[package]] -name = "sized-chunks" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" -dependencies = [ - "bitmaps", - "typenum", -] - [[package]] name = "slab" version = "0.4.11" @@ -3589,9 +3633,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.41" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" +checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" dependencies = [ "filetime", "libc", @@ -3627,7 +3671,7 @@ version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ - "fastrand", + "fastrand 2.3.0", "getrandom 0.3.1", "once_cell", "rustix 1.0.8", @@ -3706,7 +3750,7 @@ dependencies = [ "wasmtime", "wasmtime-test-util", "wat", - "wit-component", + "wit-component 0.246.1", ] [[package]] @@ -3847,12 +3891,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] @@ -4168,10 +4211,10 @@ version = "0.1.0" [[package]] name = "verify-component-adapter" -version = "43.0.0" +version = "44.0.2" dependencies = [ "anyhow", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wat", ] @@ -4190,6 +4233,12 @@ dependencies = [ "libc", ] +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + [[package]] name = "walkdir" version = "2.5.0" @@ -4227,7 +4276,7 @@ dependencies = [ [[package]] name = "wasi-common" -version = "43.0.0" +version = "44.0.2" dependencies = [ "async-trait", "bitflags 2.9.4", @@ -4267,13 +4316,13 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter" -version = "43.0.0" +version = "44.0.2" dependencies = [ "bitflags 2.9.4", "byte-array-literals", - "object 0.38.1", + "object 0.39.0", "wasip1", - "wasm-encoder", + "wasm-encoder 0.246.1", "wit-bindgen-rust-macro", ] @@ -4349,22 +4398,18 @@ checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "wasm-compose" -version = "0.245.0" +version = "0.246.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e87956946cd902ed92479d41ea3210538cb77d8bd104f0cd05b80627fcb3c6" +checksum = "f5eecb80ca9b76c7eec604b3ad2026434e5c14a616dd291166165b4b8ef55353" dependencies = [ "anyhow", "heck 0.5.0", - "im-rc", "indexmap 2.13.0", "log", "petgraph", - "serde", - "serde_derive", - "serde_yaml", "smallvec 1.15.1", - "wasm-encoder", - "wasmparser 0.245.0", + "wasm-encoder 0.246.1", + "wasmparser 0.246.1", "wat", ] @@ -4378,6 +4423,16 @@ dependencies = [ "wasmparser 0.245.0", ] +[[package]] +name = "wasm-encoder" +version = "0.246.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e1929aad146499e47362c876fcbcbb0363f730951d93438f511178626e999a8" +dependencies = [ + "leb128fmt", + "wasmparser 0.246.1", +] + [[package]] name = "wasm-metadata" version = "0.245.0" @@ -4386,36 +4441,48 @@ checksum = "ce52b194ec202d029751081d735c1ae49c1bacbdc2634c821a86211e3751300c" dependencies = [ "anyhow", "indexmap 2.13.0", - "wasm-encoder", + "wasm-encoder 0.245.0", "wasmparser 0.245.0", ] +[[package]] +name = "wasm-metadata" +version = "0.246.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e33c863ddd12ba00a9a783a7fe66d6687945f6a1a0431c01fca96ee79f8723" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder 0.246.1", + "wasmparser 0.246.1", +] + [[package]] name = "wasm-mutate" -version = "0.245.0" +version = "0.246.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce4cdc599ae42969c70593976632214edb70805ed1007b52cf90fc52294f4e7" +checksum = "579803a8b536c9a7e65bd19f1951dc95f758d3acf292c35ebcc648568404ad9c" dependencies = [ "egg", "log", "rand 0.9.2", "thiserror 2.0.17", - "wasm-encoder", - "wasmparser 0.245.0", + "wasm-encoder 0.246.1", + "wasmparser 0.246.1", ] [[package]] name = "wasm-smith" -version = "0.245.0" +version = "0.246.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b379db01f578cea54de8cce4a6906a4c70c814cc886179e885dc186311127a2" +checksum = "3e8428d681bad07d44f85ba31953a8debcfac933274299f4de1a87bb4de840c7" dependencies = [ "anyhow", "arbitrary", "flagset", "serde", "serde_derive", - "wasm-encoder", + "wasm-encoder 0.246.1", "wat", ] @@ -4429,13 +4496,13 @@ dependencies = [ [[package]] name = "wasm-wave" -version = "0.245.0" +version = "0.246.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80a3011a773f51c3c66612c66807d882fbfc206304c705fe146f535898acd4fb" +checksum = "157f7a1e45f70a61b880eb40d78f9dd5e93c2489898a5c4eb659d776bd08ae43" dependencies = [ "logos", "thiserror 2.0.17", - "wit-parser", + "wit-parser 0.246.1", ] [[package]] @@ -4493,6 +4560,18 @@ name = "wasmparser" version = "0.245.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48a767a48974f0c8b66f211b96e01aa77feed58b8ccce4e7f0cff0ae55b174d4" +dependencies = [ + "bitflags 2.9.4", + "hashbrown 0.16.1", + "indexmap 2.13.0", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.246.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d991c35d79bf8336dc1cd632ed4aacf0dc5fac4bc466c670625b037b972bb9c" dependencies = [ "bitflags 2.9.4", "hashbrown 0.16.1", @@ -4503,18 +4582,18 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.245.0" +version = "0.246.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79e39af628dc03572ecd42fab1265480d943db5858dab748142a5ce0644bb5" +checksum = "267a5e255a8f9ac8dd403fe6dbfc45f17594b1b7be211c2494c95bfa086d3906" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.245.0", + "wasmparser 0.246.1", ] [[package]] name = "wasmtime" -version = "43.0.0" +version = "44.0.2" dependencies = [ "addr2line 0.26.0", "async-trait", @@ -4535,7 +4614,7 @@ dependencies = [ "log", "mach2", "memfd", - "object 0.38.1", + "object 0.39.0", "once_cell", "postcard", "proptest", @@ -4552,9 +4631,9 @@ dependencies = [ "tempfile", "tokio", "wasm-compose", - "wasm-encoder", + "wasm-encoder 0.246.1", "wasm-wave", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmtime-environ", "wasmtime-internal-cache", "wasmtime-internal-component-macro", @@ -4575,7 +4654,7 @@ dependencies = [ [[package]] name = "wasmtime-bench-api" -version = "43.0.0" +version = "44.0.2" dependencies = [ "cap-std", "clap", @@ -4590,14 +4669,14 @@ dependencies = [ [[package]] name = "wasmtime-c-api" -version = "43.0.0" +version = "44.0.2" dependencies = [ "wasmtime-c-api-impl", ] [[package]] name = "wasmtime-c-api-impl" -version = "43.0.0" +version = "44.0.2" dependencies = [ "async-trait", "bytes", @@ -4610,13 +4689,14 @@ dependencies = [ "wasmtime", "wasmtime-internal-c-api-macros", "wasmtime-wasi", + "wasmtime-wasi-http", "wasmtime-wasi-io", "wat", ] [[package]] name = "wasmtime-cli" -version = "43.0.0" +version = "44.0.2" dependencies = [ "anyhow", "async-trait", @@ -4645,7 +4725,7 @@ dependencies = [ "log", "memchr", "num_cpus", - "object 0.38.1", + "object 0.39.0", "pulley-interpreter", "rand 0.9.2", "rayon", @@ -4664,15 +4744,17 @@ dependencies = [ "tracing", "walkdir", "wasi-common", - "wasm-encoder", - "wasmparser 0.245.0", + "wasm-encoder 0.246.1", + "wasmparser 0.246.1", "wasmtime", "wasmtime-cli-flags", "wasmtime-environ", "wasmtime-internal-cache", "wasmtime-internal-component-util", "wasmtime-internal-cranelift", + "wasmtime-internal-debugger", "wasmtime-internal-explorer", + "wasmtime-internal-gdbstub-component-artifact", "wasmtime-internal-unwinder", "wasmtime-test-macros", "wasmtime-test-util", @@ -4685,15 +4767,15 @@ dependencies = [ "wasmtime-wasi-tls", "wasmtime-wast", "wasmtime-wizer", - "wast 245.0.0", + "wast 246.0.1", "wat", "windows-sys 0.61.2", - "wit-component", + "wit-component 0.246.1", ] [[package]] name = "wasmtime-cli-flags" -version = "43.0.0" +version = "44.0.2" dependencies = [ "clap", "file-per-thread-logger", @@ -4707,7 +4789,7 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "43.0.0" +version = "44.0.2" dependencies = [ "anyhow", "arbitrary", @@ -4722,7 +4804,7 @@ dependencies = [ "hashbrown 0.16.1", "indexmap 2.13.0", "log", - "object 0.38.1", + "object 0.39.0", "postcard", "rustc-demangle", "semver", @@ -4731,8 +4813,8 @@ dependencies = [ "sha2", "smallvec 1.15.1", "target-lexicon", - "wasm-encoder", - "wasmparser 0.245.0", + "wasm-encoder 0.246.1", + "wasmparser 0.246.1", "wasmprinter", "wasmtime-internal-component-util", "wasmtime-internal-core", @@ -4746,7 +4828,7 @@ dependencies = [ "arbitrary", "env_logger 0.11.5", "libfuzzer-sys", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmprinter", "wasmtime-environ", "wasmtime-test-util", @@ -4779,7 +4861,7 @@ dependencies = [ "rand 0.9.2", "smallvec 1.15.1", "target-lexicon", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmtime", "wasmtime-fuzzing", "wasmtime-test-util", @@ -4806,14 +4888,15 @@ dependencies = [ "target-lexicon", "tempfile", "test-programs-artifacts", + "tokio", "v8", "wasm-compose", - "wasm-encoder", + "wasm-encoder 0.246.1", "wasm-mutate", "wasm-smith", "wasm-spec-interpreter", "wasmi", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmprinter", "wasmtime", "wasmtime-cli-flags", @@ -4827,7 +4910,7 @@ dependencies = [ [[package]] name = "wasmtime-internal-c-api-macros" -version = "43.0.0" +version = "44.0.2" dependencies = [ "proc-macro2", "quote", @@ -4835,7 +4918,7 @@ dependencies = [ [[package]] name = "wasmtime-internal-cache" -version = "43.0.0" +version = "44.0.2" dependencies = [ "base64", "directories-next", @@ -4856,7 +4939,7 @@ dependencies = [ [[package]] name = "wasmtime-internal-component-macro" -version = "43.0.0" +version = "44.0.2" dependencies = [ "anyhow", "component-macro-test-helpers", @@ -4871,16 +4954,16 @@ dependencies = [ "wasmtime", "wasmtime-internal-component-util", "wasmtime-internal-wit-bindgen", - "wit-parser", + "wit-parser 0.246.1", ] [[package]] name = "wasmtime-internal-component-util" -version = "43.0.0" +version = "44.0.2" [[package]] name = "wasmtime-internal-core" -version = "43.0.0" +version = "44.0.2" dependencies = [ "anyhow", "hashbrown 0.16.1", @@ -4890,7 +4973,7 @@ dependencies = [ [[package]] name = "wasmtime-internal-cranelift" -version = "43.0.0" +version = "44.0.2" dependencies = [ "cfg-if", "cranelift-codegen", @@ -4901,12 +4984,12 @@ dependencies = [ "gimli 0.33.0", "itertools 0.14.0", "log", - "object 0.38.1", + "object 0.39.0", "pulley-interpreter", "smallvec 1.15.1", "target-lexicon", "thiserror 2.0.17", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmtime-environ", "wasmtime-internal-core", "wasmtime-internal-unwinder", @@ -4915,17 +4998,20 @@ dependencies = [ [[package]] name = "wasmtime-internal-debugger" -version = "43.0.0" +version = "44.0.2" dependencies = [ + "async-trait", "env_logger 0.11.5", "log", "tokio", "wasmtime", + "wasmtime-wasi", + "wasmtime-wasi-io", ] [[package]] name = "wasmtime-internal-explorer" -version = "43.0.0" +version = "44.0.2" dependencies = [ "capstone", "serde", @@ -4939,7 +5025,7 @@ dependencies = [ [[package]] name = "wasmtime-internal-fiber" -version = "43.0.0" +version = "44.0.2" dependencies = [ "backtrace", "cc", @@ -4951,19 +5037,39 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "wasmtime-internal-gdbstub-component" +version = "44.0.2" +dependencies = [ + "anyhow", + "clap", + "env_logger 0.11.5", + "futures", + "gdbstub", + "gdbstub_arch", + "log", + "wasip2", + "wit-bindgen 0.53.0", + "wstd", +] + +[[package]] +name = "wasmtime-internal-gdbstub-component-artifact" +version = "44.0.2" + [[package]] name = "wasmtime-internal-jit-debug" -version = "43.0.0" +version = "44.0.2" dependencies = [ "cc", - "object 0.38.1", + "object 0.39.0", "rustix 1.0.8", "wasmtime-internal-versioned-export-macros", ] [[package]] name = "wasmtime-internal-jit-icache-coherence" -version = "43.0.0" +version = "44.0.2" dependencies = [ "cfg-if", "libc", @@ -4973,18 +5079,18 @@ dependencies = [ [[package]] name = "wasmtime-internal-unwinder" -version = "43.0.0" +version = "44.0.2" dependencies = [ "cfg-if", "cranelift-codegen", "log", - "object 0.38.1", + "object 0.39.0", "wasmtime-environ", ] [[package]] name = "wasmtime-internal-versioned-export-macros" -version = "43.0.0" +version = "44.0.2" dependencies = [ "proc-macro2", "quote", @@ -4993,14 +5099,14 @@ dependencies = [ [[package]] name = "wasmtime-internal-winch" -version = "43.0.0" +version = "44.0.2" dependencies = [ "cranelift-codegen", "gimli 0.33.0", "log", - "object 0.38.1", + "object 0.39.0", "target-lexicon", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmtime-environ", "wasmtime-internal-cranelift", "winch-codegen", @@ -5008,18 +5114,18 @@ dependencies = [ [[package]] name = "wasmtime-internal-wit-bindgen" -version = "43.0.0" +version = "44.0.2" dependencies = [ "anyhow", "bitflags 2.9.4", "heck 0.5.0", "indexmap 2.13.0", - "wit-parser", + "wit-parser 0.246.1", ] [[package]] name = "wasmtime-internal-wmemcheck" -version = "43.0.0" +version = "44.0.2" [[package]] name = "wasmtime-test-macros" @@ -5034,7 +5140,7 @@ dependencies = [ [[package]] name = "wasmtime-test-util" -version = "43.0.0" +version = "44.0.2" dependencies = [ "arbitrary", "arbtest", @@ -5047,7 +5153,7 @@ dependencies = [ "serde_derive", "target-lexicon", "toml", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmprinter", "wasmtime", "wasmtime-environ", @@ -5057,7 +5163,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "43.0.0" +version = "44.0.2" dependencies = [ "async-trait", "bitflags 2.9.4", @@ -5091,7 +5197,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi-config" -version = "43.0.0" +version = "44.0.2" dependencies = [ "test-programs-artifacts", "tokio", @@ -5101,7 +5207,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi-http" -version = "43.0.0" +version = "44.0.2" dependencies = [ "async-trait", "base64", @@ -5133,7 +5239,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi-io" -version = "43.0.0" +version = "44.0.2" dependencies = [ "async-trait", "bytes", @@ -5144,7 +5250,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi-keyvalue" -version = "43.0.0" +version = "44.0.2" dependencies = [ "test-programs-artifacts", "tokio", @@ -5154,7 +5260,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi-nn" -version = "43.0.0" +version = "44.0.2" dependencies = [ "cap-std", "libtest-mimic", @@ -5174,7 +5280,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi-threads" -version = "43.0.0" +version = "44.0.2" dependencies = [ "log", "rand 0.8.5", @@ -5185,64 +5291,42 @@ dependencies = [ [[package]] name = "wasmtime-wasi-tls" -version = "43.0.0" +version = "44.0.2" dependencies = [ "bytes", - "futures", - "rustls", - "test-programs-artifacts", - "tokio", - "tokio-rustls", - "wasmtime", - "wasmtime-wasi", - "webpki-roots", -] - -[[package]] -name = "wasmtime-wasi-tls-nativetls" -version = "43.0.0" -dependencies = [ + "cfg-if", "futures", "native-tls", - "test-programs-artifacts", - "tokio", - "tokio-native-tls", - "wasmtime", - "wasmtime-wasi", - "wasmtime-wasi-tls", -] - -[[package]] -name = "wasmtime-wasi-tls-openssl" -version = "43.0.0" -dependencies = [ - "futures", "openssl", + "rustls", "test-programs-artifacts", "tokio", + "tokio-native-tls", "tokio-openssl", + "tokio-rustls", + "tracing", "wasmtime", "wasmtime-wasi", - "wasmtime-wasi-tls", + "webpki-roots", ] [[package]] name = "wasmtime-wast" -version = "43.0.0" +version = "44.0.2" dependencies = [ "json-from-wast", "log", - "object 0.38.1", + "object 0.39.0", "serde_json", "tokio", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmtime", - "wast 245.0.0", + "wast 246.0.1", ] [[package]] name = "wasmtime-wizer" -version = "43.0.0" +version = "44.0.2" dependencies = [ "clap", "criterion", @@ -5250,8 +5334,8 @@ dependencies = [ "log", "rayon", "tokio", - "wasm-encoder", - "wasmparser 0.245.0", + "wasm-encoder 0.246.1", + "wasmparser 0.246.1", "wasmprinter", "wasmtime", "wasmtime-wasi", @@ -5269,25 +5353,25 @@ dependencies = [ [[package]] name = "wast" -version = "245.0.0" +version = "246.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ffc7471e16a6f3c7a3c3a230314915b5dcd158e5ef13ccda2f43358a9df00c" +checksum = "96cf2d50bc7478dcca61d00df4dadf922ef46c5924db20a97e6daaf09fe1cb09" dependencies = [ "bumpalo", "gimli 0.31.1", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder", + "wasm-encoder 0.246.1", ] [[package]] name = "wat" -version = "1.245.0" +version = "1.246.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bcac6f915e2a84a4c0d9df9d41ad7518d99cda13f3bb83e3b8c22bf8726ab6" +checksum = "723f2473b47f738c12fc11c8e0bb8b27ce7cf9c78cf1a29dadbc2d34a2513292" dependencies = [ - "wast 245.0.0", + "wast 246.0.1", ] [[package]] @@ -5322,7 +5406,7 @@ dependencies = [ [[package]] name = "wiggle" -version = "43.0.0" +version = "44.0.2" dependencies = [ "bitflags 2.9.4", "proptest", @@ -5338,7 +5422,7 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "43.0.0" +version = "44.0.2" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -5350,7 +5434,7 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "43.0.0" +version = "44.0.2" dependencies = [ "proc-macro2", "quote", @@ -5405,7 +5489,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "43.0.0" +version = "44.0.2" dependencies = [ "cranelift-assembler-x64", "cranelift-codegen", @@ -5414,7 +5498,7 @@ dependencies = [ "smallvec 1.15.1", "target-lexicon", "thiserror 2.0.17", - "wasmparser 0.245.0", + "wasmparser 0.246.1", "wasmtime-environ", "wasmtime-internal-core", "wasmtime-internal-cranelift", @@ -5675,7 +5759,7 @@ checksum = "f7a381fbf6d0b3403a207adf15c84811e039d2c4a30d4bcc329be5b8953cdad3" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser", + "wit-parser 0.245.0", ] [[package]] @@ -5698,9 +5782,9 @@ dependencies = [ "indexmap 2.13.0", "prettyplease", "syn 2.0.106", - "wasm-metadata", + "wasm-metadata 0.245.0", "wit-bindgen-core", - "wit-component", + "wit-component 0.245.0", ] [[package]] @@ -5731,10 +5815,29 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder", - "wasm-metadata", + "wasm-encoder 0.245.0", + "wasm-metadata 0.245.0", "wasmparser 0.245.0", - "wit-parser", + "wit-parser 0.245.0", +] + +[[package]] +name = "wit-component" +version = "0.246.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7938354eec9e6270abcf992dd22d2afbbe83c07040f5a12c80645735e7f94d4" +dependencies = [ + "anyhow", + "bitflags 2.9.4", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.246.1", + "wasm-metadata 0.246.1", + "wasmparser 0.246.1", + "wit-parser 0.246.1", ] [[package]] @@ -5756,6 +5859,25 @@ dependencies = [ "wasmparser 0.245.0", ] +[[package]] +name = "wit-parser" +version = "0.246.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc133164d0fa0a990756d5cdb1a4c24e1f638643e1f3e085d0e51111968e8536" +dependencies = [ + "anyhow", + "hashbrown 0.16.1", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.246.1", +] + [[package]] name = "witx" version = "0.9.1" @@ -5793,6 +5915,38 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "wstd" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0903606f1acdecad11576768ecc61ce215d6848652ac16c0e4592bb265e4200e" +dependencies = [ + "anyhow", + "async-task", + "bytes", + "futures-lite", + "http", + "http-body", + "http-body-util", + "itoa", + "pin-project-lite", + "serde", + "serde_json", + "slab", + "wasip2", + "wstd-macro", +] + +[[package]] +name = "wstd-macro" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a9df01a7fb39fbe7e9b5ef76f586f06425dd6f2be350de4781936f72f9899d" +dependencies = [ + "quote", + "syn 2.0.106", +] + [[package]] name = "xattr" version = "1.6.1" @@ -5850,9 +6004,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerovec" diff --git a/Cargo.toml b/Cargo.toml index 9f732fc37c36..739a4837fd20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,8 @@ wasmtime-wasi-threads = { workspace = true, optional = true } wasmtime-wasi-http = { workspace = true, optional = true } wasmtime-unwinder = { workspace = true } wasmtime-wizer = { workspace = true, optional = true, features = ['clap', 'wasmtime'] } +wasmtime-debugger = { workspace = true, optional = true } +gdbstub-component-artifact = { workspace = true, optional = true } clap = { workspace = true } clap_complete = { workspace = true, optional = true } target-lexicon = { workspace = true } @@ -94,7 +96,7 @@ rustix = { workspace = true, features = ["mm", "process"] } [dev-dependencies] # depend again on wasmtime to activate its default features for tests -wasmtime = { workspace = true, features = ['default', 'anyhow', 'winch', 'pulley', 'all-arch', 'call-hook', 'memory-protection-keys', 'component-model-async'] } +wasmtime = { workspace = true, features = ['default', 'anyhow', 'winch', 'pulley', 'all-arch', 'call-hook', 'memory-protection-keys', 'component-model-async', 'component-model-async-bytes'] } env_logger = { workspace = true } log = { workspace = true } filecheck = { workspace = true } @@ -164,9 +166,9 @@ members = [ "crates/test-programs", "crates/wasi-preview1-component-adapter", "crates/wasi-preview1-component-adapter/verify", - "crates/wasi-tls-nativetls", - "crates/wasi-tls-openssl", "crates/debugger", + "crates/gdbstub-component", + "crates/gdbstub-component/artifact", "crates/wizer/fuzz", "crates/wizer/tests/regex-test", "crates/wizer/benches/regex-bench", @@ -187,12 +189,12 @@ exclude = [ ] [workspace.package] -version = "43.0.0" +version = "44.0.2" authors = ["The Wasmtime Project Developers"] edition = "2024" # Wasmtime's current policy is that this number can be no larger than the # current stable release of Rust minus 2. -rust-version = "1.91.0" +rust-version = "1.92.0" [workspace.lints.rust] # Turn on some lints which are otherwise allow-by-default in rustc. @@ -203,13 +205,17 @@ unused_import_braces = 'warn' unused-lifetimes = 'warn' unused-macro-rules = 'warn' -# Don't warn about unknown cfgs for pulley +# Don't warn about unknown cfgs for our custom cfgs. [workspace.lints.rust.unexpected_cfgs] level = "warn" check-cfg = [ 'cfg(pulley_tail_calls)', 'cfg(pulley_assume_llvm_makes_tail_calls)', 'cfg(pulley_disable_interp_simd)', + 'cfg(arc_try_new)', + + # When enabled, `cfg(gc_zeal)` activates aggressive GC debugging assertions. + 'cfg(gc_zeal)', ] [workspace.lints.clippy] @@ -241,20 +247,18 @@ extra_unused_type_parameters = 'warn' # tooling but aren't intended to be widely depended on. # # All of these crates are supported though in the sense that -wasmtime = { path = "crates/wasmtime", version = "43.0.0", default-features = false } -wasmtime-cli-flags = { path = "crates/cli-flags", version = "=43.0.0" } -wasmtime-environ = { path = "crates/environ", version = "=43.0.0" } -wasmtime-wasi = { path = "crates/wasi", version = "43.0.0", default-features = false } -wasmtime-wasi-io = { path = "crates/wasi-io", version = "43.0.0", default-features = false } -wasmtime-wasi-http = { path = "crates/wasi-http", version = "43.0.0", default-features = false } -wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "43.0.0" } -wasmtime-wasi-config = { path = "crates/wasi-config", version = "43.0.0" } -wasmtime-wasi-keyvalue = { path = "crates/wasi-keyvalue", version = "43.0.0" } -wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "43.0.0" } -wasmtime-wasi-tls = { path = "crates/wasi-tls", version = "43.0.0" } -wasmtime-wasi-tls-nativetls = { path = "crates/wasi-tls-nativetls", version = "43.0.0" } -wasmtime-wasi-tls-openssl = { path = "crates/wasi-tls-openssl", version = "43.0.0" } -wasmtime-wast = { path = "crates/wast", version = "=43.0.0" } +wasmtime = { path = "crates/wasmtime", version = "44.0.2", default-features = false } +wasmtime-cli-flags = { path = "crates/cli-flags", version = "=44.0.2" } +wasmtime-environ = { path = "crates/environ", version = "=44.0.2" } +wasmtime-wasi = { path = "crates/wasi", version = "44.0.2", default-features = false } +wasmtime-wasi-io = { path = "crates/wasi-io", version = "44.0.2", default-features = false } +wasmtime-wasi-http = { path = "crates/wasi-http", version = "44.0.2", default-features = false } +wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "44.0.2" } +wasmtime-wasi-config = { path = "crates/wasi-config", version = "44.0.2" } +wasmtime-wasi-keyvalue = { path = "crates/wasi-keyvalue", version = "44.0.2" } +wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "44.0.2" } +wasmtime-wasi-tls = { path = "crates/wasi-tls", version = "44.0.2" } +wasmtime-wast = { path = "crates/wast", version = "=44.0.2" } # Internal Wasmtime-specific crates. # @@ -263,54 +267,55 @@ wasmtime-wast = { path = "crates/wast", version = "=43.0.0" } # that these are internal unsupported crates for external use. These exist as # part of the project organization of other public crates in Wasmtime and are # otherwise not supported in terms of CVEs for example. -wasmtime-core = { path = "crates/core", version = "=43.0.0", package = 'wasmtime-internal-core' } -wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=43.0.0", package = 'wasmtime-internal-wmemcheck' } -wasmtime-c-api-macros = { path = "crates/c-api-macros", version = "=43.0.0", package = 'wasmtime-internal-c-api-macros' } -wasmtime-cache = { path = "crates/cache", version = "=43.0.0", package = 'wasmtime-internal-cache' } -wasmtime-cranelift = { path = "crates/cranelift", version = "=43.0.0", package = 'wasmtime-internal-cranelift' } -wasmtime-winch = { path = "crates/winch", version = "=43.0.0", package = 'wasmtime-internal-winch' } -wasmtime-explorer = { path = "crates/explorer", version = "=43.0.0", package = 'wasmtime-internal-explorer' } -wasmtime-fiber = { path = "crates/fiber", version = "=43.0.0", package = 'wasmtime-internal-fiber' } -wasmtime-jit-debug = { path = "crates/jit-debug", version = "=43.0.0", package = 'wasmtime-internal-jit-debug' } -wasmtime-component-util = { path = "crates/component-util", version = "=43.0.0", package = 'wasmtime-internal-component-util' } -wasmtime-component-macro = { path = "crates/component-macro", version = "=43.0.0", package = 'wasmtime-internal-component-macro' } -wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=43.0.0", package = 'wasmtime-internal-versioned-export-macros' } -wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=43.0.0", package = 'wasmtime-internal-jit-icache-coherence' } -wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=43.0.0", package = 'wasmtime-internal-wit-bindgen' } -wasmtime-unwinder = { path = "crates/unwinder", version = "=43.0.0", package = 'wasmtime-internal-unwinder' } -wasmtime-debugger = { path = "crates/debugger", version = "=43.0.0", package = "wasmtime-internal-debugger" } -wasmtime-wizer = { path = "crates/wizer", version = "43.0.0" } +wasmtime-core = { path = "crates/core", version = "=44.0.2", package = 'wasmtime-internal-core' } +wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=44.0.2", package = 'wasmtime-internal-wmemcheck' } +wasmtime-c-api-macros = { path = "crates/c-api-macros", version = "=44.0.2", package = 'wasmtime-internal-c-api-macros' } +wasmtime-cache = { path = "crates/cache", version = "=44.0.2", package = 'wasmtime-internal-cache' } +wasmtime-cranelift = { path = "crates/cranelift", version = "=44.0.2", package = 'wasmtime-internal-cranelift' } +wasmtime-winch = { path = "crates/winch", version = "=44.0.2", package = 'wasmtime-internal-winch' } +wasmtime-explorer = { path = "crates/explorer", version = "=44.0.2", package = 'wasmtime-internal-explorer' } +wasmtime-fiber = { path = "crates/fiber", version = "=44.0.2", package = 'wasmtime-internal-fiber' } +wasmtime-jit-debug = { path = "crates/jit-debug", version = "=44.0.2", package = 'wasmtime-internal-jit-debug' } +wasmtime-component-util = { path = "crates/component-util", version = "=44.0.2", package = 'wasmtime-internal-component-util' } +wasmtime-component-macro = { path = "crates/component-macro", version = "=44.0.2", package = 'wasmtime-internal-component-macro' } +wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=44.0.2", package = 'wasmtime-internal-versioned-export-macros' } +wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=44.0.2", package = 'wasmtime-internal-jit-icache-coherence' } +wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=44.0.2", package = 'wasmtime-internal-wit-bindgen' } +wasmtime-unwinder = { path = "crates/unwinder", version = "=44.0.2", package = 'wasmtime-internal-unwinder' } +wasmtime-debugger = { path = "crates/debugger", version = "=44.0.2", package = "wasmtime-internal-debugger" } +gdbstub-component-artifact = { path = "crates/gdbstub-component/artifact", version = "=44.0.2", package = "wasmtime-internal-gdbstub-component-artifact" } +wasmtime-wizer = { path = "crates/wizer", version = "44.0.2" } # Miscellaneous crates without a `wasmtime-*` prefix in their name but still # used in the `wasmtime-*` family of crates depending on various features/etc. -wiggle = { path = "crates/wiggle", version = "=43.0.0", default-features = false } -wiggle-macro = { path = "crates/wiggle/macro", version = "=43.0.0" } -wiggle-generate = { path = "crates/wiggle/generate", version = "=43.0.0" } -wasi-common = { path = "crates/wasi-common", version = "=43.0.0", default-features = false } -pulley-interpreter = { path = 'pulley', version = "=43.0.0" } -pulley-macros = { path = 'pulley/macros', version = "=43.0.0" } +wiggle = { path = "crates/wiggle", version = "=44.0.2", default-features = false } +wiggle-macro = { path = "crates/wiggle/macro", version = "=44.0.2" } +wiggle-generate = { path = "crates/wiggle/generate", version = "=44.0.2" } +wasi-common = { path = "crates/wasi-common", version = "=44.0.2", default-features = false } +pulley-interpreter = { path = 'pulley', version = "=44.0.2" } +pulley-macros = { path = 'pulley/macros', version = "=44.0.2" } # Cranelift crates in this workspace -cranelift-assembler-x64 = { path = "cranelift/assembler-x64", version = "0.130.0" } -cranelift-codegen = { path = "cranelift/codegen", version = "0.130.0", default-features = false, features = ["std", "unwind"] } -cranelift-frontend = { path = "cranelift/frontend", version = "0.130.0" } -cranelift-entity = { path = "cranelift/entity", version = "0.130.0" } -cranelift-native = { path = "cranelift/native", version = "0.130.0" } -cranelift-module = { path = "cranelift/module", version = "0.130.0" } -cranelift-interpreter = { path = "cranelift/interpreter", version = "0.130.0" } -cranelift-reader = { path = "cranelift/reader", version = "0.130.0" } +cranelift-assembler-x64 = { path = "cranelift/assembler-x64", version = "0.131.2" } +cranelift-codegen = { path = "cranelift/codegen", version = "0.131.2", default-features = false, features = ["std", "unwind"] } +cranelift-frontend = { path = "cranelift/frontend", version = "0.131.2" } +cranelift-entity = { path = "cranelift/entity", version = "0.131.2" } +cranelift-native = { path = "cranelift/native", version = "0.131.2" } +cranelift-module = { path = "cranelift/module", version = "0.131.2" } +cranelift-interpreter = { path = "cranelift/interpreter", version = "0.131.2" } +cranelift-reader = { path = "cranelift/reader", version = "0.131.2" } cranelift-filetests = { path = "cranelift/filetests" } -cranelift-object = { path = "cranelift/object", version = "0.130.0" } -cranelift-jit = { path = "cranelift/jit", version = "0.130.0" } +cranelift-object = { path = "cranelift/object", version = "0.131.2" } +cranelift-jit = { path = "cranelift/jit", version = "0.131.2" } cranelift-fuzzgen = { path = "cranelift/fuzzgen" } -cranelift-bforest = { path = "cranelift/bforest", version = "0.130.0" } -cranelift-bitset = { path = "cranelift/bitset", version = "0.130.0" } -cranelift-control = { path = "cranelift/control", version = "0.130.0", default-features = false } -cranelift-srcgen = { path = "cranelift/srcgen", version = "0.130.0" } -cranelift = { path = "cranelift/umbrella", version = "0.130.0" } +cranelift-bforest = { path = "cranelift/bforest", version = "0.131.2" } +cranelift-bitset = { path = "cranelift/bitset", version = "0.131.2" } +cranelift-control = { path = "cranelift/control", version = "0.131.2", default-features = false } +cranelift-srcgen = { path = "cranelift/srcgen", version = "0.131.2" } +cranelift = { path = "cranelift/umbrella", version = "0.131.2" } # Winch crates in this workspace. -winch-codegen = { path = "winch/codegen", version = "=43.0.0" } +winch-codegen = { path = "winch/codegen", version = "=44.0.2" } # Internal crates not published to crates.io used in testing, builds, etc wasi-preview1-component-adapter = { path = "crates/wasi-preview1-component-adapter" } @@ -344,27 +349,30 @@ wit-bindgen = { version = "0.53.0", default-features = false } wit-bindgen-rust-macro = { version = "0.53.0", default-features = false } # wasm-tools family: -wasmparser = { version = "0.245.0", default-features = false, features = ['simd'] } -wat = "1.245.0" -wast = "245.0.0" -wasmprinter = "0.245.0" -wasm-encoder = "0.245.0" -wasm-smith = "0.245.0" -wasm-mutate = "0.245.0" -wit-parser = "0.245.0" -wit-component = "0.245.0" -wasm-wave = "0.245.0" -wasm-compose = "0.245.0" -json-from-wast = "0.245.0" +wasmparser = { version = "0.246.1", default-features = false, features = ['simd'] } +wat = "1.246.1" +wast = "246.0.1" +wasmprinter = "0.246.1" +wasm-encoder = "0.246.1" +wasm-smith = "0.246.1" +wasm-mutate = "0.246.1" +wit-parser = "0.246.1" +wit-component = "0.246.1" +wasm-wave = "0.246.1" +wasm-compose = { version = "0.246.1", default-features = false } +json-from-wast = "0.246.1" + +wstd = "0.6.5" +wasip2 = "1.0" # Non-Bytecode Alliance maintained dependencies: # -------------------------- arbitrary = "1.4.2" backtrace = "0.3.75" bumpalo = "3.20.0" -mutatis = "0.3.2" +mutatis = {version = "0.4.0", features = ["alloc", "derive", "check"] } cc = "1.2.41" -object = { version = "0.38.1", default-features = false, features = ['read_core', 'elf'] } +object = { version = "0.39.0", default-features = false, features = ['read_core', 'elf'] } gimli = { version = "0.33.0", default-features = false, features = ['read'] } addr2line = { version = "0.26.0", default-features = false } anyhow = { version = "1.0.100", default-features = false } @@ -422,8 +430,8 @@ libtest-mimic = "0.8.1" semver = { version = "1.0.27", default-features = false } ittapi = "0.4.0" libm = "0.2.16" -tokio-rustls = "0.25.0" -rustls = "0.22.0" +tokio-rustls = { version = "0.26.4", default-features = false, features = ["ring", "tls12", "logging"] } +rustls = { version = "0.23", default-features = false, features = ["ring", "tls12", "logging"] } tokio-native-tls = "0.3.1" native-tls = "0.2.11" tokio-openssl = "0.6.5" @@ -440,6 +448,8 @@ rayon = "1.5.3" regex = "1.9.1" pin-project-lite = "0.2.14" sha2 = { version = "0.10.2", default-features = false } +gdbstub = "0.7.10" +gdbstub_arch = "0.3.3" # ============================================================================= # @@ -522,10 +532,16 @@ component-model-async = [ "component-model", "wasmtime-wasi?/p3", "wasmtime-wasi-http?/p3", + "wasmtime-wasi-tls?/p3", "dep:futures", ] rr = ["wasmtime/rr", "component-model", "wasmtime-cli-flags/rr", "run"] +# Enabled in release builds. We cannot enable the built-in gdbstub component in +# the default build because the published-to-crates.io crates cannot build the +# artifact (a limitation caused by lack of artifact dependencies). +gdbstub = ["debug", "dep:gdbstub-component-artifact"] + # This feature, when enabled, will statically compile out all logging statements # throughout Wasmtime and its dependencies. disable-logging = ["log/max_level_off", "tracing/max_level_off"] @@ -565,7 +581,7 @@ gc-drc = ["gc", "wasmtime/gc-drc", "wasmtime-cli-flags/gc-drc"] gc-null = ["gc", "wasmtime/gc-null", "wasmtime-cli-flags/gc-null"] pulley = ["wasmtime-cli-flags/pulley"] stack-switching = ["wasmtime/stack-switching", "wasmtime-cli-flags/stack-switching"] -debug = ["wasmtime-cli-flags/debug", "wasmtime/debug"] +debug = ["wasmtime-cli-flags/debug", "wasmtime/debug", "component-model"] # CLI subcommands for the `wasmtime` executable. See `wasmtime $cmd --help` # for more information on each subcommand. @@ -576,6 +592,7 @@ serve = [ "dep:http-body-util", "dep:http", "wasmtime-cli-flags/async", + "wasmtime-wasi-http?/p2", ] explore = ["dep:wasmtime-explorer", "dep:tempfile"] wast = ["dep:wasmtime-wast"] @@ -589,6 +606,8 @@ run = [ "dep:wasi-common", "dep:tokio", "wasmtime-cli-flags/async", + "wasmtime-wasi-http?/p2", + "dep:wasmtime-debugger", ] completion = ["dep:clap_complete"] objdump = [ @@ -674,6 +693,12 @@ debug-assertions = false overflow-checks = false opt-level = 's' +# FIXME(tokio-rs/tokio#8062): once that gets merged, published, and updated here +# then this can be removed. Otherwise this is causing spurious failures on CI at +# this time. +[profile.dev.package.tokio] +overflow-checks = false + [profile.profiling] inherits = "bench" debug = "line-tables-only" @@ -682,3 +707,55 @@ debug = "line-tables-only" inherits = "release" codegen-units = 1 lto = true + +# https://github.com/crate-ci/typos/blob/master/docs/reference.md +[workspace.metadata.typos.default] +extend-ignore-re = [ + "(\\d\\w{2,}|\\w{2,}\\d)", # possible hex encoding +] +extend-ignore-words-re = [ + '^[a-zA-Z]{1,3}$' # ignore words up to length 3 +] + +extend-ignore-identifiers-re = [ + # prologue, main, epilogue + "set[pme]", +] + +[workspace.metadata.typos.default.extend-identifiers] +# Arithmetic Logic Unit +alu = "alu" +# Data Source +datas = "datas" +# library +flate2 = "flate2" +ExpandedOrShrinked = "ExpandedOrShrinked" +EigthMem = "EigthMem" + +[workspace.metadata.typos.default.extend-words] +# Vector ALU +valu = "valu" +# AND Shifted +ANDS = "ANDS" +# Posix EACCES error +ACCES = "ACCES" +# SHift Left Long +shll = "shll" +# elements +eles = "eles" +# strategy +strat = "strat" +# test +thre = "thre" +# gimli definition +endianity = "endianity" +# A Low +alow = "alow" +aranges = "aranges" +# literals +lits = "lits" +RELA = "RELA" +fpr = "fpr" + +[workspace.metadata.typos.files] +extend-exclude = [ "docs/js/mermaid*.js", "crates/wasi-nn/**/*.txt", "*.isle" ] diff --git a/RELEASES.md b/RELEASES.md index d31c0fa618c6..f87f080cb4eb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,17 +1,103 @@ -## 43.0.0 +## 44.0.2 -Unreleased. +Released 2026-05-21. + +### Fixed + +* WASI path_open(TRUNCATE) bypasses `FilePerms::WRITE` host restriction. + [GHSA-2r75-cxrj-cmph](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-2r75-cxrj-cmph) + +-------------------------------------------------------------------------------- + +## 44.0.1 + +Released 2026-04-30. + +### Fixed + +* Panic when allocating a table exceeding the size of the host's address space. + [GHSA-p8xm-42r7-89xg](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-p8xm-42r7-89xg) + +-------------------------------------------------------------------------------- + +## 44.0.0 + +Released 2026-04-20. ### Added +* The `wasmtime` CLI now supports a `-g` flag which runs a built-in wasm program + to host a `gdbstub`-compatible server (can be connected to with LLDB) to debug + guest programs. + [#12756](https://github.com/bytecodealliance/wasmtime/pull/12756) + [#12771](https://github.com/bytecodealliance/wasmtime/pull/12771) + [#12856](https://github.com/bytecodealliance/wasmtime/pull/12856) + [#12859](https://github.com/bytecodealliance/wasmtime/pull/12859) + +* Wasmtime now has experimental support for the `map` type in the + component model. + [#12216](https://github.com/bytecodealliance/wasmtime/pull/12216) + +* Wasmtime's C API now supports wasm tag types. + [#12763](https://github.com/bytecodealliance/wasmtime/pull/12763) + [#12803](https://github.com/bytecodealliance/wasmtime/pull/12803) + +* Wasmtime's C API now supports exceptions. + [#12861](https://github.com/bytecodealliance/wasmtime/pull/12861) + +* Wasmtime's C API has more support for the GC proposal. + [#12914](https://github.com/bytecodealliance/wasmtime/pull/12914) + [#12915](https://github.com/bytecodealliance/wasmtime/pull/12915) + [#12916](https://github.com/bytecodealliance/wasmtime/pull/12916) + [#12917](https://github.com/bytecodealliance/wasmtime/pull/12917) + +* An initial implementation of the `wasi:tls` proposal for the 0.3.0-draft + version has been added. + [#12834](https://github.com/bytecodealliance/wasmtime/pull/12834) + ### Changed +* The `demangle` Cargo feature of the `wasmtime` crate is now compatible with + `no_std` targets. + [#12740](https://github.com/bytecodealliance/wasmtime/pull/12740) + +* The `wasmtime-wasi-tls-*` crates are now merged into one crate with feature + flags for each backend. + [#12780](https://github.com/bytecodealliance/wasmtime/pull/12780) + +* Wasmtime now requires Rust 1.92.0 or later to build. + [#12828](https://github.com/bytecodealliance/wasmtime/pull/12828) + +* The `cranelift-codegen` crate now compiles for `no_std` targets. + [#12812](https://github.com/bytecodealliance/wasmtime/pull/12812) + +* The `csdb` instruction, a defense-in-depth measure for spectre, is no longer + emitted by default on aarch64 to match what peer runtimes are doing. In some + situations this is known to provide up to a 6x performance boost on macOS as + well. + [#12932](https://github.com/bytecodealliance/wasmtime/pull/12932) + +### Fixed + +* Wasmtime's native DWARF has been improved on aarch64 to support recovering + values more frequently. + [#12779](https://github.com/bytecodealliance/wasmtime/pull/12779) + +* A significant number of minor issues have been fixed throughout this release. + In addition to the [security advisories][adv] found by LLMs there have also + been a large number of other issues identified as well. Many minor fixes are + present in this release for various situations for issues found in this + manner. + +[adv]: https://bytecodealliance.org/articles/wasmtime-security-advisories + -------------------------------------------------------------------------------- Release notes for previous releases of Wasmtime can be found on the respective release branches of the Wasmtime repository. +* [43.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-43.0.0/RELEASES.md) * [42.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-42.0.0/RELEASES.md) * [41.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-41.0.0/RELEASES.md) * [40.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-40.0.0/RELEASES.md) diff --git a/ci/build-release-artifacts.sh b/ci/build-release-artifacts.sh index e6e0ca33aa7a..5f515e87d630 100755 --- a/ci/build-release-artifacts.sh +++ b/ci/build-release-artifacts.sh @@ -59,7 +59,7 @@ if [[ "$build" = *-min ]]; then else # For release builds the CLI is built a bit more feature-ful than the Cargo # defaults to provide artifacts that can do as much as possible. - bin_flags="--features all-arch,component-model" + bin_flags="--features all-arch,component-model,gdbstub" fi if [[ "$target" = "x86_64-pc-windows-msvc" ]]; then diff --git a/ci/run-tests.py b/ci/run-tests.py index 8e08a7b87d19..f273eb40f82f 100755 --- a/ci/run-tests.py +++ b/ci/run-tests.py @@ -7,10 +7,7 @@ # - wasmtime-wasi-nn: mutually-exclusive features that aren't available for all # targets, needs its own CI job. # -# - wasmtime-wasi-tls-nativetls: the openssl dependency does not play nice with -# cross compilation. This crate is tested in a separate CI job. -# -# - wasmtime-wasi-tls-openssl: the openssl dependency does not play nice with +# - wasmtime-wasi-tls: the openssl dependency does not play nice with # cross compilation. This crate is tested in a separate CI job. # # - wasmtime-fuzzing: enabling all features brings in OCaml which is a pain to @@ -29,8 +26,7 @@ args = ['cargo', 'test', '--workspace', '--all-features'] args.append('--exclude=test-programs') args.append('--exclude=wasmtime-wasi-nn') -args.append('--exclude=wasmtime-wasi-tls-nativetls') -args.append('--exclude=wasmtime-wasi-tls-openssl') +args.append('--exclude=wasmtime-wasi-tls') args.append('--exclude=wasmtime-fuzzing') args.append('--exclude=wasm-spec-interpreter') args.append('--exclude=veri_engine') diff --git a/ci/vendor-c-api-headers.sh b/ci/vendor-c-api-headers.sh index fa0b417927d7..5f002f4b5f01 100755 --- a/ci/vendor-c-api-headers.sh +++ b/ci/vendor-c-api-headers.sh @@ -2,7 +2,7 @@ # Script to re-vendor the headers from the `wasm-c-api` proposal. -rev="2ce1367c9d1271c83fb63bef26d896a2f290cd23" +rev="9d6b93764ac96cdd9db51081c363e09d2d488b4d" files="wasm.h wasm.hh" set -ex diff --git a/ci/vendor-wit.sh b/ci/vendor-wit.sh index 150b4ee149bf..5880d81ed2e9 100755 --- a/ci/vendor-wit.sh +++ b/ci/vendor-wit.sh @@ -10,31 +10,33 @@ set -ex cache_dir=$(mktemp -d) trap "rm -rf $cache_dir" EXIT -# Helper to download the `WebAssembly/$repo` dir at the `$tag` (or rev) -# specified. The `wit/*.wit` files are placed in `$path`. +# Helper to download content from `WebAssembly/$repo` at the `$tag` (or rev) +# specified. By default `wit/*` is copied into `$path`, or a different +# subdirectory can be specified with the optional fourth argument. get_github() { local repo=$1 local tag=$2 local path=$3 + local prefix=${4:-wit} rm -rf "$path" mkdir -p "$path" - cached_extracted_dir="$cache_dir/$repo-$tag" + cached_extracted_dir="$cache_dir/$prefix/$repo-$tag" if [[ ! -d $cached_extracted_dir ]]; then mkdir -p $cached_extracted_dir curl --retry 5 --retry-all-errors -sLO https://github.com/WebAssembly/$repo/archive/$tag.tar.gz tar xzf $tag.tar.gz --strip-components=1 -C $cached_extracted_dir rm $tag.tar.gz - rm -rf $cached_extracted_dir/wit/deps* + rm -rf $cached_extracted_dir/${prefix}/deps* fi - cp -r $cached_extracted_dir/wit/* $path + cp -r $cached_extracted_dir/${prefix}/* $path } p2=0.2.6 -p3=0.3.0-rc-2026-02-09 +p3=0.3.0-rc-2026-03-15 rm -rf crates/wasi-io/wit/deps mkdir -p crates/wasi-io/wit/deps @@ -65,6 +67,10 @@ mkdir -p crates/wasi-tls/wit/deps wkg get --format wit --overwrite "wasi:io@$p2" -o "crates/wasi-tls/wit/deps/io.wit" get_github wasi-tls v0.2.0-draft+505fc98 crates/wasi-tls/wit/deps/tls +rm -rf crates/wasi-tls/src/p3/wit/deps +mkdir -p crates/wasi-tls/src/p3/wit/deps +get_github wasi-tls 6781ae2 crates/wasi-tls/src/p3/wit/deps/tls wit-0.3.0-draft + rm -rf crates/wasi-config/wit/deps mkdir -p crates/wasi-config/wit/deps get_github wasi-config v0.2.0-rc.1 crates/wasi-config/wit/deps/config @@ -90,6 +96,15 @@ wkg get --format wit --overwrite "wasi:random@$p3" -o "crates/wasi-http/src/p3/w wkg get --format wit --overwrite "wasi:sockets@$p3" -o "crates/wasi-http/src/p3/wit/deps/sockets.wit" wkg get --format wit --overwrite "wasi:http@$p3" -o "crates/wasi-http/src/p3/wit/deps/http.wit" +rm -rf crates/debugger/wit/deps +mkdir -p crates/debugger/wit/deps +wkg get --format wit --overwrite "wasi:io@$p2" -o "crates/debugger/wit/deps/io.wit" +wkg get --format wit --overwrite "wasi:clocks@$p2" -o "crates/debugger/wit/deps/clocks.wit" +wkg get --format wit --overwrite "wasi:cli@$p2" -o "crates/debugger/wit/deps/cli.wit" +wkg get --format wit --overwrite "wasi:filesystem@$p2" -o "crates/debugger/wit/deps/filesystem.wit" +wkg get --format wit --overwrite "wasi:random@$p2" -o "crates/debugger/wit/deps/random.wit" +wkg get --format wit --overwrite "wasi:sockets@$p2" -o "crates/debugger/wit/deps/sockets.wit" + # wasi-nn is fetched separately since it's not in the standard WASI registry repo=https://raw.githubusercontent.com/WebAssembly/wasi-nn revision=0.2.0-rc-2024-10-28 diff --git a/cranelift/assembler-x64/Cargo.toml b/cranelift/assembler-x64/Cargo.toml index 67b1d6d68663..d27dee65a479 100644 --- a/cranelift/assembler-x64/Cargo.toml +++ b/cranelift/assembler-x64/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cranelift-assembler-x64" description = "A Cranelift-specific x64 assembler" -version = "0.130.0" +version = "0.131.2" license = "Apache-2.0 WITH LLVM-exception" edition.workspace = true rust-version.workspace = true @@ -16,7 +16,7 @@ arbtest = { workspace = true } capstone = { workspace = true } [build-dependencies] -cranelift-assembler-x64-meta = { path = "meta", version = "0.130.0" } +cranelift-assembler-x64-meta = { path = "meta", version = "0.131.2" } [lints] workspace = true diff --git a/cranelift/assembler-x64/meta/Cargo.toml b/cranelift/assembler-x64/meta/Cargo.toml index 8ee3c7fd03d3..d8524ed9b166 100644 --- a/cranelift/assembler-x64/meta/Cargo.toml +++ b/cranelift/assembler-x64/meta/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cranelift-assembler-x64-meta" description = "Generate a Cranelift-specific assembler for x64 instructions" -version = "0.130.0" +version = "0.131.2" license = "Apache-2.0 WITH LLVM-exception" edition.workspace = true rust-version.workspace = true diff --git a/cranelift/assembler-x64/meta/src/generate/format.rs b/cranelift/assembler-x64/meta/src/generate/format.rs index c2b9526179a6..a0664463bf7d 100644 --- a/cranelift/assembler-x64/meta/src/generate/format.rs +++ b/cranelift/assembler-x64/meta/src/generate/format.rs @@ -25,7 +25,7 @@ enum ModRmStyle { }, /// Same as `RegMem` above except that this is also used for VEX-encoded - /// instructios with "/is4" which indicates that the 4th register operand + /// instructions with "/is4" which indicates that the 4th register operand /// is encoded in a byte after the ModR/M byte. RegMemIs4 { reg: ModRmReg, diff --git a/cranelift/bforest/Cargo.toml b/cranelift/bforest/Cargo.toml index 584556c80003..b7d88336ddeb 100644 --- a/cranelift/bforest/Cargo.toml +++ b/cranelift/bforest/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-bforest" -version = "0.130.0" +version = "0.131.2" description = "A forest of B+-trees" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-bforest" diff --git a/cranelift/bforest/src/map.rs b/cranelift/bforest/src/map.rs index 2c1e671f7f75..16e545031201 100644 --- a/cranelift/bforest/src/map.rs +++ b/cranelift/bforest/src/map.rs @@ -239,25 +239,37 @@ where } } - /// Iterate over the enties within the given range. + /// Iterate over the entries within the given range. pub fn range<'a, R, C>( &'a self, range: R, forest: &'a MapForest, comp: &'a C, - ) -> MapRange<'a, K, V, R, C> + ) -> MapRange<'a, K, V, C> where R: RangeBounds, C: Comparator, { MapRange { cursor: self.cursor(forest, comp), - range, - started: false, + start: copied_bound(range.start_bound()), + end: copied_bound(range.end_bound()), + direction: Direction::None, } } } +fn copied_bound(start_bound: Bound<&K>) -> Bound +where + K: Copy, +{ + match start_bound { + Bound::Unbounded => Bound::Unbounded, + Bound::Included(x) => Bound::Included(*x), + Bound::Excluded(x) => Bound::Excluded(*x), + } +} + impl Default for Map where K: Copy, @@ -676,46 +688,61 @@ where } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Direction { + None, + Next, + NextBack, +} + /// An immutable iterator over a range of values, returned by `Map::range`. -pub struct MapRange<'a, K, V, R, C> +pub struct MapRange<'a, K, V, C> where K: Copy, V: Copy, - R: RangeBounds, C: Comparator, { cursor: MapCursor<'a, K, V, C>, - range: R, - started: bool, + start: Bound, + end: Bound, + + /// The direction of the last call into the iterator, a.k.a. whether + /// `self.cursor` is + /// + /// 1. `Direction::Next`: just before `self.start`, + /// + /// 2. `Direction::NextBack`: just after `self.end`, or + /// + /// 3. `Direction::None`: not initialized to any location yet. + direction: Direction, } -impl<'a, K, V, R, C> Iterator for MapRange<'a, K, V, R, C> +impl<'a, K, V, C> Iterator for MapRange<'a, K, V, C> where K: Copy, V: Copy, - R: RangeBounds, C: Comparator, { type Item = (K, V); fn next(&mut self) -> Option { - let started = mem::replace(&mut self.started, true); + let old_direction = mem::replace(&mut self.direction, Direction::Next); let key_val = (|| { - if started { + if old_direction == Direction::Next { self.cursor.next() } else { - match self.range.start_bound() { + match self.start { Bound::Included(k) => { - self.cursor.goto(*k); + self.cursor.goto(k); Some((self.cursor.key()?, self.cursor.value()?)) } Bound::Excluded(k) => { - self.cursor.goto(*k); + self.cursor.goto(k); if self .cursor .key() - .is_some_and(|key| self.cursor.comp.cmp(*k, key).is_eq()) + .is_some_and(|key| self.cursor.comp.cmp(k, key).is_eq()) { self.cursor.next() } else { @@ -731,7 +758,61 @@ where })(); match key_val { - Some((key, val)) if self.is_in_bounds(key) => Some((key, val)), + Some((key, val)) if self.is_in_bounds(key) => { + self.start = Bound::Excluded(key); + Some((key, val)) + } + _ => { + self.cursor.goto_end(); + None + } + } + } +} + +impl<'a, K, V, C> DoubleEndedIterator for MapRange<'a, K, V, C> +where + K: Copy, + V: Copy, + C: Comparator, +{ + fn next_back(&mut self) -> Option { + let old_direction = mem::replace(&mut self.direction, Direction::NextBack); + + let key_val = (|| { + if old_direction == Direction::NextBack { + self.cursor.prev() + } else { + match self.end { + Bound::Included(k) => match self.cursor.goto(k) { + Some(_) => Some((self.cursor.key()?, self.cursor.value()?)), + None => self.cursor.prev(), + }, + Bound::Excluded(k) => { + self.cursor.goto(k); + if self + .cursor + .key() + .is_some_and(|key| self.cursor.comp.cmp(k, key).is_eq()) + { + self.cursor.prev() + } else { + Some((self.cursor.key()?, self.cursor.value()?)) + } + } + Bound::Unbounded => { + self.cursor.goto_end(); + self.cursor.prev() + } + } + } + })(); + + match key_val { + Some((key, val)) if self.is_in_bounds(key) => { + self.end = Bound::Excluded(key); + Some((key, val)) + } _ => { self.cursor.goto_end(); None @@ -740,30 +821,28 @@ where } } -impl<'a, K, V, R, C> MapRange<'a, K, V, R, C> +impl<'a, K, V, C> MapRange<'a, K, V, C> where K: Copy, V: Copy, - R: RangeBounds, C: Comparator, { fn is_in_bounds(&self, key: K) -> bool { - debug_assert!(self.is_in_start_bound(key)); - self.is_in_end_bound(key) + self.is_in_start_bound(key) && self.is_in_end_bound(key) } fn is_in_start_bound(&self, key: K) -> bool { - match self.range.start_bound() { - Bound::Included(k) => self.cursor.comp.cmp(key, *k).is_ge(), - Bound::Excluded(k) => self.cursor.comp.cmp(key, *k).is_gt(), + match self.start { + Bound::Included(k) => self.cursor.comp.cmp(key, k).is_ge(), + Bound::Excluded(k) => self.cursor.comp.cmp(key, k).is_gt(), Bound::Unbounded => true, } } fn is_in_end_bound(&self, key: K) -> bool { - match self.range.end_bound() { - Bound::Included(k) => self.cursor.comp.cmp(*k, key).is_ge(), - Bound::Excluded(k) => self.cursor.comp.cmp(*k, key).is_gt(), + match self.end { + Bound::Included(k) => self.cursor.comp.cmp(k, key).is_ge(), + Bound::Excluded(k) => self.cursor.comp.cmp(k, key).is_gt(), Bound::Unbounded => true, } } @@ -1461,4 +1540,50 @@ mod tests { vec![(36, 36.0), (38, 38.0)], ); } + + #[test] + fn range_next_back() { + let mut f = MapForest::::new(); + let mut m = Map::::new(); + + for i in 10..20 { + m.insert(i, i as f32, &mut f, &()); + } + + assert_eq!( + m.range(12..16, &f, &()).rev().collect::>(), + vec![(15, 15.0), (14, 14.0), (13, 13.0), (12, 12.0)], + ); + + assert_eq!( + m.range(18.., &f, &()).rev().collect::>(), + vec![(19, 19.0), (18, 18.0)], + ); + + assert_eq!( + m.range(..12, &f, &()).rev().collect::>(), + vec![(11, 11.0), (10, 10.0)], + ); + + assert_eq!( + m.range(11..=12, &f, &()).rev().collect::>(), + vec![(12, 12.0), (11, 11.0)], + ); + + let mut iter = m.range(13..=16, &f, &()); + assert_eq!(iter.next(), Some((13, 13.0))); + assert_eq!(iter.next_back(), Some((16, 16.0))); + assert_eq!(iter.next(), Some((14, 14.0))); + assert_eq!(iter.next_back(), Some((15, 15.0))); + assert!(iter.next().is_none()); + assert!(iter.next_back().is_none()); + + let mut iter = m.range(13..=16, &f, &()); + assert_eq!(iter.next_back(), Some((16, 16.0))); + assert_eq!(iter.next(), Some((13, 13.0))); + assert_eq!(iter.next_back(), Some((15, 15.0))); + assert_eq!(iter.next(), Some((14, 14.0))); + assert!(iter.next_back().is_none()); + assert!(iter.next().is_none()); + } } diff --git a/cranelift/bforest/src/set.rs b/cranelift/bforest/src/set.rs index fecc149f6157..a17e705c3d99 100644 --- a/cranelift/bforest/src/set.rs +++ b/cranelift/bforest/src/set.rs @@ -544,7 +544,7 @@ mod tests { // Level 1: 1 root // Level 2: 8 inner // Level 3: 64 inner - // Level 4: 512 leafs, up to 7680 elements + // Level 4: 512 leaves, up to 7680 elements // // A 3-level tree can hold at most 960 elements. fn dense4l(f: &mut SetForest) -> Set { diff --git a/cranelift/bitset/Cargo.toml b/cranelift/bitset/Cargo.toml index e5a1e424f3f1..d34699168b4c 100644 --- a/cranelift/bitset/Cargo.toml +++ b/cranelift/bitset/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-bitset" -version = "0.130.0" +version = "0.131.2" description = "Various bitset stuff for use inside Cranelift" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-bitset" diff --git a/cranelift/bitset/src/compound.rs b/cranelift/bitset/src/compound.rs index fa08e927cb39..80cc40f60eb8 100644 --- a/cranelift/bitset/src/compound.rs +++ b/cranelift/bitset/src/compound.rs @@ -3,7 +3,7 @@ use crate::scalar::{self, ScalarBitSet, ScalarBitSetStorage}; use alloc::boxed::Box; use core::{cmp, iter, mem}; -use wasmtime_core::alloc::{TryExtend, Vec}; +use wasmtime_core::alloc::{TryExtend, TryVec}; use wasmtime_core::error::OutOfMemory; /// A large bit set backed by dynamically-sized storage. @@ -295,7 +295,7 @@ impl CompoundBitSet { // Don't make ridiculously small allocations. let to_grow = cmp::max(to_grow, 4); - let mut new_elems = Vec::from(mem::take(&mut self.elems)); + let mut new_elems = TryVec::from(mem::take(&mut self.elems)); new_elems.reserve_exact(to_grow)?; new_elems.try_extend(iter::repeat(ScalarBitSet::new()).take(to_grow))?; self.elems = new_elems.into_boxed_slice()?; diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml index 103b13fa51dd..593987f8cb28 100644 --- a/cranelift/codegen/Cargo.toml +++ b/cranelift/codegen/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-codegen" -version = "0.130.0" +version = "0.131.2" description = "Low-level code generator library" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-codegen" @@ -25,7 +25,7 @@ anyhow = { workspace = true, optional = true, features = ['std'] } bumpalo = { workspace = true } capstone = { workspace = true, optional = true } cranelift-assembler-x64 = { workspace = true } -cranelift-codegen-shared = { path = "./shared", version = "0.130.0" } +cranelift-codegen-shared = { path = "./shared", version = "0.131.2" } cranelift-entity = { workspace = true } cranelift-bforest = { workspace = true } cranelift-bitset = { workspace = true } @@ -57,8 +57,8 @@ env_logger = { workspace = true } proptest = { workspace = true } [build-dependencies] -cranelift-codegen-meta = { path = "meta", version = "0.130.0" } -cranelift-isle = { path = "../isle/isle", version = "=0.130.0" } +cranelift-codegen-meta = { path = "meta", version = "0.131.2" } +cranelift-isle = { path = "../isle/isle", version = "=0.131.2" } [features] default = ["std", "unwind", "host-arch", "timing"] diff --git a/cranelift/codegen/build.rs b/cranelift/codegen/build.rs index 18cc7b8bf1f6..82b4e7a8e4d6 100644 --- a/cranelift/codegen/build.rs +++ b/cranelift/codegen/build.rs @@ -216,12 +216,23 @@ fn run_compilation(compilation: &IsleCompilation) -> Result<(), Errors> { // the generated code to help debug rule matching. options.emit_logging = std::env::var("CARGO_FEATURE_TRACE_LOG").is_ok(); - // Enable optional match-arm splitting in iterator terms for faster compile times. - options.split_match_arms = std::env::var("CARGO_FEATURE_ISLE_SPLIT_MATCH").is_ok(); - if let Ok(value) = std::env::var("ISLE_SPLIT_MATCH_THRESHOLD") { - options.match_arm_split_threshold = Some(value.parse().unwrap_or_else(|err| { - panic!("invalid ISLE_SPLIT_MATCH_THRESHOLD value '{value}': {err}"); - })); + // Enable optional match-arm splitting in iterator terms for + // faster compile times in release builds. + // + // In debug builds, *always* split with an aggressive + // threshold, because we cannot rely on rustc doing regalloc + // on all of the local bindings to shrink the stack frame to a + // reasonable size. + if cfg!(debug_assertions) { + options.split_match_arms = true; + options.match_arm_split_threshold = Some(4); + } else { + options.split_match_arms = std::env::var("CARGO_FEATURE_ISLE_SPLIT_MATCH").is_ok(); + if let Ok(value) = std::env::var("ISLE_SPLIT_MATCH_THRESHOLD") { + options.match_arm_split_threshold = Some(value.parse().unwrap_or_else(|err| { + panic!("invalid ISLE_SPLIT_MATCH_THRESHOLD value '{value}': {err}"); + })); + } } if let Ok(out_dir) = std::env::var("OUT_DIR") { diff --git a/cranelift/codegen/meta/Cargo.toml b/cranelift/codegen/meta/Cargo.toml index a5740c5b8ea2..ab819f071c80 100644 --- a/cranelift/codegen/meta/Cargo.toml +++ b/cranelift/codegen/meta/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cranelift-codegen-meta" authors = ["The Cranelift Project Developers"] -version = "0.130.0" +version = "0.131.2" description = "Metaprogram for cranelift-codegen code generator library" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" @@ -17,8 +17,8 @@ rustdoc-args = ["--document-private-items"] [dependencies] cranelift-srcgen = { workspace = true } -cranelift-assembler-x64-meta = { path = "../../assembler-x64/meta", version = "0.130.0" } -cranelift-codegen-shared = { path = "../shared", version = "0.130.0" } +cranelift-assembler-x64-meta = { path = "../../assembler-x64/meta", version = "0.131.2" } +cranelift-codegen-shared = { path = "../shared", version = "0.131.2" } pulley-interpreter = { workspace = true, optional = true } heck = "0.5.0" diff --git a/cranelift/codegen/meta/src/gen_isle.rs b/cranelift/codegen/meta/src/gen_isle.rs index 79755ca3636b..e47a92ab4e23 100644 --- a/cranelift/codegen/meta/src/gen_isle.rs +++ b/cranelift/codegen/meta/src/gen_isle.rs @@ -230,6 +230,9 @@ fn gen_common_isle( (IsleTarget::Opt, _, 1, false) => ("Value", true, "make_inst", "inst_data_value"), (IsleTarget::Opt, _, _, _) => ("Inst", false, "make_skeleton_inst", "inst_data"), + (IsleTarget::Lower, false, r, _) if r >= 1 => { + ("Inst", true, "make_inst", "inst_data_value") + } (IsleTarget::Lower, _, _, _) => ("Inst", false, "make_inst", "inst_data_value"), }; @@ -268,7 +271,13 @@ fn gen_common_isle( let mut s = format!( "({inst_data_etor} {}(InstructionData.{} (Opcode.{})", - if ty_in_decl { "ty " } else { "" }, + if ty_in_decl { + "ty " + } else if isle_target == IsleTarget::Lower { + "_ " + } else { + "" + }, inst.format.name, inst.camel_name ); @@ -785,6 +794,16 @@ impl NumericOp<'_> { body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#, ..shift.clone() }, + NumericOp { + name: "rotl", + body: "a.rotate_left(b)", + ..shift.clone() + }, + NumericOp { + name: "rotr", + body: "a.rotate_right(b)", + ..shift.clone() + }, // Predicates. // // We generate both pure constructors and a variety of extractors diff --git a/cranelift/codegen/meta/src/isa/arm64.rs b/cranelift/codegen/meta/src/isa/arm64.rs index afad7d15468f..e43b74bc2e3a 100644 --- a/cranelift/codegen/meta/src/isa/arm64.rs +++ b/cranelift/codegen/meta/src/isa/arm64.rs @@ -54,6 +54,12 @@ pub(crate) fn define() -> TargetIsa { "", false, ); + settings.add_bool( + "use_csdb", + "Use the `csdb` instruction when spectre mitigations are enabled", + "", + false, + ); TargetIsa::new("arm64", settings.build()) } diff --git a/cranelift/codegen/meta/src/isa/x86.rs b/cranelift/codegen/meta/src/isa/x86.rs index 3780949d58c4..b757e174035d 100644 --- a/cranelift/codegen/meta/src/isa/x86.rs +++ b/cranelift/codegen/meta/src/isa/x86.rs @@ -407,19 +407,19 @@ pub(crate) fn define() -> TargetIsa { // Generic - settings.add_preset("x86_64", "Generic x86-64 microarchitecture.", preset!()); + settings.add_preset("x86-64", "Generic x86-64 microarchitecture.", preset!()); let x86_64_v2 = settings.add_preset( - "x86_64_v2", + "x86-64-v2", "Generic x86-64 (V2) microarchitecture.", preset!(sse42 && has_popcnt && has_cmpxchg16b), ); let x86_64_v3 = settings.add_preset( - "x86_64_v3", + "x86-64-v3", "Generic x86-64 (V3) microarchitecture.", preset!(x86_64_v2 && has_bmi1 && has_bmi2 && has_fma && has_lzcnt && has_avx2), ); settings.add_preset( - "x86_64_v4", + "x86-64-v4", "Generic x86-64 (V4) microarchitecture.", preset!(x86_64_v3 && has_avx512dq && has_avx512vl), ); diff --git a/cranelift/codegen/meta/src/shared/settings.rs b/cranelift/codegen/meta/src/shared/settings.rs index b152b4d46289..7849359ceaa4 100644 --- a/cranelift/codegen/meta/src/shared/settings.rs +++ b/cranelift/codegen/meta/src/shared/settings.rs @@ -77,22 +77,8 @@ pub(crate) fn define() -> SettingGroup { true, ); - settings.add_bool( - "enable_pcc", - "Enable proof-carrying code translation validation.", - r#" - This adds a proof-carrying-code mode. Proof-carrying code (PCC) is a strategy to verify - that the compiler preserves certain properties or invariants in the compiled code. - For example, a frontend that translates WebAssembly to CLIF can embed PCC facts in - the CLIF, and Cranelift will verify that the final machine code satisfies the stated - facts at each intermediate computed value. Loads and stores can be marked as "checked" - and their memory effects can be verified as safe. - "#, - false, - ); - // Note that Cranelift doesn't currently need an is_pie flag, because PIE is - // just PIC where symbols can't be pre-empted, which can be expressed with the + // just PIC where symbols can't be preempted, which can be expressed with the // `colocated` flag on external functions and global values. settings.add_bool( "is_pic", diff --git a/cranelift/codegen/shared/Cargo.toml b/cranelift/codegen/shared/Cargo.toml index 44d8ca2f54e8..bffa5e3586f7 100644 --- a/cranelift/codegen/shared/Cargo.toml +++ b/cranelift/codegen/shared/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-codegen-shared" -version = "0.130.0" +version = "0.131.2" description = "For code shared between cranelift-codegen-meta and cranelift-codegen" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs index c37de05497bc..816121464a72 100644 --- a/cranelift/codegen/src/context.rs +++ b/cranelift/codegen/src/context.rs @@ -381,7 +381,6 @@ impl Context { &self.domtree, &self.loop_analysis, &mut alias_analysis, - &fisa.flags, ctrl_plane, ); pass.run(); diff --git a/cranelift/codegen/src/egraph/mod.rs b/cranelift/codegen/src/egraph/mod.rs index 280109470d01..d79ee8fdbe21 100644 --- a/cranelift/codegen/src/egraph/mod.rs +++ b/cranelift/codegen/src/egraph/mod.rs @@ -7,16 +7,13 @@ use crate::cursor::{Cursor, CursorPosition, FuncCursor}; use crate::dominator_tree::DominatorTree; use crate::egraph::elaborate::Elaborator; use crate::inst_predicates::{is_mergeable_for_egraph, is_pure_for_egraph}; -use crate::ir::pcc::Fact; use crate::ir::{ - Block, DataFlowGraph, Function, Inst, InstructionData, Opcode, Type, Value, ValueDef, - ValueListPool, + Block, DataFlowGraph, Function, Inst, InstructionData, Type, Value, ValueDef, ValueListPool, }; use crate::loop_analysis::LoopAnalysis; use crate::opts::IsleContext; use crate::opts::generated_code::SkeletonInstSimplification; use crate::scoped_hash_map::{Entry as ScopedEntry, ScopedHashMap}; -use crate::settings::Flags; use crate::take_and_replace::TakeAndReplace; use crate::trace; use alloc::vec::Vec; @@ -60,8 +57,6 @@ pub struct EgraphPass<'a> { /// Loop analysis results, used for built-in LICM during /// elaboration. loop_analysis: &'a LoopAnalysis, - /// Compiler flags. - flags: &'a Flags, /// Chaos-mode control-plane so we can test that we still get /// correct results when our heuristics make bad decisions. ctrl_plane: &'a mut ControlPlane, @@ -95,7 +90,6 @@ where domtree: &'opt DominatorTree, pub(crate) alias_analysis: &'opt mut AliasAnalysis<'analysis>, pub(crate) alias_analysis_state: &'opt mut LastStores, - flags: &'opt Flags, ctrl_plane: &'opt mut ControlPlane, // Held locally during optimization of one node (recursively): pub(crate) rewrite_depth: usize, @@ -170,7 +164,6 @@ where let result = self.func.dfg.first_result(inst); self.value_to_opt_value[result] = orig_result; self.available_block[result] = self.available_block[orig_result]; - self.func.dfg.merge_facts(result, orig_result); } orig_result } else { @@ -194,8 +187,6 @@ where } }; - self.attach_constant_fact(inst, result, ty); - self.available_block[result] = self.get_available_block(inst); let opt_value = self.optimize_pure_enode(inst); log::trace!("optimizing inst {inst} orig result {result} gave {opt_value}"); @@ -387,7 +378,6 @@ where ctx.eclass_size[union_value] = eclass_size - 1; ctx.stats.union += 1; trace!(" -> union: now {}", union_value); - ctx.func.dfg.merge_facts(old_union_value, optimized_value); ctx.available_block[union_value] = ctx.merge_availability(old_union_value, optimized_value); } @@ -503,7 +493,6 @@ where ); self.value_to_opt_value[result] = new_result; self.available_block[result] = self.available_block[new_result]; - self.func.dfg.merge_facts(result, new_result); Some(SkeletonInstSimplification::Remove) } // Otherwise, generic side-effecting op -- always keep it, and @@ -695,23 +684,6 @@ where // Return the best simplification! best } - - /// Helper to propagate facts on constant values: if PCC is - /// enabled, then unconditionally add a fact attesting to the - /// Value's concrete value. - fn attach_constant_fact(&mut self, inst: Inst, value: Value, ty: Type) { - if self.flags.enable_pcc() { - if let InstructionData::UnaryImm { - opcode: Opcode::Iconst, - imm, - } = self.func.dfg.insts[inst] - { - let imm: i64 = imm.into(); - self.func.dfg.facts[value] = - Some(Fact::constant(ty.bits().try_into().unwrap(), imm as u64)); - } - } - } } impl<'a> EgraphPass<'a> { @@ -721,7 +693,6 @@ impl<'a> EgraphPass<'a> { domtree: &'a DominatorTree, loop_analysis: &'a LoopAnalysis, alias_analysis: &'a mut AliasAnalysis<'a>, - flags: &'a Flags, ctrl_plane: &'a mut ControlPlane, ) -> Self { Self { @@ -729,7 +700,6 @@ impl<'a> EgraphPass<'a> { domtree, loop_analysis, alias_analysis, - flags, ctrl_plane, stats: Stats::default(), remat_values: FxHashSet::default(), @@ -897,7 +867,6 @@ impl<'a> EgraphPass<'a> { domtree: &self.domtree, alias_analysis: self.alias_analysis, alias_analysis_state: &mut alias_analysis_state, - flags: self.flags, ctrl_plane: self.ctrl_plane, optimized_values: Default::default(), optimized_insts: Default::default(), @@ -1057,7 +1026,7 @@ impl<'a> EgraphPass<'a> { /// Implementation of external-context equality and hashing on /// InstructionData. This allows us to deduplicate instructions given /// some context that lets us see its value lists, so we don't need to -/// store arguments inline in the `InstuctionData` (or alongside it in +/// store arguments inline in the `InstructionData` (or alongside it in /// some newly-defined key type) in all cases. struct GVNContext<'a> { value_lists: &'a ValueListPool, diff --git a/cranelift/codegen/src/inline.rs b/cranelift/codegen/src/inline.rs index ac150c598980..da2a6e220844 100644 --- a/cranelift/codegen/src/inline.rs +++ b/cranelift/codegen/src/inline.rs @@ -7,7 +7,7 @@ //! have been marked the equivalent of `#[inline(never)]`, etc... Only the //! Cranelift user can understand these aspects of the full compilation //! pipeline, and these things can be very different between (say) Wasmtime and -//! `cg_clif`. Therefore, this module does not attempt to define hueristics for +//! `cg_clif`. Therefore, this module does not attempt to define heuristics for //! when inlining a particular call is likely beneficial. This module only //! provides hooks for the Cranelift user to define whether a given call should //! be inlined or not, and the mechanics to inline a callee into a particular @@ -60,7 +60,7 @@ pub trait Inline { /// A hook invoked for each direct call instruction in a function, whose /// result determines whether Cranelift should inline a given call. /// - /// The Cranelift user is responsible for defining their own hueristics and + /// The Cranelift user is responsible for defining their own heuristics and /// deciding whether inlining the call is beneficial. /// /// When returning a function and directing Cranelift to inline its body diff --git a/cranelift/codegen/src/ir/dfg.rs b/cranelift/codegen/src/ir/dfg.rs index 016ab32e83f3..7d5843d7f458 100644 --- a/cranelift/codegen/src/ir/dfg.rs +++ b/cranelift/codegen/src/ir/dfg.rs @@ -5,7 +5,6 @@ use crate::ir; use crate::ir::builder::ReplaceBuilder; use crate::ir::dynamic_type::{DynamicTypeData, DynamicTypes}; use crate::ir::instructions::{CallInfo, InstructionData}; -use crate::ir::pcc::Fact; use crate::ir::user_stack_maps::{UserStackMapEntry, UserStackMapEntryVec}; use crate::ir::{ Block, BlockArg, BlockCall, ConstantData, ConstantPool, DynamicType, ExceptionTables, @@ -144,9 +143,6 @@ pub struct DataFlowGraph { /// Primary value table with entries for all values. values: PrimaryMap, - /// Facts: proof-carrying-code assertions about values. - pub facts: SecondaryMap>, - /// Function signature table. These signatures are referenced by indirect call instructions as /// well as the external function references. pub signatures: PrimaryMap, @@ -181,7 +177,6 @@ impl DataFlowGraph { dynamic_types: DynamicTypes::new(), value_lists: ValueListPool::new(), values: PrimaryMap::new(), - facts: SecondaryMap::new(), signatures: PrimaryMap::new(), ext_funcs: PrimaryMap::new(), values_labels: None, @@ -207,7 +202,6 @@ impl DataFlowGraph { self.constants.clear(); self.immediates.clear(); self.jump_tables.clear(); - self.facts.clear(); } /// Get the total number of instructions created in this function, whether they are currently @@ -489,21 +483,6 @@ impl DataFlowGraph { // removed), and unions (but the egraph pass ensures there are no // aliases before creating unions). - // Merge `facts` from any alias onto the aliased value. Note that if - // there was a chain of aliases, at this point every alias that was in - // the chain points to the same final value, so their facts will all be - // merged together. - for value in self.facts.keys() { - if let ValueData::Alias { original, .. } = self.values[value].into() { - if let Some(new_fact) = self.facts[value].take() { - match &mut self.facts[original] { - Some(old_fact) => *old_fact = Fact::intersect(old_fact, &new_fact), - old_fact => *old_fact = Some(new_fact), - } - } - } - } - // - `signatures` and `ext_funcs` have no values. if let Some(values_labels) = &mut self.values_labels { @@ -1075,13 +1054,7 @@ impl DataFlowGraph { // Get the controlling type variable. let ctrl_typevar = self.ctrl_typevar(inst); // Create new result values. - let num_results = self.make_inst_results(new_inst, ctrl_typevar); - // Copy over PCC facts, if any. - for i in 0..num_results { - let old_result = self.inst_results(inst)[i]; - let new_result = self.inst_results(new_inst)[i]; - self.facts[new_result] = self.facts[old_result].clone(); - } + self.make_inst_results(new_inst, ctrl_typevar); new_inst } @@ -1416,38 +1389,6 @@ impl DataFlowGraph { pub fn detach_inst_results(&mut self, inst: Inst) { self.results[inst].clear(&mut self.value_lists); } - - /// Merge the facts for two values. If both values have facts and - /// they differ, both values get a special "conflict" fact that is - /// never satisfied. - pub fn merge_facts(&mut self, a: Value, b: Value) { - let a = self.resolve_aliases(a); - let b = self.resolve_aliases(b); - match (&self.facts[a], &self.facts[b]) { - (Some(a), Some(b)) if a == b => { /* nothing */ } - (None, None) => { /* nothing */ } - (Some(a), None) => { - self.facts[b] = Some(a.clone()); - } - (None, Some(b)) => { - self.facts[a] = Some(b.clone()); - } - (Some(a_fact), Some(b_fact)) => { - assert_eq!(self.value_type(a), self.value_type(b)); - let merged = Fact::intersect(a_fact, b_fact); - crate::trace!( - "facts merge on {} and {}: {:?}, {:?} -> {:?}", - a, - b, - a_fact, - b_fact, - merged, - ); - self.facts[a] = Some(merged.clone()); - self.facts[b] = Some(merged); - } - } - } } /// Contents of a basic block. diff --git a/cranelift/codegen/src/ir/entities.rs b/cranelift/codegen/src/ir/entities.rs index d0ee82ca4872..b4bbd68bb619 100644 --- a/cranelift/codegen/src/ir/entities.rs +++ b/cranelift/codegen/src/ir/entities.rs @@ -187,25 +187,6 @@ impl GlobalValue { } } -/// An opaque reference to a memory type. -/// -/// A `MemoryType` is a descriptor of a struct layout in memory, with -/// types and proof-carrying-code facts optionally attached to the -/// fields. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub struct MemoryType(u32); -entity_impl!(MemoryType, "mt"); - -impl MemoryType { - /// Create a new memory type reference from its number. - /// - /// This method is for use by the parser. - pub fn with_number(n: u32) -> Option { - if n < u32::MAX { Some(Self(n)) } else { None } - } -} - /// An opaque reference to a constant. /// /// You can store [`ConstantData`](super::ConstantData) in a @@ -400,8 +381,6 @@ pub enum AnyEntity { DynamicType(DynamicType), /// A Global value. GlobalValue(GlobalValue), - /// A memory type. - MemoryType(MemoryType), /// A jump table. JumpTable(JumpTable), /// A constant. @@ -427,7 +406,6 @@ impl fmt::Display for AnyEntity { Self::DynamicStackSlot(r) => r.fmt(f), Self::DynamicType(r) => r.fmt(f), Self::GlobalValue(r) => r.fmt(f), - Self::MemoryType(r) => r.fmt(f), Self::JumpTable(r) => r.fmt(f), Self::Constant(r) => r.fmt(f), Self::FuncRef(r) => r.fmt(f), @@ -486,12 +464,6 @@ impl From for AnyEntity { } } -impl From for AnyEntity { - fn from(r: MemoryType) -> Self { - Self::MemoryType(r) - } -} - impl From for AnyEntity { fn from(r: JumpTable) -> Self { Self::JumpTable(r) diff --git a/cranelift/codegen/src/ir/function.rs b/cranelift/codegen/src/ir/function.rs index 80926edcdf1c..7253fd5e84c3 100644 --- a/cranelift/codegen/src/ir/function.rs +++ b/cranelift/codegen/src/ir/function.rs @@ -9,8 +9,8 @@ use crate::ir::DebugTags; use crate::ir::{ self, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData, DynamicStackSlots, DynamicType, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Inst, JumpTable, - JumpTableData, Layout, MemoryType, MemoryTypeData, SigRef, Signature, SourceLocs, StackSlot, - StackSlotData, StackSlots, Type, pcc::Fact, + JumpTableData, Layout, SigRef, Signature, SourceLocs, StackSlot, StackSlotData, StackSlots, + Type, }; use crate::isa::CallConv; use crate::write::{write_function, write_function_spec}; @@ -173,12 +173,6 @@ pub struct FunctionStencil { /// Global values referenced. pub global_values: PrimaryMap, - /// Global value proof-carrying-code facts. - pub global_value_facts: SecondaryMap>, - - /// Memory types for proof-carrying code. - pub memory_types: PrimaryMap, - /// Data flow graph containing the primary definition of all instructions, blocks and values. pub dfg: DataFlowGraph, @@ -221,8 +215,6 @@ impl FunctionStencil { self.sized_stack_slots.clear(); self.dynamic_stack_slots.clear(); self.global_values.clear(); - self.global_value_facts.clear(); - self.memory_types.clear(); self.dfg.clear(); self.layout.clear(); self.srclocs.clear(); @@ -257,11 +249,6 @@ impl FunctionStencil { self.global_values.push(data) } - /// Declares a memory type for use by the function. - pub fn create_memory_type(&mut self, data: MemoryTypeData) -> MemoryType { - self.memory_types.push(data) - } - /// Find the global dyn_scale value associated with given DynamicType. pub fn get_dyn_scale(&self, ty: DynamicType) -> GlobalValue { self.dfg.dynamic_types.get(ty).unwrap().dynamic_scale @@ -420,8 +407,6 @@ impl Function { sized_stack_slots: StackSlots::new(), dynamic_stack_slots: DynamicStackSlots::new(), global_values: PrimaryMap::new(), - global_value_facts: SecondaryMap::new(), - memory_types: PrimaryMap::new(), dfg: DataFlowGraph::new(), layout: Layout::new(), srclocs: SecondaryMap::new(), diff --git a/cranelift/codegen/src/ir/memflags.rs b/cranelift/codegen/src/ir/memflags.rs index 45f32ce85fd2..7e1f1cb43894 100644 --- a/cranelift/codegen/src/ir/memflags.rs +++ b/cranelift/codegen/src/ir/memflags.rs @@ -97,12 +97,6 @@ const BIT_LITTLE_ENDIAN: u16 = 1 << 2; /// Load multi-byte values from memory in a big-endian format. const BIT_BIG_ENDIAN: u16 = 1 << 3; -/// Check this load or store for safety when using the -/// proof-carrying-code framework. The address must have a -/// `PointsTo` fact attached with a sufficiently large valid range -/// for the accessed size. -const BIT_CHECKED: u16 = 1 << 4; - /// Used for alias analysis, indicates which disjoint part of the abstract state /// is being accessed. const MASK_ALIAS_REGION: u16 = 0b11 << ALIAS_REGION_OFFSET; @@ -204,7 +198,6 @@ impl MemFlags { } self.with_alias_region(Some(AliasRegion::Vmctx)) } - "checked" => self.with_checked(), "can_move" => self.with_can_move(), other => match TrapCode::from_str(other) { @@ -356,32 +349,6 @@ impl MemFlags { pub const fn with_readonly(self) -> Self { self.with_bit(BIT_READONLY) } - - /// Test if the `checked` bit is set. - /// - /// Loads and stores with this flag are verified to access - /// pointers only with a validated `PointsTo` fact attached, and - /// with that fact validated, when using the proof-carrying-code - /// framework. If initial facts on program inputs are correct - /// (i.e., correctly denote the shape and types of data structures - /// in memory), and if PCC validates the compiled output, then all - /// `checked`-marked memory accesses are guaranteed (up to the - /// checker's correctness) to access valid memory. This can be - /// used to ensure memory safety and sandboxing. - pub const fn checked(self) -> bool { - self.read_bit(BIT_CHECKED) - } - - /// Set the `checked` bit. - pub fn set_checked(&mut self) { - *self = self.with_checked(); - } - - /// Set the `checked` bit, returning new flags. - pub const fn with_checked(self) -> Self { - self.with_bit(BIT_CHECKED) - } - /// Get the trap code to report if this memory access traps. /// /// A `None` trap code indicates that this memory access does not trap. @@ -434,9 +401,6 @@ impl fmt::Display for MemFlags { if self.read_bit(BIT_LITTLE_ENDIAN) { write!(f, " little")?; } - if self.checked() { - write!(f, " checked")?; - } match self.alias_region() { None => {} Some(AliasRegion::Heap) => write!(f, " heap")?, diff --git a/cranelift/codegen/src/ir/memtype.rs b/cranelift/codegen/src/ir/memtype.rs deleted file mode 100644 index b7b0d7e2db1e..000000000000 --- a/cranelift/codegen/src/ir/memtype.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! Definitions for "memory types" in CLIF. -//! -//! A memory type is a struct-like definition -- fields with offsets, -//! each field having a type and possibly an attached fact -- that we -//! can use in proof-carrying code to validate accesses to structs and -//! propagate facts onto the loaded values as well. -//! -//! Memory types are meant to be rich enough to describe the *layout* -//! of values in memory, but do not necessarily need to embody -//! higher-level features such as subtyping directly. Rather, they -//! should encode an implementation of a type or object system. -//! -//! Note also that it is a non-goal for now for this type system to be -//! "complete" or fully orthogonal: we have some restrictions now -//! (e.g., struct fields are only primitives) because this is all we -//! need for existing PCC applications, and it keeps the -//! implementation simpler. -//! -//! There are a few basic kinds of types: -//! -//! - A struct is an aggregate of fields and an overall size. Each -//! field has a *primitive Cranelift type*. This is for simplicity's -//! sake: we do not allow nested memory types because to do so -//! invites cycles, requires recursive computation of sizes, creates -//! complicated questions when field types are dynamically-sized, -//! and in general is more complexity than we need. -//! -//! The expectation (validated by PCC) is that when a checked load -//! or store accesses memory typed by a memory type, accesses will -//! only be to fields at offsets named in the type, and will be via -//! the given Cranelift type -- i.e., no type-punning occurs in -//! memory. -//! -//! The overall size of the struct may be larger than that implied -//! by the fields because (i) we may not want or need to name all -//! the actually-existing fields in the memory type, and (ii) there -//! may be alignment padding that we also don't want or need to -//! represent explicitly. -//! -//! - A static memory is an untyped blob of storage with a static -//! size. This is memory that can be accessed with any type of load -//! or store at any valid offset. -//! -//! Note that this is *distinct* from an "array of u8" kind of -//! representation of memory, if/when we can represent such a thing, -//! because the expectation with memory types' fields (including -//! array elements) is that they are strongly typed, only accessed -//! via that type, and not type-punned. We don't want to imply any -//! restriction on load/store size, or any actual structure, with -//! untyped memory; it's just a blob. -//! -//! Eventually we plan to also have: -//! -//! - A dynamic array is a sequence of struct memory types, with a -//! length given by a global value (GV). This is useful to model, -//! e.g., tables. -//! -//! - A discriminated union is a union of several memory types -//! together with a tag field. This will be useful to model and -//! verify subtyping/downcasting for Wasm GC, among other uses. -//! -//! - Nullability on pointer fields: the fact will hold only if the -//! field is not null (all zero bits). - -use crate::ir::pcc::Fact; -use crate::ir::{GlobalValue, Type}; -use alloc::vec::Vec; - -#[cfg(feature = "enable-serde")] -use serde_derive::{Deserialize, Serialize}; - -/// Data defining a memory type. -/// -/// A memory type corresponds to a layout of data in memory. It may -/// have a statically-known or dynamically-known size. -#[derive(Clone, PartialEq, Hash)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub enum MemoryTypeData { - /// An aggregate consisting of certain fields at certain offsets. - /// - /// Fields must be sorted by offset, must be within the struct's - /// overall size, and must not overlap. These conditions are - /// checked by the CLIF verifier. - Struct { - /// Size of this type. - size: u64, - - /// Fields in this type. Sorted by offset. - fields: Vec, - }, - - /// A statically-sized untyped blob of memory. - Memory { - /// Accessible size. - size: u64, - }, - - /// A dynamically-sized untyped blob of memory, with bound given - /// by a global value plus some static amount. - DynamicMemory { - /// Static part of size. - size: u64, - /// Dynamic part of size. - gv: GlobalValue, - }, - - /// A type with no size. - Empty, -} - -impl core::default::Default for MemoryTypeData { - fn default() -> Self { - Self::Empty - } -} - -impl core::fmt::Display for MemoryTypeData { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - match self { - Self::Struct { size, fields } => { - write!(f, "struct {size} {{")?; - let mut first = true; - for field in fields { - if first { - first = false; - } else { - write!(f, ",")?; - } - write!(f, " {}: {}", field.offset, field.ty)?; - if field.readonly { - write!(f, " readonly")?; - } - if let Some(fact) = &field.fact { - write!(f, " ! {fact}")?; - } - } - write!(f, " }}")?; - Ok(()) - } - Self::Memory { size } => { - write!(f, "memory {size:#x}") - } - Self::DynamicMemory { size, gv } => { - write!(f, "dynamic_memory {gv}+{size:#x}") - } - Self::Empty => { - write!(f, "empty") - } - } - } -} - -/// One field in a memory type. -#[derive(Clone, PartialEq, Hash)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub struct MemoryTypeField { - /// The offset of this field in the memory type. - pub offset: u64, - /// The primitive type of the value in this field. Accesses to the - /// field must use this type (i.e., cannot bitcast/type-pun in - /// memory). - pub ty: Type, - /// A proof-carrying-code fact about this value, if any. - pub fact: Option, - /// Whether this field is read-only, i.e., stores should be - /// disallowed. - pub readonly: bool, -} - -impl MemoryTypeField { - /// Get the fact, if any, on a field. - pub fn fact(&self) -> Option<&Fact> { - self.fact.as_ref() - } -} - -impl MemoryTypeData { - /// Provide the static size of this type, if known. - /// - /// (The size may not be known for dynamically-sized arrays or - /// memories, when those memtype kinds are added.) - pub fn static_size(&self) -> Option { - match self { - Self::Struct { size, .. } => Some(*size), - Self::Memory { size } => Some(*size), - Self::DynamicMemory { .. } => None, - Self::Empty => Some(0), - } - } -} diff --git a/cranelift/codegen/src/ir/mod.rs b/cranelift/codegen/src/ir/mod.rs index eca17e7badda..3b9e540d48bd 100644 --- a/cranelift/codegen/src/ir/mod.rs +++ b/cranelift/codegen/src/ir/mod.rs @@ -20,8 +20,6 @@ pub(crate) mod known_symbol; pub mod layout; pub(crate) mod libcall; mod memflags; -mod memtype; -pub mod pcc; mod progpoint; mod sourceloc; pub mod stackslot; @@ -42,8 +40,7 @@ pub use crate::ir::dfg::{BlockData, DataFlowGraph, ValueDef}; pub use crate::ir::dynamic_type::{DynamicTypeData, DynamicTypes, dynamic_to_fixed}; pub use crate::ir::entities::{ Block, Constant, DynamicStackSlot, DynamicType, ExceptionTable, ExceptionTag, FuncRef, - GlobalValue, Immediate, Inst, JumpTable, MemoryType, SigRef, StackSlot, UserExternalNameRef, - Value, + GlobalValue, Immediate, Inst, JumpTable, SigRef, StackSlot, UserExternalNameRef, Value, }; pub use crate::ir::exception_table::{ExceptionTableData, ExceptionTableItem}; pub use crate::ir::extfunc::{ @@ -60,8 +57,6 @@ pub use crate::ir::known_symbol::KnownSymbol; pub use crate::ir::layout::Layout; pub use crate::ir::libcall::{LibCall, get_probestack_funcref}; pub use crate::ir::memflags::{AliasRegion, Endianness, MemFlags}; -pub use crate::ir::memtype::{MemoryTypeData, MemoryTypeField}; -pub use crate::ir::pcc::{BaseExpr, Expr, Fact, FactContext, PccError, PccResult}; pub use crate::ir::progpoint::ProgramPoint; pub use crate::ir::sourceloc::RelSourceLoc; pub use crate::ir::sourceloc::SourceLoc; diff --git a/cranelift/codegen/src/ir/pcc.rs b/cranelift/codegen/src/ir/pcc.rs deleted file mode 100644 index 8b9a3cc6bb6e..000000000000 --- a/cranelift/codegen/src/ir/pcc.rs +++ /dev/null @@ -1,1680 +0,0 @@ -//! Proof-carrying code. We attach "facts" to values and then check -//! that they remain true after compilation. -//! -//! A few key design principle of this approach are: -//! -//! - The producer of the IR provides the axioms. All "ground truth", -//! such as what memory is accessible -- is meant to come by way of -//! facts on the function arguments and global values. In some -//! sense, all we are doing here is validating the "internal -//! consistency" of the facts that are provided on values, and the -//! actions performed on those values. -//! -//! - We do not derive and forward-propagate facts eagerly. Rather, -//! the producer needs to provide breadcrumbs -- a "proof witness" -//! of sorts -- to allow the checking to complete. That means that -//! as an address is computed, or pointer chains are dereferenced, -//! each intermediate value will likely have some fact attached. -//! -//! This does create more verbose IR, but a significant positive -//! benefit is that it avoids unnecessary work: we do not build up a -//! knowledge base that effectively encodes the integer ranges of -//! many or most values in the program. Rather, we only check -//! specifically the memory-access sequences. In practice, each such -//! sequence is likely to be a carefully-controlled sequence of IR -//! operations from, e.g., a sandboxing compiler (such as -//! Wasmtime) so adding annotations here to communicate -//! intent (ranges, bounds-checks, and the like) is no problem. -//! -//! Facts are attached to SSA values in CLIF, and are maintained -//! through optimizations and through lowering. They are thus also -//! present on VRegs in the VCode. In theory, facts could be checked -//! at either level, though in practice it is most useful to check -//! them at the VCode level if the goal is an end-to-end verification -//! of certain properties (e.g., memory sandboxing). -//! -//! Checking facts entails visiting each instruction that defines a -//! value with a fact, and checking the result's fact against the -//! facts on arguments and the operand. For VCode, this is -//! fundamentally a question of the target ISA's semantics, so we call -//! into the `LowerBackend` for this. Note that during checking there -//! is also limited forward propagation / inference, but only within -//! an instruction: for example, an addressing mode commonly can -//! include an addition, multiplication/shift, or extend operation, -//! and there is no way to attach facts to the intermediate values -//! "inside" the instruction, so instead the backend can use -//! `FactContext::add()` and friends to forward-propagate facts. -//! -//! TODO: -//! -//! Deployment: -//! - Add to fuzzing -//! - Turn on during wasm spec-tests -//! -//! More checks: -//! - Check that facts on `vmctx` GVs are subsumed by the actual facts -//! on the vmctx arg in block0 (function arg). -//! -//! Generality: -//! - facts on outputs (in func signature)? -//! - Implement checking at the CLIF level as well. -//! - Check instructions that can trap as well? -//! -//! Nicer errors: -//! - attach instruction index or some other identifier to errors -//! -//! Text format cleanup: -//! - make the bitwidth on `max` facts optional in the CLIF text -//! format? -//! - make offset in `mem` fact optional in the text format? -//! -//! Bikeshed colors (syntax): -//! - Put fact bang-annotations after types? -//! `v0: i64 ! fact(..)` vs. `v0 ! fact(..): i64` - -use crate::ir; -use crate::ir::types::*; -use crate::isa::TargetIsa; -use crate::machinst::{BlockIndex, LowerBackend, VCode}; -use crate::trace; -use core::fmt; -use regalloc2::Function as _; - -#[cfg(feature = "enable-serde")] -use serde_derive::{Deserialize, Serialize}; - -/// The result of checking proof-carrying-code facts. -pub type PccResult = core::result::Result; - -/// An error or inconsistency discovered when checking proof-carrying -/// code. -#[derive(Debug, Clone)] -pub enum PccError { - /// An operation wraps around, invalidating the stated value - /// range. - Overflow, - /// An input to an operator that produces a fact-annotated value - /// does not have a fact describing it, and one is needed. - MissingFact, - /// A derivation of an output fact is unsupported (incorrect or - /// not derivable). - UnsupportedFact, - /// A block parameter claims a fact that one of its predecessors - /// does not support. - UnsupportedBlockparam, - /// A memory access is out of bounds. - OutOfBounds, - /// Proof-carrying-code checking is not implemented for a - /// particular compiler backend. - UnimplementedBackend, - /// Proof-carrying-code checking is not implemented for a - /// particular instruction that instruction-selection chose. This - /// is an internal compiler error. - UnimplementedInst, - /// Access to an invalid or undefined field offset in a struct. - InvalidFieldOffset, - /// Access to a field via the wrong type. - BadFieldType, - /// Store to a read-only field. - WriteToReadOnlyField, - /// Store of data to a field with a fact that does not subsume the - /// field's fact. - InvalidStoredFact, -} - -/// A fact on a value. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub enum Fact { - /// A bitslice of a value (up to a bitwidth) is within the given - /// integer range. - /// - /// The slicing behavior is needed because this fact can describe - /// both an SSA `Value`, whose entire value is well-defined, and a - /// `VReg` in VCode, whose bits beyond the type stored in that - /// register are don't-care (undefined). - Range { - /// The bitwidth of bits we care about, from the LSB upward. - bit_width: u16, - /// The minimum value that the bitslice can take - /// (inclusive). The range is unsigned: the specified bits of - /// the actual value will be greater than or equal to this - /// value, as evaluated by an unsigned integer comparison. - min: u64, - /// The maximum value that the bitslice can take - /// (inclusive). The range is unsigned: the specified bits of - /// the actual value will be less than or equal to this value, - /// as evaluated by an unsigned integer comparison. - max: u64, - }, - - /// A value bounded by a global value. - /// - /// The range is in `(min_GV + min_offset)..(max_GV + - /// max_offset)`, inclusive on the lower and upper bound. - DynamicRange { - /// The bitwidth of bits we care about, from the LSB upward. - bit_width: u16, - /// The lower bound, inclusive. - min: Expr, - /// The upper bound, inclusive. - max: Expr, - }, - - /// A pointer to a memory type. - Mem { - /// The memory type. - ty: ir::MemoryType, - /// The minimum offset into the memory type, inclusive. - min_offset: u64, - /// The maximum offset into the memory type, inclusive. - max_offset: u64, - /// This pointer can also be null. - nullable: bool, - }, - - /// A pointer to a memory type, dynamically bounded. The pointer - /// is within `(GV_min+offset_min)..(GV_max+offset_max)` - /// (inclusive on both ends) in the memory type. - DynamicMem { - /// The memory type. - ty: ir::MemoryType, - /// The lower bound, inclusive. - min: Expr, - /// The upper bound, inclusive. - max: Expr, - /// This pointer can also be null. - nullable: bool, - }, - - /// A definition of a value to be used as a symbol in - /// BaseExprs. There can only be one of these per value number. - /// - /// Note that this differs from a `DynamicRange` specifying that - /// some value in the program is the same as `value`. A `def(v1)` - /// fact is propagated to machine code and serves as a source of - /// truth: the value or location labeled with this fact *defines* - /// what `v1` is, and any `dynamic_range(64, v1, v1)`-labeled - /// values elsewhere are claiming to be equal to this value. - /// - /// This is necessary because we don't propagate SSA value labels - /// down to machine code otherwise; so when referring symbolically - /// to addresses and expressions derived from addresses, we need - /// to introduce the symbol first. - Def { - /// The SSA value this value defines. - value: ir::Value, - }, - - /// A comparison result between two dynamic values with a - /// comparison of a certain kind. - Compare { - /// The kind of comparison. - kind: ir::condcodes::IntCC, - /// The left-hand side of the comparison. - lhs: Expr, - /// The right-hand side of the comparison. - rhs: Expr, - }, - - /// A "conflict fact": this fact results from merging two other - /// facts, and it can never be satisfied -- checking any value - /// against this fact will fail. - Conflict, -} - -/// A bound expression. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub struct Expr { - /// The dynamic (base) part. - pub base: BaseExpr, - /// The static (offset) part. - pub offset: i64, -} - -/// The base part of a bound expression. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub enum BaseExpr { - /// No dynamic part (i.e., zero). - None, - /// A global value. - GlobalValue(ir::GlobalValue), - /// An SSA Value as a symbolic value. This can be referenced in - /// facts even after we've lowered out of SSA: it becomes simply - /// some symbolic value. - Value(ir::Value), - /// Top of the address space. This is "saturating": the offset - /// doesn't matter. - Max, -} - -impl BaseExpr { - /// Is one base less than or equal to another? (We can't always - /// know; in such cases, returns `false`.) - fn le(lhs: &BaseExpr, rhs: &BaseExpr) -> bool { - // (i) reflexivity; (ii) 0 <= x for all (unsigned) x; (iii) x <= max for all x. - lhs == rhs || *lhs == BaseExpr::None || *rhs == BaseExpr::Max - } - - /// Compute some BaseExpr that will be less than or equal to both - /// inputs. This is a generalization of `min` (but looser). - fn min(lhs: &BaseExpr, rhs: &BaseExpr) -> BaseExpr { - if lhs == rhs { - lhs.clone() - } else if *lhs == BaseExpr::Max { - rhs.clone() - } else if *rhs == BaseExpr::Max { - lhs.clone() - } else { - BaseExpr::None // zero is <= x for all (unsigned) x. - } - } - - /// Compute some BaseExpr that will be greater than or equal to - /// both inputs. - fn max(lhs: &BaseExpr, rhs: &BaseExpr) -> BaseExpr { - if lhs == rhs { - lhs.clone() - } else if *lhs == BaseExpr::None { - rhs.clone() - } else if *rhs == BaseExpr::None { - lhs.clone() - } else { - BaseExpr::Max - } - } -} - -impl Expr { - /// Constant value. - pub fn constant(offset: i64) -> Self { - Expr { - base: BaseExpr::None, - offset, - } - } - - /// The value of an SSA value. - pub fn value(value: ir::Value) -> Self { - Expr { - base: BaseExpr::Value(value), - offset: 0, - } - } - - /// The value of a global value. - pub fn global_value(gv: ir::GlobalValue) -> Self { - Expr { - base: BaseExpr::GlobalValue(gv), - offset: 0, - } - } - - /// Is one expression definitely less than or equal to another? - /// (We can't always know; in such cases, returns `false`.) - fn le(lhs: &Expr, rhs: &Expr) -> bool { - if rhs.base == BaseExpr::Max { - true - } else { - BaseExpr::le(&lhs.base, &rhs.base) && lhs.offset <= rhs.offset - } - } - - /// Generalization of `min`: compute some Expr that is less than - /// or equal to both inputs. - fn min(lhs: &Expr, rhs: &Expr) -> Expr { - if lhs.base == BaseExpr::None && lhs.offset == 0 { - lhs.clone() - } else if rhs.base == BaseExpr::None && rhs.offset == 0 { - rhs.clone() - } else { - Expr { - base: BaseExpr::min(&lhs.base, &rhs.base), - offset: core::cmp::min(lhs.offset, rhs.offset), - } - } - } - - /// Generalization of `max`: compute some Expr that is greater - /// than or equal to both inputs. - fn max(lhs: &Expr, rhs: &Expr) -> Expr { - if lhs.base == BaseExpr::None && lhs.offset == 0 { - rhs.clone() - } else if rhs.base == BaseExpr::None && rhs.offset == 0 { - lhs.clone() - } else { - Expr { - base: BaseExpr::max(&lhs.base, &rhs.base), - offset: core::cmp::max(lhs.offset, rhs.offset), - } - } - } - - /// Add one expression to another. - fn add(lhs: &Expr, rhs: &Expr) -> Option { - if lhs.base == rhs.base { - Some(Expr { - base: lhs.base.clone(), - offset: lhs.offset.checked_add(rhs.offset)?, - }) - } else if lhs.base == BaseExpr::None { - Some(Expr { - base: rhs.base.clone(), - offset: lhs.offset.checked_add(rhs.offset)?, - }) - } else if rhs.base == BaseExpr::None { - Some(Expr { - base: lhs.base.clone(), - offset: lhs.offset.checked_add(rhs.offset)?, - }) - } else { - Some(Expr { - base: BaseExpr::Max, - offset: 0, - }) - } - } - - /// Add a static offset to an expression. - pub fn offset(lhs: &Expr, rhs: i64) -> Option { - let offset = lhs.offset.checked_add(rhs)?; - Some(Expr { - base: lhs.base.clone(), - offset, - }) - } - - /// Is this Expr a BaseExpr with no offset? Return it if so. - pub fn without_offset(&self) -> Option<&BaseExpr> { - if self.offset == 0 { - Some(&self.base) - } else { - None - } - } -} - -impl fmt::Display for BaseExpr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - BaseExpr::None => Ok(()), - BaseExpr::Max => write!(f, "max"), - BaseExpr::GlobalValue(gv) => write!(f, "{gv}"), - BaseExpr::Value(value) => write!(f, "{value}"), - } - } -} - -impl BaseExpr { - /// Does this dynamic_expression take an offset? - pub fn is_some(&self) -> bool { - match self { - BaseExpr::None => false, - _ => true, - } - } -} - -impl fmt::Display for Expr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.base)?; - match self.offset { - offset if offset > 0 && self.base.is_some() => write!(f, "+{offset:#x}"), - offset if offset > 0 => write!(f, "{offset:#x}"), - offset if offset < 0 => { - let negative_offset = -i128::from(offset); // upcast to support i64::MIN. - write!(f, "-{negative_offset:#x}") - } - 0 if self.base.is_some() => Ok(()), - 0 => write!(f, "0"), - _ => unreachable!(), - } - } -} - -impl fmt::Display for Fact { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Fact::Range { - bit_width, - min, - max, - } => write!(f, "range({bit_width}, {min:#x}, {max:#x})"), - Fact::DynamicRange { - bit_width, - min, - max, - } => { - write!(f, "dynamic_range({bit_width}, {min}, {max})") - } - Fact::Mem { - ty, - min_offset, - max_offset, - nullable, - } => { - let nullable_flag = if *nullable { ", nullable" } else { "" }; - write!( - f, - "mem({ty}, {min_offset:#x}, {max_offset:#x}{nullable_flag})" - ) - } - Fact::DynamicMem { - ty, - min, - max, - nullable, - } => { - let nullable_flag = if *nullable { ", nullable" } else { "" }; - write!(f, "dynamic_mem({ty}, {min}, {max}{nullable_flag})") - } - Fact::Def { value } => write!(f, "def({value})"), - Fact::Compare { kind, lhs, rhs } => { - write!(f, "compare({kind}, {lhs}, {rhs})") - } - Fact::Conflict => write!(f, "conflict"), - } - } -} - -impl Fact { - /// Create a range fact that specifies a single known constant value. - pub fn constant(bit_width: u16, value: u64) -> Self { - debug_assert!(value <= max_value_for_width(bit_width)); - // `min` and `max` are inclusive, so this specifies a range of - // exactly one value. - Fact::Range { - bit_width, - min: value, - max: value, - } - } - - /// Create a dynamic range fact that points to the base of a dynamic memory. - pub fn dynamic_base_ptr(ty: ir::MemoryType) -> Self { - Fact::DynamicMem { - ty, - min: Expr::constant(0), - max: Expr::constant(0), - nullable: false, - } - } - - /// Create a fact that specifies the value is exactly an SSA value. - /// - /// Note that this differs from a `def` fact: it is not *defining* - /// a symbol to have the value that this fact is attached to; - /// rather it is claiming that this value is the same as whatever - /// that symbol is. (In other words, the def should be elsewhere, - /// and we are tying ourselves to it.) - pub fn value(bit_width: u16, value: ir::Value) -> Self { - Fact::DynamicRange { - bit_width, - min: Expr::value(value), - max: Expr::value(value), - } - } - - /// Create a fact that specifies the value is exactly an SSA value plus some offset. - pub fn value_offset(bit_width: u16, value: ir::Value, offset: i64) -> Self { - Fact::DynamicRange { - bit_width, - min: Expr::offset(&Expr::value(value), offset).unwrap(), - max: Expr::offset(&Expr::value(value), offset).unwrap(), - } - } - - /// Create a fact that specifies the value is exactly the value of a GV. - pub fn global_value(bit_width: u16, gv: ir::GlobalValue) -> Self { - Fact::DynamicRange { - bit_width, - min: Expr::global_value(gv), - max: Expr::global_value(gv), - } - } - - /// Create a fact that specifies the value is exactly the value of a GV plus some offset. - pub fn global_value_offset(bit_width: u16, gv: ir::GlobalValue, offset: i64) -> Self { - Fact::DynamicRange { - bit_width, - min: Expr::offset(&Expr::global_value(gv), offset).unwrap(), - max: Expr::offset(&Expr::global_value(gv), offset).unwrap(), - } - } - - /// Create a range fact that specifies the maximum range for a - /// value of the given bit-width. - pub const fn max_range_for_width(bit_width: u16) -> Self { - match bit_width { - bit_width if bit_width < 64 => Fact::Range { - bit_width, - min: 0, - max: (1u64 << bit_width) - 1, - }, - 64 => Fact::Range { - bit_width: 64, - min: 0, - max: u64::MAX, - }, - _ => panic!("bit width too large!"), - } - } - - /// Create a range fact that specifies the maximum range for a - /// value of the given bit-width, zero-extended into a wider - /// width. - pub const fn max_range_for_width_extended(from_width: u16, to_width: u16) -> Self { - debug_assert!(from_width <= to_width); - match from_width { - from_width if from_width < 64 => Fact::Range { - bit_width: to_width, - min: 0, - max: (1u64 << from_width) - 1, - }, - 64 => Fact::Range { - bit_width: to_width, - min: 0, - max: u64::MAX, - }, - _ => panic!("bit width too large!"), - } - } - - /// Try to infer a minimal fact for a value of the given IR type. - pub fn infer_from_type(ty: ir::Type) -> Option<&'static Self> { - static FACTS: [Fact; 4] = [ - Fact::max_range_for_width(8), - Fact::max_range_for_width(16), - Fact::max_range_for_width(32), - Fact::max_range_for_width(64), - ]; - match ty { - I8 => Some(&FACTS[0]), - I16 => Some(&FACTS[1]), - I32 => Some(&FACTS[2]), - I64 => Some(&FACTS[3]), - _ => None, - } - } - - /// Does this fact "propagate" automatically, i.e., cause - /// instructions that process it to infer their own output facts? - /// Not all facts propagate automatically; otherwise, verification - /// would be much slower. - pub fn propagates(&self) -> bool { - match self { - Fact::Mem { .. } => true, - _ => false, - } - } - - /// Is this a constant value of the given bitwidth? Return it as a - /// `Some(value)` if so. - pub fn as_const(&self, bits: u16) -> Option { - match self { - Fact::Range { - bit_width, - min, - max, - } if *bit_width == bits && min == max => Some(*min), - _ => None, - } - } - - /// Is this fact a single-value range with a symbolic Expr? - pub fn as_symbol(&self) -> Option<&Expr> { - match self { - Fact::DynamicRange { min, max, .. } if min == max => Some(min), - _ => None, - } - } - - /// Merge two facts. We take the *intersection*: that is, we know - /// both facts to be true, so we can intersect ranges. (This - /// differs from the usual static analysis approach, where we are - /// merging multiple possibilities into a generalized / widened - /// fact. We want to narrow here.) - pub fn intersect(a: &Fact, b: &Fact) -> Fact { - match (a, b) { - ( - Fact::Range { - bit_width: bw_lhs, - min: min_lhs, - max: max_lhs, - }, - Fact::Range { - bit_width: bw_rhs, - min: min_rhs, - max: max_rhs, - }, - ) if bw_lhs == bw_rhs && max_lhs >= min_rhs && max_rhs >= min_lhs => Fact::Range { - bit_width: *bw_lhs, - min: core::cmp::max(*min_lhs, *min_rhs), - max: core::cmp::min(*max_lhs, *max_rhs), - }, - - ( - Fact::DynamicRange { - bit_width: bw_lhs, - min: min_lhs, - max: max_lhs, - }, - Fact::DynamicRange { - bit_width: bw_rhs, - min: min_rhs, - max: max_rhs, - }, - ) if bw_lhs == bw_rhs && Expr::le(min_rhs, max_lhs) && Expr::le(min_lhs, max_rhs) => { - Fact::DynamicRange { - bit_width: *bw_lhs, - min: Expr::max(min_lhs, min_rhs), - max: Expr::min(max_lhs, max_rhs), - } - } - - ( - Fact::Mem { - ty: ty_lhs, - min_offset: min_offset_lhs, - max_offset: max_offset_lhs, - nullable: nullable_lhs, - }, - Fact::Mem { - ty: ty_rhs, - min_offset: min_offset_rhs, - max_offset: max_offset_rhs, - nullable: nullable_rhs, - }, - ) if ty_lhs == ty_rhs - && max_offset_lhs >= min_offset_rhs - && max_offset_rhs >= min_offset_lhs => - { - Fact::Mem { - ty: *ty_lhs, - min_offset: core::cmp::max(*min_offset_lhs, *min_offset_rhs), - max_offset: core::cmp::min(*max_offset_lhs, *max_offset_rhs), - nullable: *nullable_lhs && *nullable_rhs, - } - } - - ( - Fact::DynamicMem { - ty: ty_lhs, - min: min_lhs, - max: max_lhs, - nullable: null_lhs, - }, - Fact::DynamicMem { - ty: ty_rhs, - min: min_rhs, - max: max_rhs, - nullable: null_rhs, - }, - ) if ty_lhs == ty_rhs && Expr::le(min_rhs, max_lhs) && Expr::le(min_lhs, max_rhs) => { - Fact::DynamicMem { - ty: *ty_lhs, - min: Expr::max(min_lhs, min_rhs), - max: Expr::min(max_lhs, max_rhs), - nullable: *null_lhs && *null_rhs, - } - } - - _ => Fact::Conflict, - } - } -} - -macro_rules! ensure { - ( $condition:expr, $err:tt $(,)? ) => { - if !$condition { - return Err(PccError::$err); - } - }; -} - -macro_rules! bail { - ( $err:tt ) => {{ - return Err(PccError::$err); - }}; -} - -/// The two kinds of inequalities: "strict" (`<`, `>`) and "loose" -/// (`<=`, `>=`), the latter of which admit equality. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum InequalityKind { - /// Strict inequality: {less,greater}-than. - Strict, - /// Loose inequality: {less,greater}-than-or-equal. - Loose, -} - -/// A "context" in which we can evaluate and derive facts. This -/// context carries environment/global properties, such as the machine -/// pointer width. -pub struct FactContext<'a> { - function: &'a ir::Function, - pointer_width: u16, -} - -impl<'a> FactContext<'a> { - /// Create a new "fact context" in which to evaluate facts. - pub fn new(function: &'a ir::Function, pointer_width: u16) -> Self { - FactContext { - function, - pointer_width, - } - } - - /// Computes whether `lhs` "subsumes" (implies) `rhs`. - pub fn subsumes(&self, lhs: &Fact, rhs: &Fact) -> bool { - match (lhs, rhs) { - // Reflexivity. - (l, r) if l == r => true, - - ( - Fact::Range { - bit_width: bw_lhs, - min: min_lhs, - max: max_lhs, - }, - Fact::Range { - bit_width: bw_rhs, - min: min_rhs, - max: max_rhs, - }, - ) => { - // If the bitwidths we're claiming facts about are the - // same, or the left-hand-side makes a claim about a - // wider bitwidth, and if the right-hand-side range is - // larger than the left-hand-side range, than the LHS - // subsumes the RHS. - // - // In other words, we can always expand the claimed - // possible value range. - bw_lhs >= bw_rhs && max_lhs <= max_rhs && min_lhs >= min_rhs - } - - ( - Fact::DynamicRange { - bit_width: bw_lhs, - min: min_lhs, - max: max_lhs, - }, - Fact::DynamicRange { - bit_width: bw_rhs, - min: min_rhs, - max: max_rhs, - }, - ) => { - // Nearly same as above, but with dynamic-expression - // comparisons. Note that we require equal bitwidths - // here: unlike in the static case, we don't have - // fixed values for min and max, so we can't lean on - // the well-formedness requirements of the static - // ranges fitting within the bit-width max. - bw_lhs == bw_rhs && Expr::le(max_lhs, max_rhs) && Expr::le(min_rhs, min_lhs) - } - - ( - Fact::Mem { - ty: ty_lhs, - min_offset: min_offset_lhs, - max_offset: max_offset_lhs, - nullable: nullable_lhs, - }, - Fact::Mem { - ty: ty_rhs, - min_offset: min_offset_rhs, - max_offset: max_offset_rhs, - nullable: nullable_rhs, - }, - ) => { - ty_lhs == ty_rhs - && max_offset_lhs <= max_offset_rhs - && min_offset_lhs >= min_offset_rhs - && (*nullable_lhs || !*nullable_rhs) - } - - ( - Fact::DynamicMem { - ty: ty_lhs, - min: min_lhs, - max: max_lhs, - nullable: nullable_lhs, - }, - Fact::DynamicMem { - ty: ty_rhs, - min: min_rhs, - max: max_rhs, - nullable: nullable_rhs, - }, - ) => { - ty_lhs == ty_rhs - && Expr::le(max_lhs, max_rhs) - && Expr::le(min_rhs, min_lhs) - && (*nullable_lhs || !*nullable_rhs) - } - - // Constant zero subsumes nullable DynamicMem pointers. - ( - Fact::Range { - bit_width, - min: 0, - max: 0, - }, - Fact::DynamicMem { nullable: true, .. }, - ) if *bit_width == self.pointer_width => true, - - // Any fact subsumes a Def, because the Def makes no - // claims about the actual value (it ties a symbol to that - // value, but the value is fed to the symbol, not the - // other way around). - (_, Fact::Def { .. }) => true, - - _ => false, - } - } - - /// Computes whether the optional fact `lhs` subsumes (implies) - /// the optional fact `lhs`. A `None` never subsumes any fact, and - /// is always subsumed by any fact at all (or no fact). - pub fn subsumes_fact_optionals(&self, lhs: Option<&Fact>, rhs: Option<&Fact>) -> bool { - match (lhs, rhs) { - (None, None) => true, - (Some(_), None) => true, - (None, Some(_)) => false, - (Some(lhs), Some(rhs)) => self.subsumes(lhs, rhs), - } - } - - /// Computes whatever fact can be known about the sum of two - /// values with attached facts. The add is performed to the given - /// bit-width. Note that this is distinct from the machine or - /// pointer width: e.g., many 64-bit machines can still do 32-bit - /// adds that wrap at 2^32. - pub fn add(&self, lhs: &Fact, rhs: &Fact, add_width: u16) -> Option { - let result = match (lhs, rhs) { - ( - Fact::Range { - bit_width: bw_lhs, - min: min_lhs, - max: max_lhs, - }, - Fact::Range { - bit_width: bw_rhs, - min: min_rhs, - max: max_rhs, - }, - ) if bw_lhs == bw_rhs && add_width >= *bw_lhs => { - let computed_min = min_lhs.checked_add(*min_rhs)?; - let computed_max = max_lhs.checked_add(*max_rhs)?; - let computed_max = core::cmp::min(max_value_for_width(add_width), computed_max); - Some(Fact::Range { - bit_width: *bw_lhs, - min: computed_min, - max: computed_max, - }) - } - - ( - Fact::Range { - bit_width: bw_max, - min, - max, - }, - Fact::Mem { - ty, - min_offset, - max_offset, - nullable, - }, - ) - | ( - Fact::Mem { - ty, - min_offset, - max_offset, - nullable, - }, - Fact::Range { - bit_width: bw_max, - min, - max, - }, - ) if *bw_max >= self.pointer_width - && add_width >= *bw_max - && (!*nullable || *max == 0) => - { - let min_offset = min_offset.checked_add(*min)?; - let max_offset = max_offset.checked_add(*max)?; - Some(Fact::Mem { - ty: *ty, - min_offset, - max_offset, - nullable: false, - }) - } - - ( - Fact::Range { - bit_width: bw_static, - min: min_static, - max: max_static, - }, - Fact::DynamicRange { - bit_width: bw_dynamic, - min: min_dynamic, - max: max_dynamic, - }, - ) - | ( - Fact::DynamicRange { - bit_width: bw_dynamic, - min: min_dynamic, - max: max_dynamic, - }, - Fact::Range { - bit_width: bw_static, - min: min_static, - max: max_static, - }, - ) if bw_static == bw_dynamic => { - let min = Expr::offset(min_dynamic, i64::try_from(*min_static).ok()?)?; - let max = Expr::offset(max_dynamic, i64::try_from(*max_static).ok()?)?; - Some(Fact::DynamicRange { - bit_width: *bw_dynamic, - min, - max, - }) - } - - ( - Fact::DynamicMem { - ty, - min: min_mem, - max: max_mem, - nullable: false, - }, - Fact::DynamicRange { - bit_width, - min: min_range, - max: max_range, - }, - ) - | ( - Fact::DynamicRange { - bit_width, - min: min_range, - max: max_range, - }, - Fact::DynamicMem { - ty, - min: min_mem, - max: max_mem, - nullable: false, - }, - ) if *bit_width == self.pointer_width => { - let min = Expr::add(min_mem, min_range)?; - let max = Expr::add(max_mem, max_range)?; - Some(Fact::DynamicMem { - ty: *ty, - min, - max, - nullable: false, - }) - } - - ( - Fact::Mem { - ty, - min_offset, - max_offset, - nullable: false, - }, - Fact::DynamicRange { - bit_width, - min: min_range, - max: max_range, - }, - ) - | ( - Fact::DynamicRange { - bit_width, - min: min_range, - max: max_range, - }, - Fact::Mem { - ty, - min_offset, - max_offset, - nullable: false, - }, - ) if *bit_width == self.pointer_width => { - let min = Expr::offset(min_range, i64::try_from(*min_offset).ok()?)?; - let max = Expr::offset(max_range, i64::try_from(*max_offset).ok()?)?; - Some(Fact::DynamicMem { - ty: *ty, - min, - max, - nullable: false, - }) - } - - ( - Fact::Range { - bit_width: bw_static, - min: min_static, - max: max_static, - }, - Fact::DynamicMem { - ty, - min: min_dynamic, - max: max_dynamic, - nullable, - }, - ) - | ( - Fact::DynamicMem { - ty, - min: min_dynamic, - max: max_dynamic, - nullable, - }, - Fact::Range { - bit_width: bw_static, - min: min_static, - max: max_static, - }, - ) if *bw_static == self.pointer_width && (!*nullable || *max_static == 0) => { - let min = Expr::offset(min_dynamic, i64::try_from(*min_static).ok()?)?; - let max = Expr::offset(max_dynamic, i64::try_from(*max_static).ok()?)?; - Some(Fact::DynamicMem { - ty: *ty, - min, - max, - nullable: false, - }) - } - - _ => None, - }; - - trace!("add: {lhs:?} + {rhs:?} -> {result:?}"); - result - } - - /// Computes the `uextend` of a value with the given facts. - pub fn uextend(&self, fact: &Fact, from_width: u16, to_width: u16) -> Option { - if from_width == to_width { - return Some(fact.clone()); - } - - let result = match fact { - // If the claim is already for a same-or-wider value and the min - // and max are within range of the narrower value, we can - // claim the same range. - Fact::Range { - bit_width, - min, - max, - } if *bit_width >= from_width - && *min <= max_value_for_width(from_width) - && *max <= max_value_for_width(from_width) => - { - Some(Fact::Range { - bit_width: to_width, - min: *min, - max: *max, - }) - } - - // If the claim is a dynamic range for the from-width, we - // can extend to the to-width. - Fact::DynamicRange { - bit_width, - min, - max, - } if *bit_width == from_width => Some(Fact::DynamicRange { - bit_width: to_width, - min: min.clone(), - max: max.clone(), - }), - - // If the claim is a definition of a value, we can say - // that the output has a range of exactly that value. - Fact::Def { value } => Some(Fact::value(to_width, *value)), - - // Otherwise, we can at least claim that the value is - // within the range of `from_width`. - Fact::Range { .. } => Some(Fact::max_range_for_width_extended(from_width, to_width)), - - _ => None, - }; - trace!("uextend: fact {fact:?} from {from_width} to {to_width} -> {result:?}"); - result - } - - /// Computes the `sextend` of a value with the given facts. - pub fn sextend(&self, fact: &Fact, from_width: u16, to_width: u16) -> Option { - match fact { - // If we have a defined value in bits 0..bit_width, and - // the MSB w.r.t. `from_width` is *not* set, then we can - // do the same as `uextend`. - Fact::Range { - bit_width, - // We can ignore `min`: it is always <= max in - // unsigned terms, and we check max's LSB below. - min: _, - max, - } if *bit_width == from_width && (*max & (1 << (*bit_width - 1)) == 0) => { - self.uextend(fact, from_width, to_width) - } - _ => None, - } - } - - /// Computes the bit-truncation of a value with the given fact. - pub fn truncate(&self, fact: &Fact, from_width: u16, to_width: u16) -> Option { - if from_width == to_width { - return Some(fact.clone()); - } - - trace!( - "truncate: fact {:?} from {} to {}", - fact, from_width, to_width - ); - - match fact { - Fact::Range { - bit_width, - min, - max, - } if *bit_width == from_width => { - let max_val = (1u64 << to_width) - 1; - if *min <= max_val && *max <= max_val { - Some(Fact::Range { - bit_width: to_width, - min: *min, - max: *max, - }) - } else { - Some(Fact::Range { - bit_width: to_width, - min: 0, - max: max_val, - }) - } - } - _ => None, - } - } - - /// Scales a value with a fact by a known constant. - pub fn scale(&self, fact: &Fact, width: u16, factor: u32) -> Option { - let result = match fact { - x if factor == 1 => Some(x.clone()), - - Fact::Range { - bit_width, - min, - max, - } if *bit_width == width => { - let min = min.checked_mul(u64::from(factor))?; - let max = max.checked_mul(u64::from(factor))?; - if *bit_width < 64 && max > max_value_for_width(width) { - return None; - } - Some(Fact::Range { - bit_width: *bit_width, - min, - max, - }) - } - _ => None, - }; - trace!("scale: {fact:?} * {factor} at width {width} -> {result:?}"); - result - } - - /// Left-shifts a value with a fact by a known constant. - pub fn shl(&self, fact: &Fact, width: u16, amount: u16) -> Option { - if amount >= 32 { - return None; - } - let factor: u32 = 1 << amount; - self.scale(fact, width, factor) - } - - /// Offsets a value with a fact by a known amount. - pub fn offset(&self, fact: &Fact, width: u16, offset: i64) -> Option { - if offset == 0 { - return Some(fact.clone()); - } - - let compute_offset = |base: u64| -> Option { - if offset >= 0 { - base.checked_add(u64::try_from(offset).unwrap()) - } else { - base.checked_sub(u64::try_from(-offset).unwrap()) - } - }; - - let result = match fact { - Fact::Range { - bit_width, - min, - max, - } if *bit_width == width => { - let min = compute_offset(*min)?; - let max = compute_offset(*max)?; - Some(Fact::Range { - bit_width: *bit_width, - min, - max, - }) - } - Fact::DynamicRange { - bit_width, - min, - max, - } if *bit_width == width => { - let min = Expr::offset(min, offset)?; - let max = Expr::offset(max, offset)?; - Some(Fact::DynamicRange { - bit_width: *bit_width, - min, - max, - }) - } - Fact::Mem { - ty, - min_offset: mem_min_offset, - max_offset: mem_max_offset, - nullable: false, - } => { - let min_offset = compute_offset(*mem_min_offset)?; - let max_offset = compute_offset(*mem_max_offset)?; - Some(Fact::Mem { - ty: *ty, - min_offset, - max_offset, - nullable: false, - }) - } - Fact::DynamicMem { - ty, - min, - max, - nullable: false, - } => { - let min = Expr::offset(min, offset)?; - let max = Expr::offset(max, offset)?; - Some(Fact::DynamicMem { - ty: *ty, - min, - max, - nullable: false, - }) - } - _ => None, - }; - trace!("offset: {fact:?} + {offset} in width {width} -> {result:?}"); - result - } - - /// Check that accessing memory via a pointer with this fact, with - /// a memory access of the given size, is valid. - /// - /// If valid, returns the memory type and offset into that type - /// that this address accesses, if known, or `None` if the range - /// doesn't constrain the access to exactly one location. - fn check_address(&self, fact: &Fact, size: u32) -> PccResult> { - trace!("check_address: fact {:?} size {}", fact, size); - match fact { - Fact::Mem { - ty, - min_offset, - max_offset, - nullable: _, - } => { - let end_offset: u64 = max_offset - .checked_add(u64::from(size)) - .ok_or(PccError::Overflow)?; - match &self.function.memory_types[*ty] { - ir::MemoryTypeData::Struct { size, .. } - | ir::MemoryTypeData::Memory { size } => { - ensure!(end_offset <= *size, OutOfBounds) - } - ir::MemoryTypeData::DynamicMemory { .. } => bail!(OutOfBounds), - ir::MemoryTypeData::Empty => bail!(OutOfBounds), - } - let specific_ty_and_offset = if min_offset == max_offset { - Some((*ty, *min_offset)) - } else { - None - }; - Ok(specific_ty_and_offset) - } - Fact::DynamicMem { - ty, - min: _, - max: - Expr { - base: BaseExpr::GlobalValue(max_gv), - offset: max_offset, - }, - nullable: _, - } => match &self.function.memory_types[*ty] { - ir::MemoryTypeData::DynamicMemory { - gv, - size: mem_static_size, - } if gv == max_gv => { - let end_offset = max_offset - .checked_add(i64::from(size)) - .ok_or(PccError::Overflow)?; - let mem_static_size = - i64::try_from(*mem_static_size).map_err(|_| PccError::Overflow)?; - ensure!(end_offset <= mem_static_size, OutOfBounds); - Ok(None) - } - _ => bail!(OutOfBounds), - }, - _ => bail!(OutOfBounds), - } - } - - /// Get the access struct field, if any, by a pointer with the - /// given fact and an access of the given type. - pub fn struct_field<'b>( - &'b self, - fact: &Fact, - access_ty: ir::Type, - ) -> PccResult> { - let (ty, offset) = match self.check_address(fact, access_ty.bytes())? { - Some((ty, offset)) => (ty, offset), - None => return Ok(None), - }; - - if let ir::MemoryTypeData::Struct { fields, .. } = &self.function.memory_types[ty] { - let field = fields - .iter() - .find(|field| field.offset == offset) - .ok_or(PccError::InvalidFieldOffset)?; - if field.ty != access_ty { - bail!(BadFieldType); - } - Ok(Some(field)) - } else { - // Access to valid memory, but not a struct: no facts can be attached to the result. - Ok(None) - } - } - - /// Check a load, and determine what fact, if any, the result of the load might have. - pub fn load<'b>(&'b self, fact: &Fact, access_ty: ir::Type) -> PccResult> { - Ok(self - .struct_field(fact, access_ty)? - .and_then(|field| field.fact())) - } - - /// Check a store. - pub fn store( - &self, - fact: &Fact, - access_ty: ir::Type, - data_fact: Option<&Fact>, - ) -> PccResult<()> { - if let Some(field) = self.struct_field(fact, access_ty)? { - // If it's a read-only field, disallow. - if field.readonly { - bail!(WriteToReadOnlyField); - } - // Check that the fact on the stored data subsumes the field's fact. - if !self.subsumes_fact_optionals(data_fact, field.fact()) { - bail!(InvalidStoredFact); - } - } - Ok(()) - } - - /// Apply a known inequality to rewrite dynamic bounds using transitivity, if possible. - /// - /// Given that `lhs >= rhs` (if not `strict`) or `lhs > rhs` (if - /// `strict`), update `fact`. - pub fn apply_inequality( - &self, - fact: &Fact, - lhs: &Fact, - rhs: &Fact, - kind: InequalityKind, - ) -> Fact { - let result = match ( - lhs.as_symbol(), - lhs.as_const(self.pointer_width) - .and_then(|k| i64::try_from(k).ok()), - rhs.as_symbol(), - fact, - ) { - ( - Some(lhs), - None, - Some(rhs), - Fact::DynamicMem { - ty, - min, - max, - nullable, - }, - ) if rhs.base == max.base => { - let strict_offset = match kind { - InequalityKind::Strict => 1, - InequalityKind::Loose => 0, - }; - if let Some(offset) = max - .offset - .checked_add(lhs.offset) - .and_then(|x| x.checked_sub(rhs.offset)) - .and_then(|x| x.checked_sub(strict_offset)) - { - let new_max = Expr { - base: lhs.base.clone(), - offset, - }; - Fact::DynamicMem { - ty: *ty, - min: min.clone(), - max: new_max, - nullable: *nullable, - } - } else { - fact.clone() - } - } - - ( - None, - Some(lhs_const), - Some(rhs), - Fact::DynamicMem { - ty, - min: _, - max, - nullable, - }, - ) if rhs.base == max.base => { - let strict_offset = match kind { - InequalityKind::Strict => 1, - InequalityKind::Loose => 0, - }; - if let Some(offset) = max - .offset - .checked_add(lhs_const) - .and_then(|x| x.checked_sub(rhs.offset)) - .and_then(|x| x.checked_sub(strict_offset)) - { - Fact::Mem { - ty: *ty, - min_offset: 0, - max_offset: u64::try_from(offset).unwrap_or(0), - nullable: *nullable, - } - } else { - fact.clone() - } - } - - _ => fact.clone(), - }; - trace!("apply_inequality({fact:?}, {lhs:?}, {rhs:?}, {kind:?} -> {result:?}"); - result - } - - /// Compute the union of two facts, if possible. - pub fn union(&self, lhs: &Fact, rhs: &Fact) -> Option { - let result = match (lhs, rhs) { - (lhs, rhs) if lhs == rhs => Some(lhs.clone()), - - ( - Fact::DynamicMem { - ty: ty_lhs, - min: min_lhs, - max: max_lhs, - nullable: nullable_lhs, - }, - Fact::DynamicMem { - ty: ty_rhs, - min: min_rhs, - max: max_rhs, - nullable: nullable_rhs, - }, - ) if ty_lhs == ty_rhs => Some(Fact::DynamicMem { - ty: *ty_lhs, - min: Expr::min(min_lhs, min_rhs), - max: Expr::max(max_lhs, max_rhs), - nullable: *nullable_lhs || *nullable_rhs, - }), - - ( - Fact::Range { - bit_width: bw_const, - min: 0, - max: 0, - }, - Fact::DynamicMem { - ty, - min, - max, - nullable: _, - }, - ) - | ( - Fact::DynamicMem { - ty, - min, - max, - nullable: _, - }, - Fact::Range { - bit_width: bw_const, - min: 0, - max: 0, - }, - ) if *bw_const == self.pointer_width => Some(Fact::DynamicMem { - ty: *ty, - min: min.clone(), - max: max.clone(), - nullable: true, - }), - - ( - Fact::Range { - bit_width: bw_const, - min: 0, - max: 0, - }, - Fact::Mem { - ty, - min_offset, - max_offset, - nullable: _, - }, - ) - | ( - Fact::Mem { - ty, - min_offset, - max_offset, - nullable: _, - }, - Fact::Range { - bit_width: bw_const, - min: 0, - max: 0, - }, - ) if *bw_const == self.pointer_width => Some(Fact::Mem { - ty: *ty, - min_offset: *min_offset, - max_offset: *max_offset, - nullable: true, - }), - - _ => None, - }; - trace!("union({lhs:?}, {rhs:?}) -> {result:?}"); - result - } -} - -fn max_value_for_width(bits: u16) -> u64 { - assert!(bits <= 64); - if bits == 64 { - u64::MAX - } else { - (1u64 << bits) - 1 - } -} - -/// Top-level entry point after compilation: this checks the facts in -/// VCode. -pub fn check_vcode_facts( - f: &ir::Function, - vcode: &mut VCode, - backend: &B, -) -> PccResult<()> { - let ctx = FactContext::new(f, backend.triple().pointer_width().unwrap().bits().into()); - - // Check that individual instructions are valid according to input - // facts, and support the stated output facts. - for block in 0..vcode.num_blocks() { - let block = BlockIndex::new(block); - let mut flow_state = B::FactFlowState::default(); - for inst in vcode.block_insns(block).iter() { - // Check any output facts on this inst. - if let Err(e) = backend.check_fact(&ctx, vcode, inst, &mut flow_state) { - log::info!("Error checking instruction: {:?}", vcode[inst]); - return Err(e); - } - - // If this is a branch, check that all block arguments subsume - // the assumed facts on the blockparams of successors. - if vcode.is_branch(inst) { - for (succ_idx, succ) in vcode.block_succs(block).iter().enumerate() { - for (arg, param) in vcode - .branch_blockparams(block, inst, succ_idx) - .iter() - .zip(vcode.block_params(*succ).iter()) - { - let arg_fact = vcode.vreg_fact(*arg); - let param_fact = vcode.vreg_fact(*param); - if !ctx.subsumes_fact_optionals(arg_fact, param_fact) { - return Err(PccError::UnsupportedBlockparam); - } - } - } - } - } - } - Ok(()) -} diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs index 6bab37ac03df..cd312d43e714 100644 --- a/cranelift/codegen/src/isa/aarch64/abi.rs +++ b/cranelift/codegen/src/isa/aarch64/abi.rs @@ -38,7 +38,10 @@ impl From for AMode { // Returns the size of stack space needed to store the // `clobbered_callee_saved` registers. -fn compute_clobber_size(clobbered_callee_saves: &[Writable]) -> u32 { +fn compute_clobber_size( + call_conv: isa::CallConv, + clobbered_callee_saves: &[Writable], +) -> u32 { let mut int_regs = 0; let mut vec_regs = 0; for ® in clobbered_callee_saves { @@ -55,16 +58,22 @@ fn compute_clobber_size(clobbered_callee_saves: &[Writable]) -> u32 { // Round up to multiple of 2, to keep 16-byte stack alignment. let int_save_bytes = (int_regs + (int_regs & 1)) * 8; - // The Procedure Call Standard for the Arm 64-bit Architecture - // (AAPCS64, including several related ABIs such as the one used by - // Windows) mandates saving only the bottom 8 bytes of the vector - // registers, so we round up the number of registers to ensure - // proper stack alignment (similarly to the situation with - // `int_reg`). - let vec_reg_size = 8; - let vec_save_padding = vec_regs & 1; - // FIXME: SVE: ABI is different to Neon, so do we treat all vec regs as Z-regs? - let vec_save_bytes = (vec_regs + vec_save_padding) * vec_reg_size; + let vec_save_bytes = if call_conv == isa::CallConv::PreserveAll { + // In the PreserveAll ABI, we save the entire vector register, + // i.e., all 128 bits. + vec_regs * 16 + } else { + // The Procedure Call Standard for the Arm 64-bit Architecture + // (AAPCS64, including several related ABIs such as the one used by + // Windows) mandates saving only the bottom 8 bytes of the vector + // registers, so we round up the number of registers to ensure + // proper stack alignment (similarly to the situation with + // `int_reg`). + let vec_reg_size = 8; + let vec_save_padding = vec_regs & 1; + // FIXME: SVE: ABI is different to Neon, so do we treat all vec regs as Z-regs? + (vec_regs + vec_save_padding) * vec_reg_size + }; int_save_bytes + vec_save_bytes } @@ -714,7 +723,7 @@ impl ABIMachineSpec for AArch64MachineDeps { } fn gen_clobber_save( - _call_conv: isa::CallConv, + call_conv: isa::CallConv, flags: &settings::Flags, frame_layout: &FrameLayout, ) -> SmallVec<[Inst; 16]> { @@ -857,74 +866,91 @@ impl ABIMachineSpec for AArch64MachineDeps { } } - let store_vec_reg = |rd| Inst::FpuStore64 { - rd, - mem: AMode::SPPreIndexed { - simm9: SImm9::maybe_from_i64(-clobber_offset_change).unwrap(), - }, - flags: MemFlags::trusted(), - }; - let iter = clobbered_vec.chunks_exact(2); + if call_conv == isa::CallConv::PreserveAll { + // Store full vector registers in PreserveAll convention. + for reg in clobbered_vec.iter().rev() { + let inst = Inst::FpuStore128 { + rd: reg.to_reg().into(), + mem: AMode::SPPreIndexed { + simm9: SImm9::maybe_from_i64(-clobber_offset_change).unwrap(), + }, + flags: MemFlags::trusted(), + }; + insts.push(inst); + // N.B.: no unwind info: we don't have a way to + // represent "full register" anyway. + } + } else { + let store_vec_reg_half = |rd| Inst::FpuStore64 { + rd, + mem: AMode::SPPreIndexed { + simm9: SImm9::maybe_from_i64(-clobber_offset_change).unwrap(), + }, + flags: MemFlags::trusted(), + }; + let iter = clobbered_vec.chunks_exact(2); - if let [rd] = iter.remainder() { - let rd: Reg = rd.to_reg().into(); + if let [rd] = iter.remainder() { + let rd: Reg = rd.to_reg().into(); - debug_assert_eq!(rd.class(), RegClass::Float); - insts.push(store_vec_reg(rd)); + debug_assert_eq!(rd.class(), RegClass::Float); + insts.push(store_vec_reg_half(rd)); - if flags.unwind_info() { - clobber_offset -= clobber_offset_change as u32; - insts.push(Inst::Unwind { - inst: UnwindInst::SaveReg { - clobber_offset, - reg: rd.to_real_reg().unwrap(), - }, - }); + if flags.unwind_info() { + clobber_offset -= clobber_offset_change as u32; + insts.push(Inst::Unwind { + inst: UnwindInst::SaveReg { + clobber_offset, + reg: rd.to_real_reg().unwrap(), + }, + }); + } } - } - let store_vec_reg_pair = |rt, rt2| { - let clobber_offset_change = 16; + let store_vec_reg_half_pair = |rt, rt2| { + let clobber_offset_change = 16; - ( - Inst::FpuStoreP64 { - rt, - rt2, - mem: PairAMode::SPPreIndexed { - simm7: SImm7Scaled::maybe_from_i64(-clobber_offset_change, F64).unwrap(), + ( + Inst::FpuStoreP64 { + rt, + rt2, + mem: PairAMode::SPPreIndexed { + simm7: SImm7Scaled::maybe_from_i64(-clobber_offset_change, F64) + .unwrap(), + }, + flags: MemFlags::trusted(), }, - flags: MemFlags::trusted(), - }, - clobber_offset_change as u32, - ) - }; - let mut iter = iter.rev(); + clobber_offset_change as u32, + ) + }; + let mut iter = iter.rev(); - while let Some([rt, rt2]) = iter.next() { - let rt: Reg = rt.to_reg().into(); - let rt2: Reg = rt2.to_reg().into(); + while let Some([rt, rt2]) = iter.next() { + let rt: Reg = rt.to_reg().into(); + let rt2: Reg = rt2.to_reg().into(); - debug_assert_eq!(rt.class(), RegClass::Float); - debug_assert_eq!(rt2.class(), RegClass::Float); + debug_assert_eq!(rt.class(), RegClass::Float); + debug_assert_eq!(rt2.class(), RegClass::Float); - let (inst, clobber_offset_change) = store_vec_reg_pair(rt, rt2); + let (inst, clobber_offset_change) = store_vec_reg_half_pair(rt, rt2); - insts.push(inst); + insts.push(inst); - if flags.unwind_info() { - clobber_offset -= clobber_offset_change; - insts.push(Inst::Unwind { - inst: UnwindInst::SaveReg { - clobber_offset, - reg: rt.to_real_reg().unwrap(), - }, - }); - insts.push(Inst::Unwind { - inst: UnwindInst::SaveReg { - clobber_offset: clobber_offset + clobber_offset_change / 2, - reg: rt2.to_real_reg().unwrap(), - }, - }); + if flags.unwind_info() { + clobber_offset -= clobber_offset_change; + insts.push(Inst::Unwind { + inst: UnwindInst::SaveReg { + clobber_offset, + reg: rt.to_real_reg().unwrap(), + }, + }); + insts.push(Inst::Unwind { + inst: UnwindInst::SaveReg { + clobber_offset: clobber_offset + clobber_offset_change / 2, + reg: rt2.to_real_reg().unwrap(), + }, + }); + } } } @@ -943,7 +969,7 @@ impl ABIMachineSpec for AArch64MachineDeps { } fn gen_clobber_restore( - _call_conv: isa::CallConv, + call_conv: isa::CallConv, _flags: &settings::Flags, frame_layout: &FrameLayout, ) -> SmallVec<[Inst; 16]> { @@ -956,40 +982,55 @@ impl ABIMachineSpec for AArch64MachineDeps { insts.extend(Self::gen_sp_reg_adjust(stack_size as i32)); } - let load_vec_reg = |rd| Inst::FpuLoad64 { - rd, - mem: AMode::SPPostIndexed { - simm9: SImm9::maybe_from_i64(16).unwrap(), - }, - flags: MemFlags::trusted(), - }; - let load_vec_reg_pair = |rt, rt2| Inst::FpuLoadP64 { - rt, - rt2, - mem: PairAMode::SPPostIndexed { - simm7: SImm7Scaled::maybe_from_i64(16, F64).unwrap(), - }, - flags: MemFlags::trusted(), - }; + if call_conv == isa::CallConv::PreserveAll { + for reg in clobbered_vec.iter() { + let inst = Inst::FpuLoad128 { + rd: reg.map(|r| r.into()), + mem: AMode::SPPostIndexed { + simm9: SImm9::maybe_from_i64(16).unwrap(), + }, + flags: MemFlags::trusted(), + }; + insts.push(inst); + // N.B.: no unwind info; we don't have a way to + // represent "full vector register saved" anyway. + } + } else { + let load_vec_reg_half = |rd| Inst::FpuLoad64 { + rd, + mem: AMode::SPPostIndexed { + simm9: SImm9::maybe_from_i64(16).unwrap(), + }, + flags: MemFlags::trusted(), + }; + let load_vec_reg_half_pair = |rt, rt2| Inst::FpuLoadP64 { + rt, + rt2, + mem: PairAMode::SPPostIndexed { + simm7: SImm7Scaled::maybe_from_i64(16, F64).unwrap(), + }, + flags: MemFlags::trusted(), + }; - let mut iter = clobbered_vec.chunks_exact(2); + let mut iter = clobbered_vec.chunks_exact(2); - while let Some([rt, rt2]) = iter.next() { - let rt: Writable = rt.map(|r| r.into()); - let rt2: Writable = rt2.map(|r| r.into()); + while let Some([rt, rt2]) = iter.next() { + let rt: Writable = rt.map(|r| r.into()); + let rt2: Writable = rt2.map(|r| r.into()); - debug_assert_eq!(rt.to_reg().class(), RegClass::Float); - debug_assert_eq!(rt2.to_reg().class(), RegClass::Float); - insts.push(load_vec_reg_pair(rt, rt2)); - } + debug_assert_eq!(rt.to_reg().class(), RegClass::Float); + debug_assert_eq!(rt2.to_reg().class(), RegClass::Float); + insts.push(load_vec_reg_half_pair(rt, rt2)); + } - debug_assert!(iter.remainder().len() <= 1); + debug_assert!(iter.remainder().len() <= 1); - if let [rd] = iter.remainder() { - let rd: Writable = rd.map(|r| r.into()); + if let [rd] = iter.remainder() { + let rd: Writable = rd.map(|r| r.into()); - debug_assert_eq!(rd.to_reg().class(), RegClass::Float); - insts.push(load_vec_reg(rd)); + debug_assert_eq!(rd.to_reg().class(), RegClass::Float); + insts.push(load_vec_reg_half(rd)); + } } let mut iter = clobbered_int.chunks_exact(2); @@ -1150,7 +1191,7 @@ impl ABIMachineSpec for AArch64MachineDeps { regs.sort_unstable(); // Compute clobber size. - let clobber_size = compute_clobber_size(®s); + let clobber_size = compute_clobber_size(call_conv, ®s); // Compute linkage frame size. let setup_area_size = if flags.preserve_frame_pointers() diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle index 88b6d5a1581f..06131fd08c59 100644 --- a/cranelift/codegen/src/isa/aarch64/inst.isle +++ b/cranelift/codegen/src/isa/aarch64/inst.isle @@ -1822,6 +1822,9 @@ (decl pure use_fp16 () bool) (extern constructor use_fp16 use_fp16) +(decl pure use_csdb () bool) +(extern constructor use_csdb use_csdb) + ;; Extractor helpers for various immediate constants ;;;;;;;;;;;;;;;;;;;;;;;;;; (decl pure partial move_wide_const_from_u64 (Type u64) MoveWideConst) @@ -1956,7 +1959,7 @@ (decl imm12_from_value (Imm12) Value) (extractor (imm12_from_value n) - (iconst (u64_from_imm64 (imm12_from_u64 n)))) + (iconst _ (u64_from_imm64 (imm12_from_u64 n)))) ;; Conceptually the same as `imm12_from_value`, but tries negating the constant ;; value (first sign-extending to handle narrow widths). (spec (imm12_from_negated_value arg) @@ -1980,7 +1983,7 @@ ) (decl pure partial imm12_from_negated_value (Value) Imm12) (rule imm12_from_negated_value - (imm12_from_negated_value (has_type ty (iconst n))) + (imm12_from_negated_value (has_type ty (iconst _ n))) (if-let (imm12_from_u64 imm) (i64_cast_unsigned (i64_checked_neg (i64_sextend_imm64 ty n)))) imm) @@ -3618,23 +3621,17 @@ ;; are zero-extending the value. (rule 3 (imm (integral_ty ty) (ImmExtend.Zero) k) (if-let n (move_wide_const_from_u64 ty k)) - (add_range_fact - (movz n (operand_size ty)) - 64 k k)) + (movz n (operand_size ty))) (rule 2 (imm (integral_ty (ty_32_or_64 ty)) (ImmExtend.Zero) k) (if-let n (move_wide_const_from_inverted_u64 ty k)) - (add_range_fact - (movn n (operand_size ty)) - 64 k k)) + (movn n (operand_size ty))) ;; Weird logical-instruction immediate in ORI using zero register; to simplify, ;; we only match when we are zero-extending the value. (rule 1 (imm (integral_ty ty) (ImmExtend.Zero) k) (if-let n (imm_logic_from_u64 ty k)) (if-let m (imm_size_from_type ty)) - (add_range_fact - (orr_imm ty (zero_reg) n) - m k k)) + (orr_imm ty (zero_reg) n)) (decl load_constant_full (Type ImmExtend OperandSize u64) Reg) (extern constructor load_constant_full load_constant_full) @@ -3755,7 +3752,7 @@ (MInst.TrapIf (cond_br_cond (Cond.Hs)) tc)))) (decl sink_atomic_load (Inst) Reg) -(rule (sink_atomic_load x @ (atomic_load _ addr)) +(rule (sink_atomic_load x @ (atomic_load _ _ addr)) (let ((_ Unit (sink_inst x))) (put_in_reg addr))) @@ -3777,18 +3774,18 @@ (alu_rrr op ty x y)) ;; Special cases for when one operand is a constant. -(rule (alu_rs_imm_logic_commutative op ty x (iconst k)) +(rule (alu_rs_imm_logic_commutative op ty x (iconst _ k)) (if-let imm (imm_logic_from_imm64 ty k)) (alu_rr_imm_logic op ty x imm)) -(rule 1 (alu_rs_imm_logic_commutative op ty (iconst k) x) +(rule 1 (alu_rs_imm_logic_commutative op ty (iconst _ k) x) (if-let imm (imm_logic_from_imm64 ty k)) (alu_rr_imm_logic op ty x imm)) ;; Special cases for when one operand is shifted left by a constant. -(rule (alu_rs_imm_logic_commutative op ty x (ishl y (iconst k))) +(rule (alu_rs_imm_logic_commutative op ty x (ishl _ y (iconst _ k))) (if-let amt (lshl_from_imm64 ty k)) (alu_rrr_shift op ty x y amt)) -(rule 1 (alu_rs_imm_logic_commutative op ty (ishl x (iconst k)) y) +(rule 1 (alu_rs_imm_logic_commutative op ty (ishl _ x (iconst _ k)) y) (if-let amt (lshl_from_imm64 ty k)) (alu_rrr_shift op ty y x amt)) @@ -3805,10 +3802,10 @@ (decl alu_rs_imm_logic (ALUOp Type Value Value) Reg) (rule -1 (alu_rs_imm_logic op ty x y) (alu_rrr op ty x y)) -(rule (alu_rs_imm_logic op ty x (iconst k)) +(rule (alu_rs_imm_logic op ty x (iconst _ k)) (if-let imm (imm_logic_from_imm64 ty k)) (alu_rr_imm_logic op ty x imm)) -(rule (alu_rs_imm_logic op ty x (ishl y (iconst k))) +(rule (alu_rs_imm_logic op ty x (ishl _ y (iconst _ k))) (if-let amt (lshl_from_imm64 ty k)) (alu_rrr_shift op ty x y amt)) @@ -3905,15 +3902,15 @@ (rule 0 (amode ty val offset) (amode_no_more_iconst ty val offset)) -(rule 1 (amode ty (iadd x (i32_from_iconst y)) offset) +(rule 1 (amode ty (iadd _ x (i32_from_iconst y)) offset) (if-let new_offset (i32_checked_add y offset)) (amode_no_more_iconst ty x new_offset)) -(rule 2 (amode ty (iadd (i32_from_iconst x) y) offset) +(rule 2 (amode ty (iadd _ (i32_from_iconst x) y) offset) (if-let new_offset (i32_checked_add x offset)) (amode_no_more_iconst ty y new_offset)) (rule 3 - (amode ty (stack_addr slot offset1) offset2) + (amode ty (stack_addr _ slot offset1) offset2) (AMode.SlotOffset (abi_stackslot_offset_into_slot_region slot offset1 offset2))) @@ -3939,15 +3936,15 @@ ;; instructions. Constants on the other hand added to the amode represent only ;; a single instruction folded in, so fewer instructions should be generated ;; with these higher priority than the rules above. -(rule 3 (amode_no_more_iconst ty (iadd x y) offset) +(rule 3 (amode_no_more_iconst ty (iadd _ x y) offset) (AMode.RegReg (amode_add x offset) y)) -(rule 4 (amode_no_more_iconst ty (iadd x (uextend y @ (value_type $I32))) offset) +(rule 4 (amode_no_more_iconst ty (iadd _ x (uextend _ y @ (value_type $I32))) offset) (AMode.RegExtended (amode_add x offset) y (ExtendOp.UXTW))) -(rule 4 (amode_no_more_iconst ty (iadd x (sextend y @ (value_type $I32))) offset) +(rule 4 (amode_no_more_iconst ty (iadd _ x (sextend _ y @ (value_type $I32))) offset) (AMode.RegExtended (amode_add x offset) y (ExtendOp.SXTW))) -(rule 5 (amode_no_more_iconst ty (iadd (uextend x @ (value_type $I32)) y) offset) +(rule 5 (amode_no_more_iconst ty (iadd _ (uextend _ x @ (value_type $I32)) y) offset) (AMode.RegExtended (amode_add y offset) x (ExtendOp.UXTW))) -(rule 5 (amode_no_more_iconst ty (iadd (sextend x @ (value_type $I32)) y) offset) +(rule 5 (amode_no_more_iconst ty (iadd _ (sextend _ x @ (value_type $I32)) y) offset) (AMode.RegExtended (amode_add y offset) x (ExtendOp.SXTW))) ;; `RegScaled*` rules where this matches an addition of an "index register" to a @@ -3957,19 +3954,19 @@ ;; Note that this can additionally bundle an extending operation but the ;; extension must happen before the shift. This will pattern-match the shift ;; first and then if that succeeds afterwards try to find an extend. -(rule 6 (amode_no_more_iconst ty (iadd x (ishl y (iconst (u64_from_imm64 n)))) offset) - (if-let true (u64_eq (ty_bytes ty) (u64_wrapping_shl 1 (shift_masked_imm ty n)))) +(rule 6 (amode_no_more_iconst ty (iadd _ x (ishl shift_ty y (iconst _ (u64_from_imm64 n)))) offset) + (if-let true (u64_eq (ty_bytes ty) (u64_wrapping_shl 1 (shift_masked_imm shift_ty n)))) (amode_reg_scaled (amode_add x offset) y)) -(rule 7 (amode_no_more_iconst ty (iadd (ishl y (iconst (u64_from_imm64 n))) x) offset) - (if-let true (u64_eq (ty_bytes ty) (u64_wrapping_shl 1 (shift_masked_imm ty n)))) +(rule 7 (amode_no_more_iconst ty (iadd _ (ishl shift_ty y (iconst _ (u64_from_imm64 n))) x) offset) + (if-let true (u64_eq (ty_bytes ty) (u64_wrapping_shl 1 (shift_masked_imm shift_ty n)))) (amode_reg_scaled (amode_add x offset) y)) (decl amode_reg_scaled (Reg Value) AMode) (rule 0 (amode_reg_scaled base index) (AMode.RegScaled base index)) -(rule 1 (amode_reg_scaled base (uextend index @ (value_type $I32))) +(rule 1 (amode_reg_scaled base (uextend _ index @ (value_type $I32))) (AMode.RegScaledExtended base index (ExtendOp.UXTW))) -(rule 1 (amode_reg_scaled base (sextend index @ (value_type $I32))) +(rule 1 (amode_reg_scaled base (sextend _ index @ (value_type $I32))) (AMode.RegScaledExtended base index (ExtendOp.SXTW))) ;; Helper to add a 32-bit signed immediate to the register provided. This will @@ -4007,7 +4004,7 @@ (decl sink_load_into_addr (Type Inst) Reg) -(rule (sink_load_into_addr ty x @ (load _ addr (offset32 offset))) +(rule (sink_load_into_addr ty x @ (load _ _ addr (offset32 offset))) (let ((_ Unit (sink_inst x))) (add_imm_to_addr addr (i64_cast_unsigned offset)))) diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index 06e0eb017708..51776985ac02 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -3,6 +3,7 @@ use cranelift_control::ControlPlane; use crate::ir::{self, types::*}; +use crate::isa::aarch64; use crate::isa::aarch64::inst::*; use crate::trace; @@ -711,12 +712,15 @@ impl EmitState { } /// Constant state used during function compilation. -pub struct EmitInfo(settings::Flags); +pub struct EmitInfo { + flags: settings::Flags, + isa_flags: aarch64::settings::Flags, +} impl EmitInfo { /// Create a constant state for emission of instructions. - pub fn new(flags: settings::Flags) -> Self { - Self(flags) + pub fn new(flags: settings::Flags, isa_flags: aarch64::settings::Flags) -> Self { + Self { flags, isa_flags } } } @@ -3186,8 +3190,13 @@ impl MachInstEmit for Inst { rm: ridx, }; inst.emit(sink, emit_info, state); - // Prevent any data value speculation. - Inst::Csdb.emit(sink, emit_info, state); + // Prevent any data value speculation if spectre mitigations are + // enabled. + if emit_info.flags.enable_table_access_spectre_mitigation() + && emit_info.isa_flags.use_csdb() + { + Inst::Csdb.emit(sink, emit_info, state); + } // Load address of jump table let inst = Inst::Adr { rd: rtmp1, off: 16 }; @@ -3557,7 +3566,7 @@ impl MachInstEmit for Inst { } &Inst::StackProbeLoop { start, end, step } => { - assert!(emit_info.0.enable_probestack()); + assert!(emit_info.flags.enable_probestack()); // The loop generated here uses `start` as a counter register to // count backwards until negating it exceeds `end`. In other @@ -3644,9 +3653,11 @@ fn emit_return_call_common_sequence( state: &mut EmitState, info: &ReturnCallInfo, ) { - for inst in - AArch64MachineDeps::gen_clobber_restore(CallConv::Tail, &emit_info.0, state.frame_layout()) - { + for inst in AArch64MachineDeps::gen_clobber_restore( + CallConv::Tail, + &emit_info.flags, + state.frame_layout(), + ) { inst.emit(sink, emit_info, state); } diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs index d8e6cd6a2083..f64f603606a1 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs @@ -1,5 +1,6 @@ use crate::ir::types::*; use crate::ir::{ExternalName, TrapCode}; +use crate::isa::aarch64; use crate::isa::aarch64::inst::*; use alloc::boxed::Box; @@ -7929,7 +7930,8 @@ fn test_aarch64_binemit() { insns.push((Inst::Fence {}, "BF3B03D5", "dmb ish")); let flags = settings::Flags::new(settings::builder()); - let emit_info = EmitInfo::new(flags); + let isa_flags = aarch64::settings::Flags::new(&flags, &aarch64::settings::builder()); + let emit_info = EmitInfo::new(flags, isa_flags); for (insn, expected_encoding, expected_printing) in insns { println!("AArch64: {insn:?}, {expected_encoding}, {expected_printing}"); diff --git a/cranelift/codegen/src/isa/aarch64/inst/imms.rs b/cranelift/codegen/src/isa/aarch64/inst/imms.rs index fcd67deed576..066c7621675a 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/imms.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/imms.rs @@ -602,7 +602,7 @@ impl MoveWideConst { None } - /// Create a `MoveWideCosnt` from a given shift, if possible. + /// Create a `MoveWideConst` from a given shift, if possible. pub fn maybe_with_shift(imm: u16, shift: u8) -> Option { let shift_enc = shift / 16; if shift_enc > 3 { diff --git a/cranelift/codegen/src/isa/aarch64/lower.isle b/cranelift/codegen/src/isa/aarch64/lower.isle index dbd8b27fa098..13c35b4bbcf7 100644 --- a/cranelift/codegen/src/isa/aarch64/lower.isle +++ b/cranelift/codegen/src/isa/aarch64/lower.isle @@ -18,27 +18,27 @@ ;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule iconst (lower (has_type ty (iconst (u64_from_imm64 n)))) +(rule iconst (lower (has_type ty (iconst _ (u64_from_imm64 n)))) (imm ty (ImmExtend.Zero) n)) ;;;; Rules for `f16const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f16const (u16_from_ieee16 n))) +(rule (lower (f16const _ (u16_from_ieee16 n))) (constant_f16 n)) ;;;; Rules for `f32const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f32const (u32_from_ieee32 n))) +(rule (lower (f32const _ (u32_from_ieee32 n))) (constant_f32 n)) ;;;; Rules for `f64const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f64const (u64_from_ieee64 n))) +(rule (lower (f64const _ (u64_from_ieee64 n))) (constant_f64 n)) ;;;; Rules for `f128const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F128 (f128const (u128_from_constant n)))) +(rule (lower (has_type $F128 (f128const _ (u128_from_constant n)))) (constant_f128 n)) ;;;; Rules for `nop` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -51,64 +51,64 @@ ;; `i64` and smaller ;; Base case, simply adding things in registers. -(rule iadd_base_case -1 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd x y))) +(rule iadd_base_case -1 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x y))) (add ty x y)) ;; Special cases for when one operand is an immediate that fits in 12 bits. -(rule iadd_imm12_right 4 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd x (imm12_from_value y)))) +(rule iadd_imm12_right 4 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x (imm12_from_value y)))) (add_imm ty x y)) -(rule iadd_imm12_left 5 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd (imm12_from_value x) y))) +(rule iadd_imm12_left 5 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ (imm12_from_value x) y))) (add_imm ty y x)) ;; Same as the previous special cases, except we can switch the addition to a ;; subtraction if the negated immediate fits in 12 bits. -(rule iadd_imm12_neg_right 2 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd x y))) +(rule iadd_imm12_neg_right 2 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x y))) (if-let imm12_neg (imm12_from_negated_value y)) (sub_imm ty x imm12_neg)) -(rule iadd_imm12_neg_left 3 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd x y))) +(rule iadd_imm12_neg_left 3 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x y))) (if-let imm12_neg (imm12_from_negated_value x)) (sub_imm ty y imm12_neg)) ;; Special cases for when we're adding an extended register where the extending ;; operation can get folded into the add itself. -(rule iadd_extend_right 0 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd x (extended_value_from_value y)))) +(rule iadd_extend_right 0 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x (extended_value_from_value y)))) (add_extend ty x y)) -(rule iadd_extend_left 1 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd (extended_value_from_value x) y))) +(rule iadd_extend_left 1 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ (extended_value_from_value x) y))) (add_extend ty y x)) ;; Special cases for when we're adding the shift of a different ;; register by a constant amount and the shift can get folded into the add. (rule iadd_ishl_right 7 (lower (has_type (ty_int_ref_scalar_64 ty) - (iadd x (ishl y (iconst k))))) + (iadd _ x (ishl _ y (iconst _ k))))) (if-let amt (lshl_from_imm64 ty k)) (add_shift ty x y amt)) (rule iadd_ishl_left 6 (lower (has_type (ty_int_ref_scalar_64 ty) - (iadd (ishl x (iconst k)) y))) + (iadd _ (ishl _ x (iconst _ k)) y))) (if-let amt (lshl_from_imm64 ty k)) (add_shift ty y x amt)) ;; Fold an `iadd` and `imul` combination into a `madd` instruction. -(rule iadd_imul_right 7 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd x (imul y z)))) +(rule iadd_imul_right 7 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x (imul _ y z)))) (madd ty y z x)) -(rule iadd_imul_left 6 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd (imul x y) z))) +(rule iadd_imul_left 6 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ (imul _ x y) z))) (madd ty x y z)) ;; Fold an `isub` and `imul` combination into a `msub` instruction. -(rule isub_imul (lower (has_type (ty_int_ref_scalar_64 ty) (isub x (imul y z)))) +(rule isub_imul (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x (imul _ y z)))) (msub ty y z x)) ;; vectors -(rule -2 (lower (has_type ty @ (multi_lane _ _) (iadd x y))) +(rule -2 (lower (has_type ty @ (multi_lane _ _) (iadd _ x y))) (add_vec x y (vector_size ty))) ;; `i128` -(rule -3 (lower (has_type $I128 (iadd x y))) +(rule -3 (lower (has_type $I128 (iadd _ x y))) (let ;; Get the high/low registers for `x`. ((x_regs ValueRegs x) @@ -131,13 +131,13 @@ ;; lanes then the `dup` instruction can be used for this operation. Note that ;; for now this only matches lane selection from the first vector `a`, but ;; if necessary in the future rules can be added to select from `b` as well. -(rule 6 (lower (shuffle a b (shuffle_dup8_from_imm n))) +(rule 6 (lower (shuffle _ a b (shuffle_dup8_from_imm n))) (vec_dup_from_fpu a (VectorSize.Size8x16) n)) -(rule 5 (lower (shuffle a b (shuffle_dup16_from_imm n))) +(rule 5 (lower (shuffle _ a b (shuffle_dup16_from_imm n))) (vec_dup_from_fpu a (VectorSize.Size16x8) n)) -(rule 4 (lower (shuffle a b (shuffle_dup32_from_imm n))) +(rule 4 (lower (shuffle _ a b (shuffle_dup32_from_imm n))) (vec_dup_from_fpu a (VectorSize.Size32x4) n)) -(rule 3 (lower (shuffle a b (shuffle_dup64_from_imm n))) +(rule 3 (lower (shuffle _ a b (shuffle_dup64_from_imm n))) (vec_dup_from_fpu a (VectorSize.Size64x2) n)) ;; If the `Immediate` specified to the extractor looks like a duplication of the @@ -156,7 +156,7 @@ ;; When the shuffle looks like "concatenate `a` and `b` and shift right by n*8 ;; bytes", that's an `ext` instruction. -(rule 2 (lower (shuffle a b (vec_extract_imm4_from_immediate n))) +(rule 2 (lower (shuffle _ a b (vec_extract_imm4_from_immediate n))) (vec_extract a b n)) ;; Attempts to extract `n` from the specified shuffle `Immediate` where each @@ -169,36 +169,36 @@ ;; Rules for the `uzp1` and `uzp2` instructions which gather even-numbered lanes ;; or odd-numbered lanes -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1e1c_1a18_1614_1210_0e0c_0a08_0604_0200))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1e1c_1a18_1614_1210_0e0c_0a08_0604_0200))) (vec_uzp1 a b (VectorSize.Size8x16))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1d_1b19_1715_1311_0f0d_0b09_0705_0301))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f1d_1b19_1715_1311_0f0d_0b09_0705_0301))) (vec_uzp2 a b (VectorSize.Size8x16))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1d1c_1918_1514_1110_0d0c_0908_0504_0100))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1d1c_1918_1514_1110_0d0c_0908_0504_0100))) (vec_uzp1 a b (VectorSize.Size16x8))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e_1b1a_1716_1312_0f0e_0b0a_0706_0302))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f1e_1b1a_1716_1312_0f0e_0b0a_0706_0302))) (vec_uzp2 a b (VectorSize.Size16x8))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1b1a1918_13121110_0b0a0908_03020100))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1b1a1918_13121110_0b0a0908_03020100))) (vec_uzp1 a b (VectorSize.Size32x4))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_17161514_0f0e0d0c_07060504))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f1e1d1c_17161514_0f0e0d0c_07060504))) (vec_uzp2 a b (VectorSize.Size32x4))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1716151413121110_0706050403020100))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1716151413121110_0706050403020100))) (vec_uzp1 a b (VectorSize.Size64x2))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c1b1a1918_0f0e0d0c0b0a0908))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f1e1d1c1b1a1918_0f0e0d0c0b0a0908))) (vec_uzp2 a b (VectorSize.Size64x2))) ;; Rules for the `zip1` and `zip2` instructions which interleave lanes in the ;; low or high halves of the two input vectors. -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000))) (vec_zip1 a b (VectorSize.Size8x16))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808))) (vec_zip2 a b (VectorSize.Size8x16))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1716_0706_1514_0504_1312_0302_1110_0100))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1716_0706_1514_0504_1312_0302_1110_0100))) (vec_zip1 a b (VectorSize.Size16x8))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e_0f0e_1d1c_0d0c_1b1a_0b0a_1918_0908))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f1e_0f0e_1d1c_0d0c_1b1a_0b0a_1918_0908))) (vec_zip2 a b (VectorSize.Size16x8))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x17161514_07060504_13121110_03020100))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x17161514_07060504_13121110_03020100))) (vec_zip1 a b (VectorSize.Size32x4))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_1b1a1918_0b0a0908))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_1b1a1918_0b0a0908))) (vec_zip2 a b (VectorSize.Size32x4))) ;; Note that zip1/zip2 for i64x2 vectors is omitted since it's already covered ;; by the i64x2 cases of uzp1/uzp2 above where both zip and uzp have the same @@ -206,17 +206,17 @@ ;; Rules for the `trn1` and `trn2` instructions which interleave odd or even ;; lanes in the two input vectors. -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1e0e_1c0c_1a0a_1808_1606_1404_1202_1000))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1e0e_1c0c_1a0a_1808_1606_1404_1202_1000))) (vec_trn1 a b (VectorSize.Size8x16))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f0f_1d0d_1b0b_1909_1707_1505_1303_1101))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f0f_1d0d_1b0b_1909_1707_1505_1303_1101))) (vec_trn2 a b (VectorSize.Size8x16))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1d1c_0d0c_1918_0908_1514_0504_1110_0100))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1d1c_0d0c_1918_0908_1514_0504_1110_0100))) (vec_trn1 a b (VectorSize.Size16x8))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e_0f0e_1b1a_0b0a_1716_0706_1312_0302))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f1e_0f0e_1b1a_0b0a_1716_0706_1312_0302))) (vec_trn2 a b (VectorSize.Size16x8))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1b1a1918_0b0a0908_13121110_03020100))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1b1a1918_0b0a0908_13121110_03020100))) (vec_trn1 a b (VectorSize.Size32x4))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_17161514_07060504))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_17161514_07060504))) (vec_trn2 a b (VectorSize.Size32x4))) ;; Note that trn1/trn2 for i64x2 vectors is omitted since it's already covered ;; by the i64x2 cases of uzp1/uzp2 above where both trn and uzp have the same @@ -226,31 +226,31 @@ ;; the byte level, the 16-bit level, or 32-bit level. Note that all of these ;; patterns only match reversals in the first operand, but they can ;; theoretically be extended if necessary to reversals in the second operand. -(rule 1 (lower (shuffle a b (u128_from_immediate 0x0e0f_0c0d_0a0b_0809_0607_0405_0203_0001))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x0e0f_0c0d_0a0b_0809_0607_0405_0203_0001))) (rev16 a (VectorSize.Size8x16))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x0c0d0e0f_08090a0b_04050607_00010203))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x0c0d0e0f_08090a0b_04050607_00010203))) (rev32 a (VectorSize.Size8x16))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x0d0c0f0e_09080b0a_05040706_01000302))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x0d0c0f0e_09080b0a_05040706_01000302))) (rev32 a (VectorSize.Size16x8))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x08090a0b0c0d0e0f_0001020304050607))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x08090a0b0c0d0e0f_0001020304050607))) (rev64 a (VectorSize.Size8x16))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x09080b0a0d0c0f0e_0100030205040706))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x09080b0a0d0c0f0e_0100030205040706))) (rev64 a (VectorSize.Size16x8))) -(rule 1 (lower (shuffle a b (u128_from_immediate 0x0b0a09080f0e0d0c_0302010007060504))) +(rule 1 (lower (shuffle _ a b (u128_from_immediate 0x0b0a09080f0e0d0c_0302010007060504))) (rev64 a (VectorSize.Size32x4))) -(rule (lower (has_type ty (shuffle rn rn2 (u128_from_immediate mask)))) +(rule (lower (has_type ty (shuffle _ rn rn2 (u128_from_immediate mask)))) (let ((mask_reg Reg (constant_f128 mask))) (vec_tbl2 rn rn2 mask_reg ty))) ;;;; Rules for `swizzle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type vec_i128_ty (swizzle rn rm))) +(rule (lower (has_type vec_i128_ty (swizzle _ rn rm))) (vec_tbl rn rm)) ;;;; Rules for `isplit` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (isplit x @ (value_type $I128))) +(rule (lower (isplit _ x @ (value_type $I128))) (let ((x_regs ValueRegs x) (x_lo ValueRegs (value_regs_get x_regs 0)) @@ -260,13 +260,13 @@ ;; Special-case the lowering of an `isplit` of a 128-bit multiply where the ;; lower bits of the result are discarded and the operands are sign or zero ;; extended. This maps directly to `umulh` and `smulh`. -(rule 1 (lower i @ (isplit (has_type $I128 (imul (uextend x) (uextend y))))) +(rule 1 (lower i @ (isplit _ (has_type $I128 (imul _ (uextend _ x) (uextend _ y))))) (if-let (first_result lo) i) (if-let true (value_is_unused lo)) (output_pair (invalid_reg) (umulh $I64 (put_in_reg_zext64 x) (put_in_reg_zext64 y)))) -(rule 1 (lower i @ (isplit (has_type $I128 (imul (sextend x) (sextend y))))) +(rule 1 (lower i @ (isplit _ (has_type $I128 (imul _ (sextend _ x) (sextend _ y))))) (if-let (first_result lo) i) (if-let true (value_is_unused lo)) (output_pair (invalid_reg) @@ -274,21 +274,21 @@ ;;;; Rules for `iconcat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I128 (iconcat lo hi))) +(rule (lower (has_type $I128 (iconcat _ lo hi))) (output (value_regs lo hi))) ;;;; Rules for `scalar_to_vector` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32X4 (scalar_to_vector x))) +(rule (lower (has_type $F32X4 (scalar_to_vector _ x))) (fpu_extend x (ScalarSize.Size32))) -(rule (lower (has_type $F64X2 (scalar_to_vector x))) +(rule (lower (has_type $F64X2 (scalar_to_vector _ x))) (fpu_extend x (ScalarSize.Size64))) -(rule -1 (lower (scalar_to_vector x @ (value_type $I64))) +(rule -1 (lower (scalar_to_vector _ x @ (value_type $I64))) (mov_to_fpu x (ScalarSize.Size64))) -(rule -2 (lower (scalar_to_vector x @ (value_type (int_fits_in_32 _)))) +(rule -2 (lower (scalar_to_vector _ x @ (value_type (int_fits_in_32 _)))) (mov_to_fpu (put_in_reg_zext32 x) (ScalarSize.Size32))) ;;;; Rules for `vall_true` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -302,13 +302,13 @@ ;; 0 when all input elements are true, i.e. non-zero, or a NaN otherwise ;; (either -1 or -2 when represented as an integer); NaNs are the only ;; floating-point numbers that compare unequal to themselves. -(rule (lower (vall_true x @ (value_type (multi_lane 64 2)))) +(rule (lower (vall_true _ x @ (value_type (multi_lane 64 2)))) (let ((x1 Reg (cmeq0 x (VectorSize.Size64x2))) (x2 Reg (addp x1 x1 (VectorSize.Size64x2)))) (with_flags (fpu_cmp (ScalarSize.Size64) x2 x2) (materialize_bool_result (Cond.Eq))))) -(rule (lower (vall_true x @ (value_type (multi_lane 32 2)))) +(rule (lower (vall_true _ x @ (value_type (multi_lane 32 2)))) (let ((x1 Reg (mov_from_vec x 0 (ScalarSize.Size64)))) (with_flags (cmp_rr_shift (OperandSize.Size64) (zero_reg) x1 32) (ccmp_imm @@ -325,7 +325,7 @@ ;; mov xm, vn.d[0] ;; cmp xm, #0 ;; cset xm, ne -(rule -1 (lower (vall_true x @ (value_type (lane_fits_in_32 ty)))) +(rule -1 (lower (vall_true _ x @ (value_type (lane_fits_in_32 ty)))) (if (not_vec32x2 ty)) (let ((x1 Reg (vec_lanes (VecLanesOp.Uminv) x (vector_size ty))) (x2 Reg (mov_from_vec x1 0 (ScalarSize.Size64)))) @@ -334,40 +334,40 @@ ;;;; Rules for `vany_true` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (vany_true x @ (value_type in_ty))) +(rule (lower (vany_true _ x @ (value_type in_ty))) (with_flags (vanytrue x in_ty) (materialize_bool_result (Cond.Ne)))) ;;;; Rules for `iadd_pairwise` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; special case for the `i16x8.extadd_pairwise_i8x16_s` wasm instruction -(rule (lower (has_type $I16X8 (iadd_pairwise (swiden_low x) (swiden_high x)))) +(rule (lower (has_type $I16X8 (iadd_pairwise _ (swiden_low _ x) (swiden_high _ x)))) (saddlp8 x)) ;; special case for the `i32x4.extadd_pairwise_i16x8_s` wasm instruction -(rule (lower (has_type $I32X4 (iadd_pairwise (swiden_low x) (swiden_high x)))) +(rule (lower (has_type $I32X4 (iadd_pairwise _ (swiden_low _ x) (swiden_high _ x)))) (saddlp16 x)) ;; special case for the `i16x8.extadd_pairwise_i8x16_u` wasm instruction -(rule (lower (has_type $I16X8 (iadd_pairwise (uwiden_low x) (uwiden_high x)))) +(rule (lower (has_type $I16X8 (iadd_pairwise _ (uwiden_low _ x) (uwiden_high _ x)))) (uaddlp8 x)) ;; special case for the `i32x4.extadd_pairwise_i16x8_u` wasm instruction -(rule (lower (has_type $I32X4 (iadd_pairwise (uwiden_low x) (uwiden_high x)))) +(rule (lower (has_type $I32X4 (iadd_pairwise _ (uwiden_low _ x) (uwiden_high _ x)))) (uaddlp16 x)) -(rule -1 (lower (has_type ty (iadd_pairwise x y))) +(rule -1 (lower (has_type ty (iadd_pairwise _ x y))) (addp x y (vector_size ty))) ;;;; Rules for `iabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (iabs x))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (iabs _ x))) (vec_abs x (vector_size ty))) -(rule iabs_64 2 (lower (has_type $I64 (iabs x))) +(rule iabs_64 2 (lower (has_type $I64 (iabs _ x))) (abs (OperandSize.Size64) x)) -(rule iabs_8_16_32 1 (lower (has_type (fits_in_32 ty) (iabs x))) +(rule iabs_8_16_32 1 (lower (has_type (fits_in_32 ty) (iabs _ x))) (abs (OperandSize.Size32) (put_in_reg_sext32 x))) ; `rustc` implementation. @@ -377,7 +377,7 @@ ; `x - 0`. ; - if `x` is negative, the xor'd bits = ~x and the mask = -1, so we end up with ; `~x - (-1) = ~x + 1`, which is exactly `abs(x)`. -(rule (lower (has_type $I128 (iabs x))) +(rule (lower (has_type $I128 (iabs _ x))) (let ((x_regs ValueRegs x) (x_lo Reg (value_regs_get x_regs 0)) (x_hi Reg (value_regs_get x_regs 1)) @@ -390,7 +390,7 @@ ;;;; Rules for `avg_round` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I64X2 (avg_round x y))) +(rule (lower (has_type $I64X2 (avg_round _ x y))) (let ((one Reg (splat_const 1 (VectorSize.Size64x2))) (c Reg (orr_vec x y (VectorSize.Size64x2))) (c Reg (and_vec c one (VectorSize.Size64x2))) @@ -399,144 +399,144 @@ (sum Reg (add_vec x y (VectorSize.Size64x2)))) (add_vec c sum (VectorSize.Size64x2)))) -(rule -1 (lower (has_type (lane_fits_in_32 ty) (avg_round x y))) +(rule -1 (lower (has_type (lane_fits_in_32 ty) (avg_round _ x y))) (vec_rrr (VecALUOp.Urhadd) x y (vector_size ty))) ;;;; Rules for `sqmul_round_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty @ (multi_lane _ _) (sqmul_round_sat x y))) +(rule (lower (has_type ty @ (multi_lane _ _) (sqmul_round_sat _ x y))) (vec_rrr (VecALUOp.Sqrdmulh) x y (vector_size ty))) ;;;; Rules for `fadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (fadd rn rm))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (fadd _ rn rm))) (vec_rrr (VecALUOp.Fadd) rn rm (vector_size ty))) -(rule (lower (has_type (ty_scalar_float ty) (fadd rn rm))) +(rule (lower (has_type (ty_scalar_float ty) (fadd _ rn rm))) (fpu_rrr (FPUOp2.Add) rn rm (scalar_size ty))) ;;;; Rules for `fsub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (fsub rn rm))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (fsub _ rn rm))) (vec_rrr (VecALUOp.Fsub) rn rm (vector_size ty))) -(rule (lower (has_type (ty_scalar_float ty) (fsub rn rm))) +(rule (lower (has_type (ty_scalar_float ty) (fsub _ rn rm))) (fpu_rrr (FPUOp2.Sub) rn rm (scalar_size ty))) ;;;; Rules for `fmul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (fmul rn rm))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (fmul _ rn rm))) (vec_rrr (VecALUOp.Fmul) rn rm (vector_size ty))) -(rule (lower (has_type (ty_scalar_float ty) (fmul rn rm))) +(rule (lower (has_type (ty_scalar_float ty) (fmul _ rn rm))) (fpu_rrr (FPUOp2.Mul) rn rm (scalar_size ty))) ;;;; Rules for `fdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (fdiv rn rm))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (fdiv _ rn rm))) (vec_rrr (VecALUOp.Fdiv) rn rm (vector_size ty))) -(rule (lower (has_type (ty_scalar_float ty) (fdiv rn rm))) +(rule (lower (has_type (ty_scalar_float ty) (fdiv _ rn rm))) (fpu_rrr (FPUOp2.Div) rn rm (scalar_size ty))) ;;;; Rules for `fmin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (fmin rn rm))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (fmin _ rn rm))) (vec_rrr (VecALUOp.Fmin) rn rm (vector_size ty))) -(rule (lower (has_type (ty_scalar_float ty) (fmin rn rm))) +(rule (lower (has_type (ty_scalar_float ty) (fmin _ rn rm))) (fpu_rrr (FPUOp2.Min) rn rm (scalar_size ty))) ;;;; Rules for `fmax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (fmax rn rm))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (fmax _ rn rm))) (vec_rrr (VecALUOp.Fmax) rn rm (vector_size ty))) -(rule (lower (has_type (ty_scalar_float ty) (fmax rn rm))) +(rule (lower (has_type (ty_scalar_float ty) (fmax _ rn rm))) (fpu_rrr (FPUOp2.Max) rn rm (scalar_size ty))) ;;;; Rules for `sqrt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (sqrt x))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (sqrt _ x))) (vec_misc (VecMisc2.Fsqrt) x (vector_size ty))) -(rule (lower (has_type (ty_scalar_float ty) (sqrt x))) +(rule (lower (has_type (ty_scalar_float ty) (sqrt _ x))) (fpu_rr (FPUOp1.Sqrt) x (scalar_size ty))) ;;;; Rules for `fneg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (fneg x))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (fneg _ x))) (vec_misc (VecMisc2.Fneg) x (vector_size ty))) -(rule (lower (has_type (ty_scalar_float ty) (fneg x))) +(rule (lower (has_type (ty_scalar_float ty) (fneg _ x))) (fpu_rr (FPUOp1.Neg) x (scalar_size ty))) ;;;; Rules for `fabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (fabs x))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (fabs _ x))) (vec_misc (VecMisc2.Fabs) x (vector_size ty))) -(rule (lower (has_type (ty_scalar_float ty) (fabs x))) +(rule (lower (has_type (ty_scalar_float ty) (fabs _ x))) (fpu_rr (FPUOp1.Abs) x (scalar_size ty))) ;;;; Rules for `fpromote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F64 (fpromote x))) +(rule (lower (has_type $F64 (fpromote _ x))) (fpu_rr (FPUOp1.Cvt32To64) x (ScalarSize.Size32))) ;;;; Rules for `fdemote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fdemote x))) +(rule (lower (has_type $F32 (fdemote _ x))) (fpu_rr (FPUOp1.Cvt64To32) x (ScalarSize.Size64))) ;;;; Rules for `ceil` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (ceil x))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (ceil _ x))) (vec_misc (VecMisc2.Frintp) x (vector_size ty))) -(rule (lower (has_type $F32 (ceil x))) +(rule (lower (has_type $F32 (ceil _ x))) (fpu_round (FpuRoundMode.Plus32) x)) -(rule (lower (has_type $F64 (ceil x))) +(rule (lower (has_type $F64 (ceil _ x))) (fpu_round (FpuRoundMode.Plus64) x)) ;;;; Rules for `floor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (floor x))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (floor _ x))) (vec_misc (VecMisc2.Frintm) x (vector_size ty))) -(rule (lower (has_type $F32 (floor x))) +(rule (lower (has_type $F32 (floor _ x))) (fpu_round (FpuRoundMode.Minus32) x)) -(rule (lower (has_type $F64 (floor x))) +(rule (lower (has_type $F64 (floor _ x))) (fpu_round (FpuRoundMode.Minus64) x)) ;;;; Rules for `trunc` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (trunc x))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (trunc _ x))) (vec_misc (VecMisc2.Frintz) x (vector_size ty))) -(rule (lower (has_type $F32 (trunc x))) +(rule (lower (has_type $F32 (trunc _ x))) (fpu_round (FpuRoundMode.Zero32) x)) -(rule (lower (has_type $F64 (trunc x))) +(rule (lower (has_type $F64 (trunc _ x))) (fpu_round (FpuRoundMode.Zero64) x)) ;;;; Rules for `nearest` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane _ _) (nearest x))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (nearest _ x))) (vec_misc (VecMisc2.Frintn) x (vector_size ty))) -(rule (lower (has_type $F32 (nearest x))) +(rule (lower (has_type $F32 (nearest _ x))) (fpu_round (FpuRoundMode.Nearest32) x)) -(rule (lower (has_type $F64 (nearest x))) +(rule (lower (has_type $F64 (nearest _ x))) (fpu_round (FpuRoundMode.Nearest64) x)) ;;;; Rules for `fma` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type ty (fma x y z))) (fmadd ty x y z)) -(rule 2 (lower (has_type (ty_scalar_float ty) (fma x y (fneg z)))) (fnmsub ty x y z)) +(rule 1 (lower (has_type ty (fma _ x y z))) (fmadd ty x y z)) +(rule 2 (lower (has_type (ty_scalar_float ty) (fma _ x y (fneg _ z)))) (fnmsub ty x y z)) ;; Constructors matching the scalar behavior of aarch64. If you're confused like ;; I was reading over these, they are: @@ -552,10 +552,10 @@ ;; Switch `fm{add,sub}` to `fnm{add,sub}` if one of the operands are negated ;; instruction instead -(rule 2 (fmadd ty (fneg x) y z) (fmsub ty x y z)) -(rule 3 (fmadd ty x (fneg y) z) (fmsub ty x y z)) -(rule 2 (fnmsub ty (fneg x) y z) (fnmadd ty x y z)) -(rule 3 (fnmsub ty x (fneg y) z) (fnmadd ty x y z)) +(rule 2 (fmadd ty (fneg _ x) y z) (fmsub ty x y z)) +(rule 3 (fmadd ty x (fneg _ y) z) (fmsub ty x y z)) +(rule 2 (fnmsub ty (fneg _ x) y z) (fnmadd ty x y z)) +(rule 3 (fnmsub ty x (fneg _ y) z) (fnmadd ty x y z)) ;; Scalar cases (rule 0 (fmadd (ty_scalar_float ty) x y z) (fpu_rrrr (FPUOp3.MAdd) (scalar_size ty) x y z)) @@ -577,9 +577,9 @@ ;; Special case: if one of the multiplicands are a splat then the element-based ;; fma can be used instead with 0 as the element index. -(rule 1 (lower_fmla op (splat x) y z size) +(rule 1 (lower_fmla op (splat _ x) y z size) (vec_fmla_elem op z y x size 0)) -(rule 2 (lower_fmla op x (splat y) z size) +(rule 2 (lower_fmla op x (splat _ y) z size) (vec_fmla_elem op z x y size 0)) ;; Special case: if one of the multiplicands is a shuffle to broadcast a @@ -589,130 +589,130 @@ ;; Note that in Cranelift shuffle always has i8x16 inputs and outputs so ;; a `bitcast` is matched here explicitly since that's the main way a shuffle ;; output will be fed into this instruction. -(rule 3 (lower_fmla op (bitcast _ (shuffle x x (shuffle32_from_imm n n n n))) y z size @ (VectorSize.Size32x4)) +(rule 3 (lower_fmla op (bitcast _ _ (shuffle _ x x (shuffle32_from_imm n n n n))) y z size @ (VectorSize.Size32x4)) (if-let true (u64_lt n 4)) (vec_fmla_elem op z y x size n)) -(rule 4 (lower_fmla op x (bitcast _ (shuffle y y (shuffle32_from_imm n n n n))) z size @ (VectorSize.Size32x4)) +(rule 4 (lower_fmla op x (bitcast _ _ (shuffle _ y y (shuffle32_from_imm n n n n))) z size @ (VectorSize.Size32x4)) (if-let true (u64_lt n 4)) (vec_fmla_elem op z x y size n)) -(rule 3 (lower_fmla op (bitcast _ (shuffle x x (shuffle64_from_imm n n))) y z size @ (VectorSize.Size64x2)) +(rule 3 (lower_fmla op (bitcast _ _ (shuffle _ x x (shuffle64_from_imm n n))) y z size @ (VectorSize.Size64x2)) (if-let true (u64_lt n 2)) (vec_fmla_elem op z y x size n)) -(rule 4 (lower_fmla op x (bitcast _ (shuffle y y (shuffle64_from_imm n n))) z size @ (VectorSize.Size64x2)) +(rule 4 (lower_fmla op x (bitcast _ _ (shuffle _ y y (shuffle64_from_imm n n))) z size @ (VectorSize.Size64x2)) (if-let true (u64_lt n 2)) (vec_fmla_elem op z x y size n)) ;;;; Rules for `fcopysign` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (fcopysign x y))) +(rule (lower (has_type ty (fcopysign _ x y))) (fcopy_sign x y ty)) ;;;; Rules for `fcvt_to_uint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_uint x @ (value_type $F32)))) +(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_uint _ x @ (value_type $F32)))) (fpu_to_int_cvt (FpuToIntOp.F32ToU32) x false $F32 out_ty)) -(rule 1 (lower (has_type $I64 (fcvt_to_uint x @ (value_type $F32)))) +(rule 1 (lower (has_type $I64 (fcvt_to_uint _ x @ (value_type $F32)))) (fpu_to_int_cvt (FpuToIntOp.F32ToU64) x false $F32 $I64)) -(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_uint x @ (value_type $F64)))) +(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_uint _ x @ (value_type $F64)))) (fpu_to_int_cvt (FpuToIntOp.F64ToU32) x false $F64 out_ty)) -(rule 1 (lower (has_type $I64 (fcvt_to_uint x @ (value_type $F64)))) +(rule 1 (lower (has_type $I64 (fcvt_to_uint _ x @ (value_type $F64)))) (fpu_to_int_cvt (FpuToIntOp.F64ToU64) x false $F64 $I64)) ;;;; Rules for `fcvt_to_sint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint x @ (value_type $F32)))) +(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint _ x @ (value_type $F32)))) (fpu_to_int_cvt (FpuToIntOp.F32ToI32) x true $F32 out_ty)) -(rule 1 (lower (has_type $I64 (fcvt_to_sint x @ (value_type $F32)))) +(rule 1 (lower (has_type $I64 (fcvt_to_sint _ x @ (value_type $F32)))) (fpu_to_int_cvt (FpuToIntOp.F32ToI64) x true $F32 $I64)) -(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint x @ (value_type $F64)))) +(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint _ x @ (value_type $F64)))) (fpu_to_int_cvt (FpuToIntOp.F64ToI32) x true $F64 out_ty)) -(rule 1 (lower (has_type $I64 (fcvt_to_sint x @ (value_type $F64)))) +(rule 1 (lower (has_type $I64 (fcvt_to_sint _ x @ (value_type $F64)))) (fpu_to_int_cvt (FpuToIntOp.F64ToI64) x true $F64 $I64)) ;;;; Rules for `fcvt_from_uint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane 32 _) (fcvt_from_uint x @ (value_type (multi_lane 32 _))))) +(rule -1 (lower (has_type ty @ (multi_lane 32 _) (fcvt_from_uint _ x @ (value_type (multi_lane 32 _))))) (vec_misc (VecMisc2.Ucvtf) x (vector_size ty))) -(rule -1 (lower (has_type ty @ (multi_lane 64 _) (fcvt_from_uint x @ (value_type (multi_lane 64 _))))) +(rule -1 (lower (has_type ty @ (multi_lane 64 _) (fcvt_from_uint _ x @ (value_type (multi_lane 64 _))))) (vec_misc (VecMisc2.Ucvtf) x (vector_size ty))) -(rule (lower (has_type $F32 (fcvt_from_uint x @ (value_type (fits_in_32 _))))) +(rule (lower (has_type $F32 (fcvt_from_uint _ x @ (value_type (fits_in_32 _))))) (int_to_fpu (IntToFpuOp.U32ToF32) (put_in_reg_zext32 x))) -(rule (lower (has_type $F64 (fcvt_from_uint x @ (value_type (fits_in_32 _))))) +(rule (lower (has_type $F64 (fcvt_from_uint _ x @ (value_type (fits_in_32 _))))) (int_to_fpu (IntToFpuOp.U32ToF64) (put_in_reg_zext32 x))) -(rule 1 (lower (has_type $F32 (fcvt_from_uint x @ (value_type $I64)))) +(rule 1 (lower (has_type $F32 (fcvt_from_uint _ x @ (value_type $I64)))) (int_to_fpu (IntToFpuOp.U64ToF32) x)) -(rule 1 (lower (has_type $F64 (fcvt_from_uint x @ (value_type $I64)))) +(rule 1 (lower (has_type $F64 (fcvt_from_uint _ x @ (value_type $I64)))) (int_to_fpu (IntToFpuOp.U64ToF64) x)) ;;;; Rules for `fcvt_from_sint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane 32 _) (fcvt_from_sint x @ (value_type (multi_lane 32 _))))) +(rule -1 (lower (has_type ty @ (multi_lane 32 _) (fcvt_from_sint _ x @ (value_type (multi_lane 32 _))))) (vec_misc (VecMisc2.Scvtf) x (vector_size ty))) -(rule -1 (lower (has_type ty @ (multi_lane 64 _) (fcvt_from_sint x @ (value_type (multi_lane 64 _))))) +(rule -1 (lower (has_type ty @ (multi_lane 64 _) (fcvt_from_sint _ x @ (value_type (multi_lane 64 _))))) (vec_misc (VecMisc2.Scvtf) x (vector_size ty))) -(rule (lower (has_type $F32 (fcvt_from_sint x @ (value_type (fits_in_32 _))))) +(rule (lower (has_type $F32 (fcvt_from_sint _ x @ (value_type (fits_in_32 _))))) (int_to_fpu (IntToFpuOp.I32ToF32) (put_in_reg_sext32 x))) -(rule (lower (has_type $F64 (fcvt_from_sint x @ (value_type (fits_in_32 _))))) +(rule (lower (has_type $F64 (fcvt_from_sint _ x @ (value_type (fits_in_32 _))))) (int_to_fpu (IntToFpuOp.I32ToF64) (put_in_reg_sext32 x))) -(rule 1 (lower (has_type $F32 (fcvt_from_sint x @ (value_type $I64)))) +(rule 1 (lower (has_type $F32 (fcvt_from_sint _ x @ (value_type $I64)))) (int_to_fpu (IntToFpuOp.I64ToF32) x)) -(rule 1 (lower (has_type $F64 (fcvt_from_sint x @ (value_type $I64)))) +(rule 1 (lower (has_type $F64 (fcvt_from_sint _ x @ (value_type $I64)))) (int_to_fpu (IntToFpuOp.I64ToF64) x)) ;;;; Rules for `fcvt_to_uint_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane 32 _) (fcvt_to_uint_sat x @ (value_type (multi_lane 32 _))))) +(rule -1 (lower (has_type ty @ (multi_lane 32 _) (fcvt_to_uint_sat _ x @ (value_type (multi_lane 32 _))))) (vec_misc (VecMisc2.Fcvtzu) x (vector_size ty))) -(rule -1 (lower (has_type ty @ (multi_lane 64 _) (fcvt_to_uint_sat x @ (value_type (multi_lane 64 _))))) +(rule -1 (lower (has_type ty @ (multi_lane 64 _) (fcvt_to_uint_sat _ x @ (value_type (multi_lane 64 _))))) (vec_misc (VecMisc2.Fcvtzu) x (vector_size ty))) -(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_uint_sat x @ (value_type $F32)))) +(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_uint_sat _ x @ (value_type $F32)))) (fpu_to_int_cvt_sat (FpuToIntOp.F32ToU32) x false out_ty)) -(rule 1 (lower (has_type $I64 (fcvt_to_uint_sat x @ (value_type $F32)))) +(rule 1 (lower (has_type $I64 (fcvt_to_uint_sat _ x @ (value_type $F32)))) (fpu_to_int_cvt_sat (FpuToIntOp.F32ToU64) x false $I64)) -(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_uint_sat x @ (value_type $F64)))) +(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_uint_sat _ x @ (value_type $F64)))) (fpu_to_int_cvt_sat (FpuToIntOp.F64ToU32) x false out_ty)) -(rule 1 (lower (has_type $I64 (fcvt_to_uint_sat x @ (value_type $F64)))) +(rule 1 (lower (has_type $I64 (fcvt_to_uint_sat _ x @ (value_type $F64)))) (fpu_to_int_cvt_sat (FpuToIntOp.F64ToU64) x false $I64)) ;;;; Rules for `fcvt_to_sint_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty @ (multi_lane 32 _) (fcvt_to_sint_sat x @ (value_type (multi_lane 32 _))))) +(rule -1 (lower (has_type ty @ (multi_lane 32 _) (fcvt_to_sint_sat _ x @ (value_type (multi_lane 32 _))))) (vec_misc (VecMisc2.Fcvtzs) x (vector_size ty))) -(rule -1 (lower (has_type ty @ (multi_lane 64 _) (fcvt_to_sint_sat x @ (value_type (multi_lane 64 _))))) +(rule -1 (lower (has_type ty @ (multi_lane 64 _) (fcvt_to_sint_sat _ x @ (value_type (multi_lane 64 _))))) (vec_misc (VecMisc2.Fcvtzs) x (vector_size ty))) -(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint_sat x @ (value_type $F32)))) +(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint_sat _ x @ (value_type $F32)))) (fpu_to_int_cvt_sat (FpuToIntOp.F32ToI32) x true out_ty)) -(rule 1 (lower (has_type $I64 (fcvt_to_sint_sat x @ (value_type $F32)))) +(rule 1 (lower (has_type $I64 (fcvt_to_sint_sat _ x @ (value_type $F32)))) (fpu_to_int_cvt_sat (FpuToIntOp.F32ToI64) x true $I64)) -(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint_sat x @ (value_type $F64)))) +(rule (lower (has_type (fits_in_32 out_ty) (fcvt_to_sint_sat _ x @ (value_type $F64)))) (fpu_to_int_cvt_sat (FpuToIntOp.F64ToI32) x true out_ty)) -(rule 1 (lower (has_type $I64 (fcvt_to_sint_sat x @ (value_type $F64)))) +(rule 1 (lower (has_type $I64 (fcvt_to_sint_sat _ x @ (value_type $F64)))) (fpu_to_int_cvt_sat (FpuToIntOp.F64ToI64) x true $I64)) ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -720,81 +720,81 @@ ;; `i64` and smaller ;; Base case, simply subtracting things in registers. -(rule isub_base_case -4 (lower (has_type (ty_int_ref_scalar_64 ty) (isub x y))) +(rule isub_base_case -4 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x y))) (sub ty x y)) ;; Special case for when one operand is an immediate that fits in 12 bits. -(rule isub_imm12 0 (lower (has_type (ty_int_ref_scalar_64 ty) (isub x (imm12_from_value y)))) +(rule isub_imm12 0 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x (imm12_from_value y)))) (sub_imm ty x y)) ;; Same as the previous special case, except we can switch the subtraction to an ;; addition if the negated immediate fits in 12 bits. -(rule isub_imm12_neg 2 (lower (has_type (ty_int_ref_scalar_64 ty) (isub x y))) +(rule isub_imm12_neg 2 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x y))) (if-let imm12_neg (imm12_from_negated_value y)) (add_imm ty x imm12_neg)) ;; Special cases for when we're subtracting an extended register where the ;; extending operation can get folded into the sub itself. -(rule isub_extend 1 (lower (has_type (ty_int_ref_scalar_64 ty) (isub x (extended_value_from_value y)))) +(rule isub_extend 1 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x (extended_value_from_value y)))) (sub_extend ty x y)) ;; Finally a special case for when we're subtracting the shift of a different ;; register by a constant amount and the shift can get folded into the sub. (rule isub_ishl -3 (lower (has_type (ty_int_ref_scalar_64 ty) - (isub x (ishl y (iconst k))))) + (isub _ x (ishl _ y (iconst _ k))))) (if-let amt (lshl_from_imm64 ty k)) (sub_shift ty x y amt)) ;; vectors -(rule -2 (lower (has_type ty @ (multi_lane _ _) (isub x y))) +(rule -2 (lower (has_type ty @ (multi_lane _ _) (isub _ x y))) (sub_vec x y (vector_size ty))) ;; `i128` -(rule -1 (lower (has_type $I128 (isub x y))) +(rule -1 (lower (has_type $I128 (isub _ x y))) (sub_i128 x y)) ;;;; Rules for `uadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_vec128 ty) (uadd_sat x y))) +(rule (lower (has_type (ty_vec128 ty) (uadd_sat _ x y))) (uqadd x y (vector_size ty))) ;;;; Rules for `sadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_vec128 ty) (sadd_sat x y))) +(rule (lower (has_type (ty_vec128 ty) (sadd_sat _ x y))) (sqadd x y (vector_size ty))) ;;;; Rules for `usub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_vec128 ty) (usub_sat x y))) +(rule (lower (has_type (ty_vec128 ty) (usub_sat _ x y))) (uqsub x y (vector_size ty))) ;;;; Rules for `ssub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_vec128 ty) (ssub_sat x y))) +(rule (lower (has_type (ty_vec128 ty) (ssub_sat _ x y))) (sqsub x y (vector_size ty))) ;;;; Rules for `ineg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `i64` and smaller. -(rule ineg_base_case 1 (lower (has_type (fits_in_64 ty) (ineg x))) +(rule ineg_base_case 1 (lower (has_type (fits_in_64 ty) (ineg _ x))) (sub ty (zero_reg) x)) ;; `i128` -(rule 2 (lower (has_type $I128 (ineg x))) +(rule 2 (lower (has_type $I128 (ineg _ x))) (sub_i128 (value_regs_zero) x)) ;; vectors. -(rule (lower (has_type (ty_vec128 ty) (ineg x))) +(rule (lower (has_type (ty_vec128 ty) (ineg _ x))) (neg x (vector_size ty))) ;;;; Rules for `imul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `i64` and smaller. -(rule imul_base_case -3 (lower (has_type (ty_int_ref_scalar_64 ty) (imul x y))) +(rule imul_base_case -3 (lower (has_type (ty_int_ref_scalar_64 ty) (imul _ x y))) (madd ty x y (zero_reg))) ;; `i128`. -(rule -1 (lower (has_type $I128 (imul x y))) +(rule -1 (lower (has_type $I128 (imul _ x y))) (let ;; Get the high/low registers for `x`. ((x_regs ValueRegs x) @@ -824,7 +824,7 @@ ;; Special cases where the upper bits are sign-or-zero extended of the lower bits ;; so the calculation here is much simpler with just a `umulh` or `smulh` ;; instead of the additions above as well. -(rule (lower (has_type $I128 (imul (uextend x) (uextend y)))) +(rule (lower (has_type $I128 (imul _ (uextend _ x) (uextend _ y)))) (let ( (x Reg (put_in_reg_zext64 x)) (y Reg (put_in_reg_zext64 y)) @@ -832,7 +832,7 @@ (value_regs (madd $I64 x y (zero_reg)) (umulh $I64 x y)))) -(rule (lower (has_type $I128 (imul (sextend x) (sextend y)))) +(rule (lower (has_type $I128 (imul _ (sextend _ x) (sextend _ y)))) (let ( (x Reg (put_in_reg_sext64 x)) (y Reg (put_in_reg_sext64 y)) @@ -842,7 +842,7 @@ (smulh $I64 x y)))) ;; vectors (i8x8/i8x16/i16x4/i16x8/i32x2/i32x4) -(rule -2 (lower (has_type (lane_fits_in_32 ty @ (multi_lane _ _)) (imul x y))) +(rule -2 (lower (has_type (lane_fits_in_32 ty @ (multi_lane _ _)) (imul _ x y))) (mul x y (vector_size ty))) ;; Special lowering for i64x2. @@ -874,7 +874,7 @@ ;; xtn tmp2.2s, rm.2d ;; shll rd.2d, rd.2s, #32 ;; umlal rd.2d, tmp2.2s, tmp1.2s -(rule -1 (lower (has_type $I64X2 (imul x y))) +(rule -1 (lower (has_type $I64X2 (imul _ x y))) (let ((rn Reg x) (rm Reg y) ;; Reverse the 32-bit elements in the 64-bit words. @@ -914,82 +914,82 @@ ;; Special case for `i16x8.extmul_low_i8x16_s`. (rule (lower (has_type $I16X8 - (imul (swiden_low x @ (value_type $I8X16)) - (swiden_low y @ (value_type $I8X16))))) + (imul _ (swiden_low _ x @ (value_type $I8X16)) + (swiden_low _ y @ (value_type $I8X16))))) (smull8 x y false)) ;; Special case for `i16x8.extmul_high_i8x16_s`. (rule (lower (has_type $I16X8 - (imul (swiden_high x @ (value_type $I8X16)) - (swiden_high y @ (value_type $I8X16))))) + (imul _ (swiden_high _ x @ (value_type $I8X16)) + (swiden_high _ y @ (value_type $I8X16))))) (smull8 x y true)) ;; Special case for `i16x8.extmul_low_i8x16_u`. (rule (lower (has_type $I16X8 - (imul (uwiden_low x @ (value_type $I8X16)) - (uwiden_low y @ (value_type $I8X16))))) + (imul _ (uwiden_low _ x @ (value_type $I8X16)) + (uwiden_low _ y @ (value_type $I8X16))))) (umull8 x y false)) ;; Special case for `i16x8.extmul_high_i8x16_u`. (rule (lower (has_type $I16X8 - (imul (uwiden_high x @ (value_type $I8X16)) - (uwiden_high y @ (value_type $I8X16))))) + (imul _ (uwiden_high _ x @ (value_type $I8X16)) + (uwiden_high _ y @ (value_type $I8X16))))) (umull8 x y true)) ;; Special case for `i32x4.extmul_low_i16x8_s`. (rule (lower (has_type $I32X4 - (imul (swiden_low x @ (value_type $I16X8)) - (swiden_low y @ (value_type $I16X8))))) + (imul _ (swiden_low _ x @ (value_type $I16X8)) + (swiden_low _ y @ (value_type $I16X8))))) (smull16 x y false)) ;; Special case for `i32x4.extmul_high_i16x8_s`. (rule (lower (has_type $I32X4 - (imul (swiden_high x @ (value_type $I16X8)) - (swiden_high y @ (value_type $I16X8))))) + (imul _ (swiden_high _ x @ (value_type $I16X8)) + (swiden_high _ y @ (value_type $I16X8))))) (smull16 x y true)) ;; Special case for `i32x4.extmul_low_i16x8_u`. (rule (lower (has_type $I32X4 - (imul (uwiden_low x @ (value_type $I16X8)) - (uwiden_low y @ (value_type $I16X8))))) + (imul _ (uwiden_low _ x @ (value_type $I16X8)) + (uwiden_low _ y @ (value_type $I16X8))))) (umull16 x y false)) ;; Special case for `i32x4.extmul_high_i16x8_u`. (rule (lower (has_type $I32X4 - (imul (uwiden_high x @ (value_type $I16X8)) - (uwiden_high y @ (value_type $I16X8))))) + (imul _ (uwiden_high _ x @ (value_type $I16X8)) + (uwiden_high _ y @ (value_type $I16X8))))) (umull16 x y true)) ;; Special case for `i64x2.extmul_low_i32x4_s`. (rule (lower (has_type $I64X2 - (imul (swiden_low x @ (value_type $I32X4)) - (swiden_low y @ (value_type $I32X4))))) + (imul _ (swiden_low _ x @ (value_type $I32X4)) + (swiden_low _ y @ (value_type $I32X4))))) (smull32 x y false)) ;; Special case for `i64x2.extmul_high_i32x4_s`. (rule (lower (has_type $I64X2 - (imul (swiden_high x @ (value_type $I32X4)) - (swiden_high y @ (value_type $I32X4))))) + (imul _ (swiden_high _ x @ (value_type $I32X4)) + (swiden_high _ y @ (value_type $I32X4))))) (smull32 x y true)) ;; Special case for `i64x2.extmul_low_i32x4_u`. (rule (lower (has_type $I64X2 - (imul (uwiden_low x @ (value_type $I32X4)) - (uwiden_low y @ (value_type $I32X4))))) + (imul _ (uwiden_low _ x @ (value_type $I32X4)) + (uwiden_low _ y @ (value_type $I32X4))))) (umull32 x y false)) ;; Special case for `i64x2.extmul_high_i32x4_u`. (rule (lower (has_type $I64X2 - (imul (uwiden_high x @ (value_type $I32X4)) - (uwiden_high y @ (value_type $I32X4))))) + (imul _ (uwiden_high _ x @ (value_type $I32X4)) + (uwiden_high _ y @ (value_type $I32X4))))) (umull32 x y true)) ;;;; Rules for `smulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type $I64 (smulhi x y))) +(rule 1 (lower (has_type $I64 (smulhi _ x y))) (smulh $I64 x y)) -(rule (lower (has_type (fits_in_32 ty) (smulhi x y))) +(rule (lower (has_type (fits_in_32 ty) (smulhi _ x y))) (let ((x64 Reg (put_in_reg_sext64 x)) (y64 Reg (put_in_reg_sext64 y)) (mul Reg (madd $I64 x64 y64 (zero_reg))) @@ -998,10 +998,10 @@ ;;;; Rules for `umulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type $I64 (umulhi x y))) +(rule 1 (lower (has_type $I64 (umulhi _ x y))) (umulh $I64 x y)) -(rule (lower (has_type (fits_in_32 ty) (umulhi x y))) +(rule (lower (has_type (fits_in_32 ty) (umulhi _ x y))) (let ( (x64 Reg (put_in_reg_zext64 x)) (y64 Reg (put_in_reg_zext64 y)) @@ -1030,11 +1030,11 @@ ;; move it into a register. ;; zero-extend non-zero constant -(rule (put_nonzero_in_reg (iconst (nonzero_u64_from_imm64 n)) (ExtType.Unsigned) ty) +(rule (put_nonzero_in_reg (iconst _ (nonzero_u64_from_imm64 n)) (ExtType.Unsigned) ty) (imm ty (ImmExtend.Zero) n)) ;; sign-extend non-zero constant -(rule (put_nonzero_in_reg (iconst (nonzero_u64_from_imm64 n)) (ExtType.Signed) ty) +(rule (put_nonzero_in_reg (iconst _ (nonzero_u64_from_imm64 n)) (ExtType.Signed) ty) (imm ty (ImmExtend.Sign) n)) (rule -1 (put_nonzero_in_reg val _ $I64) @@ -1049,10 +1049,10 @@ ;; Note that aarch64's `udiv` doesn't trap so to respect the semantics of ;; CLIF's `udiv` the check for zero needs to be manually performed. -(rule udiv 1 (lower (has_type $I64 (udiv x y))) +(rule udiv 1 (lower (has_type $I64 (udiv _ x y))) (a64_udiv $I64 (put_in_reg x) (put_nonzero_in_reg y (ExtType.Unsigned) $I64))) -(rule udiv (lower (has_type (fits_in_32 ty) (udiv x y))) +(rule udiv (lower (has_type (fits_in_32 ty) (udiv _ x y))) (a64_udiv $I32 (put_in_reg_zext32 x) (put_nonzero_in_reg y (ExtType.Unsigned) ty))) ;;;; Rules for `sdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1078,7 +1078,7 @@ ;; TODO: if `y` is -1 then a check that `x` is not INT_MIN is all that's ;; necessary, but right now `y` is checked to not be -1 as well. -(rule sdiv_base_case (lower (has_type $I64 (sdiv x y))) +(rule sdiv_base_case (lower (has_type $I64 (sdiv _ x y))) (let ((x64 Reg (put_in_reg_sext64 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Signed) $I64)) (intmin_check_x Reg (intmin_check $I64 x64)) @@ -1086,7 +1086,7 @@ (result Reg (a64_sdiv $I64 valid_x64 y64))) result)) -(rule sdiv_base_case -1 (lower (has_type (fits_in_32 ty) (sdiv x y))) +(rule sdiv_base_case -1 (lower (has_type (fits_in_32 ty) (sdiv _ x y))) (let ((x32 Reg (put_in_reg_sext32 x)) (y32 Reg (put_nonzero_in_reg y (ExtType.Signed) ty)) (intmin_check_x Reg (intmin_check ty x32)) @@ -1096,11 +1096,11 @@ ;; Special case for `sdiv` where no checks are needed due to division by a ;; constant meaning the checks are always passed. -(rule sdiv_safe_divisor 2 (lower (has_type $I64 (sdiv x (iconst imm)))) +(rule sdiv_safe_divisor 2 (lower (has_type $I64 (sdiv _ x (iconst _ imm)))) (if-let y (safe_divisor_from_imm64 $I64 imm)) (a64_sdiv $I64 (put_in_reg_sext64 x) (imm $I64 (ImmExtend.Sign) y))) -(rule sdiv_safe_divisor 1 (lower (has_type (fits_in_32 ty) (sdiv x (iconst imm)))) +(rule sdiv_safe_divisor 1 (lower (has_type (fits_in_32 ty) (sdiv _ x (iconst _ imm)))) (if-let y (safe_divisor_from_imm64 ty imm)) (a64_sdiv ty (put_in_reg_sext32 x) (imm ty (ImmExtend.Sign) y))) @@ -1122,28 +1122,28 @@ ;; TODO: we can avoid a 0 check, if the dividend is a non-0 constant -(rule urem (lower (has_type $I64 (urem x y))) +(rule urem (lower (has_type $I64 (urem _ x y))) (let ((x64 Reg (put_in_reg_zext64 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Unsigned) $I64)) (div Reg (a64_udiv $I64 x64 y64)) (result Reg (msub $I64 div y64 x64))) result)) -(rule urem -1 (lower (has_type (fits_in_32 ty) (urem x y))) +(rule urem -1 (lower (has_type (fits_in_32 ty) (urem _ x y))) (let ((x64 Reg (put_in_reg_zext32 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Unsigned) ty)) (div Reg (a64_udiv ty x64 y64)) (result Reg (msub ty div y64 x64))) result)) -(rule srem (lower (has_type $I64 (srem x y))) +(rule srem (lower (has_type $I64 (srem _ x y))) (let ((x64 Reg (put_in_reg_sext64 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Signed) $I64)) (div Reg (a64_sdiv $I64 x64 y64)) (result Reg (msub $I64 div y64 x64))) result)) -(rule srem -1 (lower (has_type (fits_in_32 ty) (srem x y))) +(rule srem -1 (lower (has_type (fits_in_32 ty) (srem _ x y))) (let ((x64 Reg (put_in_reg_sext32 x)) (y64 Reg (put_nonzero_in_reg y (ExtType.Signed) ty)) (div Reg (a64_sdiv ty x64 y64)) @@ -1190,76 +1190,76 @@ (with_flags_reg (cmp (operand_size ty) x y) (csel cc x y)))) -(rule umin 2 (lower (has_type (and (fits_in_64 ty) (ty_int _)) (umin x y))) +(rule umin 2 (lower (has_type (and (fits_in_64 ty) (ty_int _)) (umin _ x y))) (cmp_and_choose ty (Cond.Lo) false x y)) -(rule smin 2 (lower (has_type (and (fits_in_64 ty) (ty_int _)) (smin x y))) +(rule smin 2 (lower (has_type (and (fits_in_64 ty) (ty_int _)) (smin _ x y))) (cmp_and_choose ty (Cond.Lt) true x y)) -(rule umax 2 (lower (has_type (and (fits_in_64 ty) (ty_int _)) (umax x y))) +(rule umax 2 (lower (has_type (and (fits_in_64 ty) (ty_int _)) (umax _ x y))) (cmp_and_choose ty (Cond.Hi) false x y)) -(rule smax 2 (lower (has_type (and (fits_in_64 ty) (ty_int _)) (smax x y))) +(rule smax 2 (lower (has_type (and (fits_in_64 ty) (ty_int _)) (smax _ x y))) (cmp_and_choose ty (Cond.Gt) true x y)) ;; Vector types. -(rule (lower (has_type ty @ (not_i64x2) (smin x y))) +(rule (lower (has_type ty @ (not_i64x2) (smin _ x y))) (vec_rrr (VecALUOp.Smin) x y (vector_size ty))) -(rule 1 (lower (has_type $I64X2 (smin x y))) +(rule 1 (lower (has_type $I64X2 (smin _ x y))) (bsl $I64X2 (vec_rrr (VecALUOp.Cmgt) y x (VectorSize.Size64x2)) x y)) -(rule (lower (has_type ty @ (not_i64x2) (umin x y))) +(rule (lower (has_type ty @ (not_i64x2) (umin _ x y))) (vec_rrr (VecALUOp.Umin) x y (vector_size ty))) -(rule 1 (lower (has_type $I64X2 (umin x y))) +(rule 1 (lower (has_type $I64X2 (umin _ x y))) (bsl $I64X2 (vec_rrr (VecALUOp.Cmhi) y x (VectorSize.Size64x2)) x y)) -(rule (lower (has_type ty @ (not_i64x2) (smax x y))) +(rule (lower (has_type ty @ (not_i64x2) (smax _ x y))) (vec_rrr (VecALUOp.Smax) x y (vector_size ty))) -(rule 1 (lower (has_type $I64X2 (smax x y))) +(rule 1 (lower (has_type $I64X2 (smax _ x y))) (bsl $I64X2 (vec_rrr (VecALUOp.Cmgt) x y (VectorSize.Size64x2)) x y)) -(rule (lower (has_type ty @ (not_i64x2) (umax x y))) +(rule (lower (has_type ty @ (not_i64x2) (umax _ x y))) (vec_rrr (VecALUOp.Umax) x y (vector_size ty))) -(rule 1 (lower (has_type $I64X2 (umax x y))) +(rule 1 (lower (has_type $I64X2 (umax _ x y))) (bsl $I64X2 (vec_rrr (VecALUOp.Cmhi) x y (VectorSize.Size64x2)) x y)) ;;;; Rules for `uextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; General rule for extending input to an output which fits in a single ;; register. -(rule uextend -2 (lower (has_type (fits_in_64 out) (uextend x @ (value_type in)))) +(rule uextend -2 (lower (has_type (fits_in_64 out) (uextend _ x @ (value_type in)))) (extend x false (ty_bits in) (ty_bits out))) ;; Extraction of a vector lane automatically extends as necessary, so we can ;; skip an explicit extending instruction. (rule 1 (lower (has_type (fits_in_64 out) - (uextend (extractlane vec @ (value_type in) + (uextend _ (extractlane _ vec @ (value_type in) (u8_from_uimm8 lane))))) (mov_from_vec (put_in_reg vec) lane (lane_size in))) ;; Atomic loads will also automatically zero their upper bits so the `uextend` ;; instruction can effectively get skipped here. (rule 1 (lower (has_type (fits_in_64 out) - (uextend x @ (and (value_type in) (atomic_load (little_or_native_endian flags) _))))) + (uextend _ x @ (and (value_type in) (atomic_load _ (little_or_native_endian flags) _))))) (if-let mem_op (is_sinkable_inst x)) (load_acquire in flags (sink_atomic_load mem_op))) ;; Conversion to 128-bit needs a zero-extension of the lower bits and the upper ;; bits are all zero. -(rule -1 (lower (has_type $I128 (uextend x))) +(rule -1 (lower (has_type $I128 (uextend _ x))) (value_regs (put_in_reg_zext64 x) (imm $I64 (ImmExtend.Zero) 0))) ;; Like above where vector extraction automatically zero-extends extending to ;; i128 only requires generating a 0 constant for the upper bits. (rule (lower (has_type $I128 - (uextend (extractlane vec @ (value_type in) + (uextend _ (extractlane _ vec @ (value_type in) (u8_from_uimm8 lane))))) (value_regs (mov_from_vec (put_in_reg vec) lane (lane_size in)) (imm $I64 (ImmExtend.Zero) 0))) ;; Zero extensions from a load can be encoded in the load itself -(rule (lower (has_type (fits_in_64 _) (uextend x @ (has_type in_ty (load (little_or_native_endian flags) address offset))))) +(rule (lower (has_type (fits_in_64 _) (uextend _ x @ (has_type in_ty (load _ (little_or_native_endian flags) address offset))))) (if-let inst (is_sinkable_inst x)) (let ((_ Unit (sink_inst inst))) (aarch64_uload in_ty (amode in_ty address offset) flags))) @@ -1273,13 +1273,13 @@ ;; General rule for extending input to an output which fits in a single ;; register. -(rule sextend -4 (lower (has_type (fits_in_64 out) (sextend x @ (value_type in)))) +(rule sextend -4 (lower (has_type (fits_in_64 out) (sextend _ x @ (value_type in)))) (extend x true (ty_bits in) (ty_bits out))) ;; Extraction of a vector lane automatically extends as necessary, so we can ;; skip an explicit extending instruction. (rule -3 (lower (has_type (fits_in_64 out) - (sextend (extractlane vec @ (value_type in) + (sextend _ (extractlane _ vec @ (value_type in) (u8_from_uimm8 lane))))) (mov_from_vec_signed (put_in_reg vec) lane @@ -1287,7 +1287,7 @@ (size_from_ty out))) ;; 64-bit to 128-bit only needs to sign-extend the input to the upper bits. -(rule -2 (lower (has_type $I128 (sextend x))) +(rule -2 (lower (has_type $I128 (sextend _ x))) (let ((lo Reg (put_in_reg_sext64 x)) (hi Reg (asr_imm $I64 lo (imm_shift_from_u8 63)))) (value_regs lo hi))) @@ -1298,7 +1298,7 @@ ;; Note that `mov_from_vec_signed` doesn't exist for i64x2, so that's ;; specifically excluded here. (rule (lower (has_type $I128 - (sextend (extractlane vec @ (value_type in @ (not_i64x2)) + (sextend _ (extractlane _ vec @ (value_type in @ (not_i64x2)) (u8_from_uimm8 lane))))) (let ((lo Reg (mov_from_vec_signed (put_in_reg vec) lane @@ -1309,7 +1309,7 @@ ;; Extension from an extraction of i64x2 into i128. (rule -1 (lower (has_type $I128 - (sextend (extractlane vec @ (value_type $I64X2) + (sextend _ (extractlane _ vec @ (value_type $I64X2) (u8_from_uimm8 lane))))) (let ((lo Reg (mov_from_vec (put_in_reg vec) lane @@ -1318,7 +1318,7 @@ (value_regs lo hi))) ;; Signed extensions from a load can be encoded in the load itself -(rule (lower (has_type (fits_in_64 _) (sextend x @ (has_type in_ty (load (little_or_native_endian flags) address offset))))) +(rule (lower (has_type (fits_in_64 _) (sextend _ x @ (has_type in_ty (load _ (little_or_native_endian flags) address offset))))) (if-let inst (is_sinkable_inst x)) (let ((_ Unit (sink_inst inst))) (aarch64_sload in_ty (amode in_ty address offset) flags))) @@ -1335,21 +1335,21 @@ ;; Note that bitwise negation is implemented here as ;; ;; NOT rd, rm ==> ORR_NOT rd, zero, rm -(rule bnot_base_case -4 (lower (has_type (fits_in_64 (ty_int ty)) (bnot x))) +(rule bnot_base_case -4 (lower (has_type (fits_in_64 (ty_int ty)) (bnot _ x))) (orr_not ty (zero_reg) x)) ;; Implementation of `bnot` for floats. -(rule -3 (lower (has_type (fits_in_64 (ty_scalar_float ty)) (bnot x))) +(rule -3 (lower (has_type (fits_in_64 (ty_scalar_float ty)) (bnot _ x))) (not x (float_vector_size_in_64 ty))) ;; Implementation of `bnot` for vector types. -(rule -2 (lower (has_type (ty_vec64 ty) (bnot x))) +(rule -2 (lower (has_type (ty_vec64 ty) (bnot _ x))) (not x (vector_size ty))) -(rule -1 (lower (has_type (ty_vec128 ty) (bnot x))) +(rule -1 (lower (has_type (ty_vec128 ty) (bnot _ x))) (not x (vector_size ty))) ;; Implementation of `bnot` for `i128`. -(rule (lower (has_type $I128 (bnot x))) +(rule (lower (has_type $I128 (bnot _ x))) (let ((x_regs ValueRegs x) (x_lo Reg (value_regs_get x_regs 0)) (x_hi Reg (value_regs_get x_regs 1)) @@ -1360,87 +1360,87 @@ ;; Special case to use `orr_not_shift` if it's a `bnot` of a const-left-shifted ;; value. (rule bnot_ishl 1 (lower (has_type (fits_in_64 (ty_int ty)) - (bnot (ishl x (iconst k))))) + (bnot _ (ishl _ x (iconst _ k))))) (if-let amt (lshl_from_imm64 ty k)) (orr_not_shift ty (zero_reg) x amt)) ;; Special-cases for fusing a bnot with bxor -(rule 2 (lower (has_type (fits_in_64 ty) (bnot (bxor x y)))) +(rule 2 (lower (has_type (fits_in_64 ty) (bnot _ (bxor _ x y)))) (alu_rs_imm_logic (ALUOp.EorNot) ty x y)) -(rule 3 (lower (has_type $I128 (bnot (bxor x y)))) (i128_alu_bitop (ALUOp.EorNot) $I64 x y)) +(rule 3 (lower (has_type $I128 (bnot _ (bxor _ x y)))) (i128_alu_bitop (ALUOp.EorNot) $I64 x y)) ;;;; Rules for `band` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule band_fits_in_64 -5 (lower (has_type (fits_in_64 (ty_int ty)) (band x y))) +(rule band_fits_in_64 -5 (lower (has_type (fits_in_64 (ty_int ty)) (band _ x y))) (alu_rs_imm_logic_commutative (ALUOp.And) ty x y)) -(rule -4 (lower (has_type (fits_in_64 (ty_scalar_float ty)) (band x y))) +(rule -4 (lower (has_type (fits_in_64 (ty_scalar_float ty)) (band _ x y))) (and_vec x y (float_vector_size_in_64 ty))) ;; Implementation of `band` for vector types. -(rule -2 (lower (has_type (ty_vec64 ty) (band x y))) +(rule -2 (lower (has_type (ty_vec64 ty) (band _ x y))) (and_vec x y (vector_size ty))) -(rule -1 (lower (has_type (ty_vec128 ty) (band x y))) +(rule -1 (lower (has_type (ty_vec128 ty) (band _ x y))) (and_vec x y (vector_size ty))) -(rule (lower (has_type $I128 (band x y))) (i128_alu_bitop (ALUOp.And) $I64 x y)) +(rule (lower (has_type $I128 (band _ x y))) (i128_alu_bitop (ALUOp.And) $I64 x y)) ;; Specialized lowerings for `(band x (bnot y))` which is additionally produced ;; by Cranelift's `band_not` instruction that is legalized into the simpler ;; forms early on. -(rule band_not_right 1 (lower (has_type (fits_in_64 (ty_int ty)) (band x (bnot y)))) +(rule band_not_right 1 (lower (has_type (fits_in_64 (ty_int ty)) (band _ x (bnot _ y)))) (alu_rs_imm_logic (ALUOp.AndNot) ty x y)) -(rule band_not_left 2 (lower (has_type (fits_in_64 (ty_int ty)) (band (bnot y) x))) +(rule band_not_left 2 (lower (has_type (fits_in_64 (ty_int ty)) (band _ (bnot _ y) x))) (alu_rs_imm_logic (ALUOp.AndNot) ty x y)) -(rule 3 (lower (has_type $I128 (band x (bnot y)))) (i128_alu_bitop (ALUOp.AndNot) $I64 x y)) -(rule 4 (lower (has_type $I128 (band (bnot y) x))) (i128_alu_bitop (ALUOp.AndNot) $I64 x y)) +(rule 3 (lower (has_type $I128 (band _ x (bnot _ y)))) (i128_alu_bitop (ALUOp.AndNot) $I64 x y)) +(rule 4 (lower (has_type $I128 (band _ (bnot _ y) x))) (i128_alu_bitop (ALUOp.AndNot) $I64 x y)) -(rule 5 (lower (has_type (ty_vec64 ty) (band x (bnot y)))) +(rule 5 (lower (has_type (ty_vec64 ty) (band _ x (bnot _ y)))) (bic_vec x y (vector_size ty))) -(rule 6 (lower (has_type (ty_vec64 ty) (band (bnot y) x))) +(rule 6 (lower (has_type (ty_vec64 ty) (band _ (bnot _ y) x))) (bic_vec x y (vector_size ty))) -(rule 7 (lower (has_type (ty_vec128 ty) (band x (bnot y)))) +(rule 7 (lower (has_type (ty_vec128 ty) (band _ x (bnot _ y)))) (bic_vec x y (vector_size ty))) -(rule 8 (lower (has_type (ty_vec128 ty) (band (bnot y) x))) +(rule 8 (lower (has_type (ty_vec128 ty) (band _ (bnot _ y) x))) (bic_vec x y (vector_size ty))) ;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule bor_fits_in_64 -4 (lower (has_type (fits_in_64 (ty_int ty)) (bor x y))) +(rule bor_fits_in_64 -4 (lower (has_type (fits_in_64 (ty_int ty)) (bor _ x y))) (alu_rs_imm_logic_commutative (ALUOp.Orr) ty x y)) -(rule -3 (lower (has_type (fits_in_64 (ty_scalar_float ty)) (bor x y))) +(rule -3 (lower (has_type (fits_in_64 (ty_scalar_float ty)) (bor _ x y))) (orr_vec x y (float_vector_size_in_64 ty))) ;; Implementation of `bor` for vector types. -(rule -2 (lower (has_type (ty_vec64 ty) (bor x y))) +(rule -2 (lower (has_type (ty_vec64 ty) (bor _ x y))) (orr_vec x y (vector_size ty))) -(rule -1 (lower (has_type (ty_vec128 ty) (bor x y))) +(rule -1 (lower (has_type (ty_vec128 ty) (bor _ x y))) (orr_vec x y (vector_size ty))) -(rule (lower (has_type $I128 (bor x y))) (i128_alu_bitop (ALUOp.Orr) $I64 x y)) +(rule (lower (has_type $I128 (bor _ x y))) (i128_alu_bitop (ALUOp.Orr) $I64 x y)) ;; Specialized lowerings for `(bor x (bnot y))` which is additionally produced ;; by Cranelift's `bor_not` instruction that is legalized into the simpler ;; forms early on. -(rule bor_not_right 1 (lower (has_type (fits_in_64 (ty_int ty)) (bor x (bnot y)))) +(rule bor_not_right 1 (lower (has_type (fits_in_64 (ty_int ty)) (bor _ x (bnot _ y)))) (alu_rs_imm_logic (ALUOp.OrrNot) ty x y)) -(rule bor_not_left 2 (lower (has_type (fits_in_64 (ty_int ty)) (bor (bnot y) x))) +(rule bor_not_left 2 (lower (has_type (fits_in_64 (ty_int ty)) (bor _ (bnot _ y) x))) (alu_rs_imm_logic (ALUOp.OrrNot) ty x y)) -(rule 3 (lower (has_type $I128 (bor x (bnot y)))) (i128_alu_bitop (ALUOp.OrrNot) $I64 x y)) -(rule 4 (lower (has_type $I128 (bor (bnot y) x))) (i128_alu_bitop (ALUOp.OrrNot) $I64 x y)) +(rule 3 (lower (has_type $I128 (bor _ x (bnot _ y)))) (i128_alu_bitop (ALUOp.OrrNot) $I64 x y)) +(rule 4 (lower (has_type $I128 (bor _ (bnot _ y) x))) (i128_alu_bitop (ALUOp.OrrNot) $I64 x y)) -(rule bor_not_right_vec64 5 (lower (has_type (ty_vec64 ty) (bor x (bnot y)))) +(rule bor_not_right_vec64 5 (lower (has_type (ty_vec64 ty) (bor _ x (bnot _ y)))) (orn_vec x y (vector_size ty))) -(rule bor_not_left_vec64 6 (lower (has_type (ty_vec64 ty) (bor (bnot y) x))) +(rule bor_not_left_vec64 6 (lower (has_type (ty_vec64 ty) (bor _ (bnot _ y) x))) (orn_vec x y (vector_size ty))) -(rule bor_not_right_vec128 7 (lower (has_type (ty_vec128 ty) (bor x (bnot y)))) +(rule bor_not_right_vec128 7 (lower (has_type (ty_vec128 ty) (bor _ x (bnot _ y)))) (orn_vec x y (vector_size ty))) -(rule bor_not_left_vec128 8 (lower (has_type (ty_vec128 ty) (bor (bnot y) x))) +(rule bor_not_left_vec128 8 (lower (has_type (ty_vec128 ty) (bor _ (bnot _ y) x))) (orn_vec x y (vector_size ty))) @@ -1455,55 +1455,64 @@ ;; `bor`. ;; ;; (x << xs) | (y >> ys) if (xs + ys == widthof(ty)) => extr(x, y, ys) +;; +;; Note that both `xs` and `ys` must be larger than 0. If either one is 0 and +;; they sum to the type width then it means that the shifts don't actually do +;; anything CLIF-wise and this should compile down to a `bor` operation. Leave +;; that edge case to the mid-end and only lower to `extr` here. (rule 5 (lower (has_type (ty_32_or_64 ty) - (bor (ishl x (u8_from_iconst xs)) (ushr y (u8_from_iconst ys))))) + (bor _ (ishl _ x (u8_from_iconst xs)) (ushr _ y (u8_from_iconst ys))))) (if-let true (u64_eq (ty_bits ty) (u64_wrapping_add xs ys))) + (if-let true (u64_gt xs 0)) + (if-let true (u64_gt ys 0)) (a64_extr ty x y (imm_shift_from_u8 ys))) (rule 5 (lower (has_type (ty_32_or_64 ty) - (bor (ushr y (u8_from_iconst ys)) (ishl x (u8_from_iconst xs))))) + (bor _ (ushr _ y (u8_from_iconst ys)) (ishl _ x (u8_from_iconst xs))))) (if-let true (u64_eq (ty_bits ty) (u64_wrapping_add xs ys))) + (if-let true (u64_gt xs 0)) + (if-let true (u64_gt ys 0)) (a64_extr ty x y (imm_shift_from_u8 ys))) ;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule bxor_fits_in_64 -4 (lower (has_type (fits_in_64 (ty_int ty)) (bxor x y))) +(rule bxor_fits_in_64 -4 (lower (has_type (fits_in_64 (ty_int ty)) (bxor _ x y))) (alu_rs_imm_logic_commutative (ALUOp.Eor) ty x y)) -(rule -3 (lower (has_type (fits_in_64 (ty_scalar_float ty)) (bxor x y))) +(rule -3 (lower (has_type (fits_in_64 (ty_scalar_float ty)) (bxor _ x y))) (eor_vec x y (float_vector_size_in_64 ty))) ;; Implementation of `bxor` for vector types. -(rule -2 (lower (has_type (ty_vec64 ty) (bxor x y))) +(rule -2 (lower (has_type (ty_vec64 ty) (bxor _ x y))) (eor_vec x y (vector_size ty))) -(rule -1 (lower (has_type (ty_vec128 ty) (bxor x y))) +(rule -1 (lower (has_type (ty_vec128 ty) (bxor _ x y))) (eor_vec x y (vector_size ty))) -(rule (lower (has_type $I128 (bxor x y))) (i128_alu_bitop (ALUOp.Eor) $I64 x y)) +(rule (lower (has_type $I128 (bxor _ x y))) (i128_alu_bitop (ALUOp.Eor) $I64 x y)) ;; Specialized lowerings for `(bxor x (bnot y))` which is additionally produced ;; by Cranelift's `bxor_not` instruction that is legalized into the simpler ;; forms early on. -(rule bxor_not_right 1 (lower (has_type (fits_in_64 (ty_int ty)) (bxor x (bnot y)))) +(rule bxor_not_right 1 (lower (has_type (fits_in_64 (ty_int ty)) (bxor _ x (bnot _ y)))) (alu_rs_imm_logic (ALUOp.EorNot) ty x y)) -(rule bxor_not_left 2 (lower (has_type (fits_in_64 (ty_int ty)) (bxor (bnot y) x))) +(rule bxor_not_left 2 (lower (has_type (fits_in_64 (ty_int ty)) (bxor _ (bnot _ y) x))) (alu_rs_imm_logic (ALUOp.EorNot) ty x y)) -(rule 3 (lower (has_type $I128 (bxor x (bnot y)))) (i128_alu_bitop (ALUOp.EorNot) $I64 x y)) -(rule 4 (lower (has_type $I128 (bxor (bnot y) x))) (i128_alu_bitop (ALUOp.EorNot) $I64 x y)) +(rule 3 (lower (has_type $I128 (bxor _ x (bnot _ y)))) (i128_alu_bitop (ALUOp.EorNot) $I64 x y)) +(rule 4 (lower (has_type $I128 (bxor _ (bnot _ y) x))) (i128_alu_bitop (ALUOp.EorNot) $I64 x y)) ;;;; Rules for `ishl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Shift for i8/i16/i32. -(rule ishl_fits_in_32 -1 (lower (has_type (fits_in_32 ty) (ishl x y))) +(rule ishl_fits_in_32 -1 (lower (has_type (fits_in_32 ty) (ishl _ x y))) (do_shift (ALUOp.Lsl) ty x y)) ;; Shift for i64. -(rule ishl_64 (lower (has_type $I64 (ishl x y))) +(rule ishl_64 (lower (has_type $I64 (ishl _ x y))) (do_shift (ALUOp.Lsl) $I64 x y)) ;; Shift for i128. -(rule (lower (has_type $I128 (ishl x y))) +(rule (lower (has_type $I128 (ishl _ x y))) (lower_shl128 x (value_regs_get y 0))) ;; lsl lo_lshift, src_lo, amt @@ -1533,12 +1542,12 @@ (csel (Cond.Ne) lo_lshift maybe_hi))))) ;; Shift for vector types. -(rule -3 (lower (has_type (ty_vec128 ty) (ishl x y))) +(rule -3 (lower (has_type (ty_vec128 ty) (ishl _ x y))) (let ((size VectorSize (vector_size ty)) (masked_shift_amt Reg (and_imm $I32 y (shift_mask ty))) (shift Reg (vec_dup masked_shift_amt size))) (sshl x shift size))) -(rule -2 (lower (has_type (ty_vec128 ty) (ishl x (iconst (u64_from_imm64 n))))) +(rule -2 (lower (has_type (ty_vec128 ty) (ishl _ x (iconst _ (u64_from_imm64 n))))) (ushl_vec_imm x (shift_masked_imm ty n) (vector_size ty))) (decl pure shift_masked_imm (Type u64) u8) @@ -1619,22 +1628,22 @@ ;; Note that this rule explicitly has a higher priority than the others ;; to ensure it's attempted first, otherwise the type-based filters on the ;; previous rules seem to take priority over this rule. -(rule do_shift_imm 1 (do_shift op ty x (iconst k)) +(rule do_shift_imm 1 (do_shift op ty x (iconst _ k)) (if-let shift (imm_shift_from_imm64 ty k)) (alu_rr_imm_shift op ty x shift)) ;;;; Rules for `ushr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Shift for i8/i16/i32. -(rule ushr_fits_in_32 -1 (lower (has_type (fits_in_32 ty) (ushr x y))) +(rule ushr_fits_in_32 -1 (lower (has_type (fits_in_32 ty) (ushr _ x y))) (do_shift (ALUOp.Lsr) ty (put_in_reg_zext32 x) y)) ;; Shift for i64. -(rule ushr_64 (lower (has_type $I64 (ushr x y))) +(rule ushr_64 (lower (has_type $I64 (ushr _ x y))) (do_shift (ALUOp.Lsr) $I64 (put_in_reg_zext64 x) y)) ;; Shift for i128. -(rule (lower (has_type $I128 (ushr x y))) +(rule (lower (has_type $I128 (ushr _ x y))) (lower_ushr128 x (value_regs_get y 0))) ;; Vector shifts. @@ -1642,14 +1651,14 @@ ;; Note that for constant shifts a 0-width shift can't be emitted so it's ;; special cased to pass through the input as-is since a 0-shift doesn't modify ;; the input anyway. -(rule -4 (lower (has_type (ty_vec128 ty) (ushr x y))) +(rule -4 (lower (has_type (ty_vec128 ty) (ushr _ x y))) (let ((size VectorSize (vector_size ty)) (masked_shift_amt Reg (and_imm $I32 y (shift_mask ty))) (shift Reg (vec_dup (sub $I64 (zero_reg) masked_shift_amt) size))) (ushl x shift size))) -(rule -3 (lower (has_type (ty_vec128 ty) (ushr x (iconst (u64_from_imm64 n))))) +(rule -3 (lower (has_type (ty_vec128 ty) (ushr _ x (iconst _ (u64_from_imm64 n))))) (ushr_vec_imm x (shift_masked_imm ty n) (vector_size ty))) -(rule -2 (lower (has_type (ty_vec128 ty) (ushr x (iconst (u64_from_imm64 n))))) +(rule -2 (lower (has_type (ty_vec128 ty) (ushr _ x (iconst _ (u64_from_imm64 n))))) (if-let 0 (shift_masked_imm ty n)) x) @@ -1683,15 +1692,15 @@ ;;;; Rules for `sshr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Shift for i8/i16/i32. -(rule sshr_fits_in_32 -4 (lower (has_type (fits_in_32 ty) (sshr x y))) +(rule sshr_fits_in_32 -4 (lower (has_type (fits_in_32 ty) (sshr _ x y))) (do_shift (ALUOp.Asr) ty (put_in_reg_sext32 x) y)) ;; Shift for i64. -(rule sshr_64 (lower (has_type $I64 (sshr x y))) +(rule sshr_64 (lower (has_type $I64 (sshr _ x y))) (do_shift (ALUOp.Asr) $I64 (put_in_reg_sext64 x) y)) ;; Shift for i128. -(rule (lower (has_type $I128 (sshr x y))) +(rule (lower (has_type $I128 (sshr _ x y))) (lower_sshr128 x (value_regs_get y 0))) ;; Vector shifts. @@ -1700,14 +1709,14 @@ ;; that for constant shifts a 0-width shift can't be emitted so it's special ;; cased to pass through the input as-is since a 0-shift doesn't modify the ;; input anyway. -(rule -3 (lower (has_type (ty_vec128 ty) (sshr x y))) +(rule -3 (lower (has_type (ty_vec128 ty) (sshr _ x y))) (let ((size VectorSize (vector_size ty)) (masked_shift_amt Reg (and_imm $I32 y (shift_mask ty))) (shift Reg (vec_dup (sub $I64 (zero_reg) masked_shift_amt) size))) (sshl x shift size))) -(rule -2 (lower (has_type (ty_vec128 ty) (sshr x (iconst (u64_from_imm64 n))))) +(rule -2 (lower (has_type (ty_vec128 ty) (sshr _ x (iconst _ (u64_from_imm64 n))))) (sshr_vec_imm x (shift_masked_imm ty n) (vector_size ty))) -(rule -1 (lower (has_type (ty_vec128 ty) (sshr x (iconst (u64_from_imm64 n))))) +(rule -1 (lower (has_type (ty_vec128 ty) (sshr _ x (iconst _ (u64_from_imm64 n))))) (if-let 0 (shift_masked_imm ty n)) x) @@ -1743,13 +1752,13 @@ ;;;; Rules for `rotl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; General 8/16-bit case. -(rule rotl_fits_in_16 -2 (lower (has_type (fits_in_16 ty) (rotl x y))) +(rule rotl_fits_in_16 -2 (lower (has_type (fits_in_16 ty) (rotl _ x y))) (let ((amt Reg (value_regs_get y 0)) (neg_shift Reg (sub $I32 (zero_reg) amt))) (small_rotr ty (put_in_reg_zext32 x) neg_shift))) ;; Specialization for the 8/16-bit case when the rotation amount is an immediate. -(rule rotl_fits_in_16_imm -1 (lower (has_type (fits_in_16 ty) (rotl x (iconst k)))) +(rule rotl_fits_in_16_imm -1 (lower (has_type (fits_in_16 ty) (rotl _ x (iconst _ k)))) (if-let n (imm_shift_from_imm64 ty k)) (small_rotr_imm ty (put_in_reg_zext32 x) (negate_imm_shift ty n))) @@ -1762,24 +1771,24 @@ ;; amount. ;; General 32-bit case. -(rule rotl_32_base_case (lower (has_type $I32 (rotl x y))) +(rule rotl_32_base_case (lower (has_type $I32 (rotl _ x y))) (let ((amt Reg (value_regs_get y 0)) (neg_shift Reg (sub $I32 (zero_reg) amt))) (a64_rotr $I32 x neg_shift))) ;; General 64-bit case. -(rule rotl_64_base_case (lower (has_type $I64 (rotl x y))) +(rule rotl_64_base_case (lower (has_type $I64 (rotl _ x y))) (let ((amt Reg (value_regs_get y 0)) (neg_shift Reg (sub $I64 (zero_reg) amt))) (a64_rotr $I64 x neg_shift))) ;; Specialization for the 32-bit case when the rotation amount is an immediate. -(rule rotl_32_imm 1 (lower (has_type $I32 (rotl x (iconst k)))) +(rule rotl_32_imm 1 (lower (has_type $I32 (rotl _ x (iconst _ k)))) (if-let n (imm_shift_from_imm64 $I32 k)) (a64_rotr_imm $I32 x (negate_imm_shift $I32 n))) ;; Specialization for the 64-bit case when the rotation amount is an immediate. -(rule rotl_64_imm 1 (lower (has_type $I64 (rotl x (iconst k)))) +(rule rotl_64_imm 1 (lower (has_type $I64 (rotl _ x (iconst _ k)))) (if-let n (imm_shift_from_imm64 $I64 k)) (a64_rotr_imm $I64 x (negate_imm_shift $I64 n))) @@ -1798,7 +1807,7 @@ ;; General 128-bit case. ;; ;; TODO: much better codegen is possible with a constant amount. -(rule (lower (has_type $I128 (rotl x y))) +(rule (lower (has_type $I128 (rotl _ x y))) (let ((val ValueRegs x) (amt Reg (value_regs_get y 0)) (neg_amt Reg (sub $I64 (imm $I64 (ImmExtend.Zero) 128) amt)) @@ -1811,29 +1820,29 @@ ;;;; Rules for `rotr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; General 8/16-bit case. -(rule rotr_fits_in_16 -3 (lower (has_type (fits_in_16 ty) (rotr x y))) +(rule rotr_fits_in_16 -3 (lower (has_type (fits_in_16 ty) (rotr _ x y))) (small_rotr ty (put_in_reg_zext32 x) (value_regs_get y 0))) ;; General 32-bit case. -(rule rotr_32_base_case -1 (lower (has_type $I32 (rotr x y))) +(rule rotr_32_base_case -1 (lower (has_type $I32 (rotr _ x y))) (a64_rotr $I32 x (value_regs_get y 0))) ;; General 64-bit case. -(rule rotr_64_base_case -1 (lower (has_type $I64 (rotr x y))) +(rule rotr_64_base_case -1 (lower (has_type $I64 (rotr _ x y))) (a64_rotr $I64 x (value_regs_get y 0))) ;; Specialization for the 8/16-bit case when the rotation amount is an immediate. -(rule rotr_fits_in_16_imm -2 (lower (has_type (fits_in_16 ty) (rotr x (iconst k)))) +(rule rotr_fits_in_16_imm -2 (lower (has_type (fits_in_16 ty) (rotr _ x (iconst _ k)))) (if-let n (imm_shift_from_imm64 ty k)) (small_rotr_imm ty (put_in_reg_zext32 x) n)) ;; Specialization for the 32-bit case when the rotation amount is an immediate. -(rule rotr_32_imm (lower (has_type $I32 (rotr x (iconst k)))) +(rule rotr_32_imm (lower (has_type $I32 (rotr _ x (iconst _ k)))) (if-let n (imm_shift_from_imm64 $I32 k)) (a64_rotr_imm $I32 x n)) ;; Specialization for the 64-bit case when the rotation amount is an immediate. -(rule rotr_64_imm (lower (has_type $I64 (rotr x (iconst k)))) +(rule rotr_64_imm (lower (has_type $I64 (rotr _ x (iconst _ k)))) (if-let n (imm_shift_from_imm64 $I64 k)) (a64_rotr_imm $I64 x n)) @@ -1914,7 +1923,7 @@ ;; General 128-bit case. ;; ;; TODO: much better codegen is possible with a constant amount. -(rule (lower (has_type $I128 (rotr x y))) +(rule (lower (has_type $I128 (rotr _ x y))) (let ((val ValueRegs x) (amt Reg (value_regs_get y 0)) (neg_amt Reg (sub $I64 (imm $I64 (ImmExtend.Zero) 128) amt)) @@ -1929,37 +1938,37 @@ ;; Reversing an 8-bit value with a 32-bit bitrev instruction will place ;; the reversed result in the highest 8 bits, so we need to shift them down into ;; place. -(rule (lower (has_type $I8 (bitrev x))) +(rule (lower (has_type $I8 (bitrev _ x))) (lsr_imm $I32 (rbit $I32 x) (imm_shift_from_u8 24))) ;; Reversing an 16-bit value with a 32-bit bitrev instruction will place ;; the reversed result in the highest 16 bits, so we need to shift them down into ;; place. -(rule (lower (has_type $I16 (bitrev x))) +(rule (lower (has_type $I16 (bitrev _ x))) (lsr_imm $I32 (rbit $I32 x) (imm_shift_from_u8 16))) -(rule (lower (has_type $I128 (bitrev x))) +(rule (lower (has_type $I128 (bitrev _ x))) (let ((val ValueRegs x) (lo_rev Reg (rbit $I64 (value_regs_get val 0))) (hi_rev Reg (rbit $I64 (value_regs_get val 1)))) (value_regs hi_rev lo_rev))) -(rule -1 (lower (has_type ty (bitrev x))) +(rule -1 (lower (has_type ty (bitrev _ x))) (rbit ty x)) ;;;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule clz_8 (lower (has_type $I8 (clz x))) +(rule clz_8 (lower (has_type $I8 (clz _ x))) (sub_imm $I32 (a64_clz $I32 (put_in_reg_zext32 x)) (u8_into_imm12 24))) -(rule clz_16 (lower (has_type $I16 (clz x))) +(rule clz_16 (lower (has_type $I16 (clz _ x))) (sub_imm $I32 (a64_clz $I32 (put_in_reg_zext32 x)) (u8_into_imm12 16))) -(rule (lower (has_type $I128 (clz x))) +(rule (lower (has_type $I128 (clz _ x))) (lower_clz128 x)) -(rule clz_32_64 -1 (lower (has_type ty (clz x))) +(rule clz_32_64 -1 (lower (has_type ty (clz _ x))) (a64_clz ty x)) ;; clz hi_clz, hi @@ -1980,27 +1989,27 @@ ;; then using a `clz` instruction since the tail zeros are the same as the ;; leading zeros of the reversed value. -(rule ctz_8 (lower (has_type $I8 (ctz x))) +(rule ctz_8 (lower (has_type $I8 (ctz _ x))) (a64_clz $I32 (orr_imm $I32 (rbit $I32 x) (u64_into_imm_logic $I32 0x800000)))) -(rule ctz_16 (lower (has_type $I16 (ctz x))) +(rule ctz_16 (lower (has_type $I16 (ctz _ x))) (a64_clz $I32 (orr_imm $I32 (rbit $I32 x) (u64_into_imm_logic $I32 0x8000)))) -(rule (lower (has_type $I128 (ctz x))) +(rule (lower (has_type $I128 (ctz _ x))) (let ((val ValueRegs x) (lo Reg (rbit $I64 (value_regs_get val 0))) (hi Reg (rbit $I64 (value_regs_get val 1)))) (lower_clz128 (value_regs hi lo)))) -(rule ctz_32_64 -1 (lower (has_type ty (ctz x))) +(rule ctz_32_64 -1 (lower (has_type ty (ctz _ x))) (a64_clz ty (rbit ty x))) ;;;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule cls_8 (lower (has_type $I8 (cls x))) +(rule cls_8 (lower (has_type $I8 (cls _ x))) (sub_imm $I32 (a64_cls $I32 (put_in_reg_sext32 x)) (u8_into_imm12 24))) -(rule cls_16 (lower (has_type $I16 (cls x))) +(rule cls_16 (lower (has_type $I16 (cls _ x))) (sub_imm $I32 (a64_cls $I32 (put_in_reg_sext32 x)) (u8_into_imm12 16))) ;; cls lo_cls, lo @@ -2012,7 +2021,7 @@ ;; csel maybe_lo, lo_sign_bits, xzr, eq ;; add out_lo, maybe_lo, hi_cls ;; mov out_hi, 0 -(rule (lower (has_type $I128 (cls x))) +(rule (lower (has_type $I128 (cls _ x))) (let ((val ValueRegs x) (lo Reg (value_regs_get val 0)) (hi Reg (value_regs_get val 1)) @@ -2026,21 +2035,21 @@ (csel (Cond.Eq) lo_sign_bits (zero_reg))))) (value_regs (add $I64 maybe_lo hi_cls) (imm $I64 (ImmExtend.Zero) 0)))) -(rule cls_32_64 -1 (lower (has_type ty (cls x))) +(rule cls_32_64 -1 (lower (has_type ty (cls _ x))) (a64_cls ty x)) ;;;; Rules for `bswap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I16 (bswap x))) +(rule (lower (has_type $I16 (bswap _ x))) (a64_rev16 $I16 x)) -(rule (lower (has_type $I32 (bswap x))) +(rule (lower (has_type $I32 (bswap _ x))) (a64_rev32 $I32 x)) -(rule (lower (has_type $I64 (bswap x))) +(rule (lower (has_type $I64 (bswap _ x))) (a64_rev64 $I64 x)) -(rule (lower (has_type $I128 (bswap x))) +(rule (lower (has_type $I128 (bswap _ x))) (value_regs (a64_rev64 $I64 (value_regs_get x 1)) (a64_rev64 $I64 (value_regs_get x 0)))) @@ -2048,7 +2057,7 @@ ;;;; Rules for `bmask` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Bmask tests the value against zero, and uses `csetm` to assert the result. -(rule (lower (has_type out_ty (bmask x @ (value_type in_ty)))) +(rule (lower (has_type out_ty (bmask _ x @ (value_type in_ty)))) (lower_bmask out_ty in_ty x)) ;;;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2070,31 +2079,31 @@ ;; if ty == i128: ;; mov out_hi, 0 -(rule popcnt_8 (lower (has_type $I8 (popcnt x))) +(rule popcnt_8 (lower (has_type $I8 (popcnt _ x))) (let ((tmp Reg (mov_to_fpu x (ScalarSize.Size32))) (nbits Reg (vec_cnt tmp (VectorSize.Size8x8)))) (mov_from_vec nbits 0 (ScalarSize.Size8)))) ;; Note that this uses `addp` instead of `addv` as it's usually cheaper. -(rule popcnt_16 (lower (has_type $I16 (popcnt x))) +(rule popcnt_16 (lower (has_type $I16 (popcnt _ x))) (let ((tmp Reg (mov_to_fpu x (ScalarSize.Size32))) (nbits Reg (vec_cnt tmp (VectorSize.Size8x8))) (added Reg (addp nbits nbits (VectorSize.Size8x8)))) (mov_from_vec added 0 (ScalarSize.Size8)))) -(rule popcnt_32 (lower (has_type $I32 (popcnt x))) +(rule popcnt_32 (lower (has_type $I32 (popcnt _ x))) (let ((tmp Reg (mov_to_fpu x (ScalarSize.Size32))) (nbits Reg (vec_cnt tmp (VectorSize.Size8x8))) (added Reg (addv nbits (VectorSize.Size8x8)))) (mov_from_vec added 0 (ScalarSize.Size8)))) -(rule popcnt_64 (lower (has_type $I64 (popcnt x))) +(rule popcnt_64 (lower (has_type $I64 (popcnt _ x))) (let ((tmp Reg (mov_to_fpu x (ScalarSize.Size64))) (nbits Reg (vec_cnt tmp (VectorSize.Size8x8))) (added Reg (addv nbits (VectorSize.Size8x8)))) (mov_from_vec added 0 (ScalarSize.Size8)))) -(rule (lower (has_type $I128 (popcnt x))) +(rule (lower (has_type $I128 (popcnt _ x))) (let ((val ValueRegs x) (tmp_half Reg (mov_to_fpu (value_regs_get val 0) (ScalarSize.Size64))) (tmp Reg (mov_to_vec tmp_half (value_regs_get val 1) 1 (VectorSize.Size64x2))) @@ -2102,18 +2111,18 @@ (added Reg (addv nbits (VectorSize.Size8x16)))) (value_regs (mov_from_vec added 0 (ScalarSize.Size8)) (imm $I64 (ImmExtend.Zero) 0)))) -(rule (lower (has_type $I8X16 (popcnt x))) +(rule (lower (has_type $I8X16 (popcnt _ x))) (vec_cnt x (VectorSize.Size8x16))) ;;;; Rules for `bitselect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule bitselect (lower (has_type ty (bitselect c x y))) +(rule bitselect (lower (has_type ty (bitselect _ c x y))) (if (ty_int_ref_scalar_64 ty)) (let ((tmp1 Reg (and_reg ty x c)) (tmp2 Reg (bic ty y c))) (orr ty tmp1 tmp2))) -(rule 1 (lower (has_type (ty_vec128 ty) (bitselect c x y))) +(rule 1 (lower (has_type (ty_vec128 ty) (bitselect _ c x y))) (bsl ty c x y)) ;;;; Rules for `ireduce` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2121,72 +2130,72 @@ ;; T -> I{64,32,16,8}: We can simply pass through the value: values ;; are always stored with high bits undefined, so we can just leave ;; them be. -(rule (lower (has_type ty (ireduce src))) +(rule (lower (has_type ty (ireduce _ src))) (if (ty_int_ref_scalar_64 ty)) (value_regs_get src 0)) ;;;; Rules for `fcmp` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 4 (lower (has_type ty @ (multi_lane _ _) (fcmp (fcmp_zero_cond_not_eq cond) x y))) +(rule 4 (lower (has_type ty @ (multi_lane _ _) (fcmp _ (fcmp_zero_cond_not_eq cond) x y))) (if (zero_value y)) (let ((rn Reg x) (vec_size VectorSize (vector_size ty))) (value_reg (not (fcmeq0 rn vec_size) vec_size)))) -(rule 3 (lower (has_type ty @ (multi_lane _ _) (fcmp (fcmp_zero_cond cond) x y))) +(rule 3 (lower (has_type ty @ (multi_lane _ _) (fcmp _ (fcmp_zero_cond cond) x y))) (if (zero_value y)) (let ((rn Reg x) (vec_size VectorSize (vector_size ty))) (value_reg (float_cmp_zero cond rn vec_size)))) -(rule 2 (lower (has_type ty @ (multi_lane _ _) (fcmp (fcmp_zero_cond_not_eq cond) x y))) +(rule 2 (lower (has_type ty @ (multi_lane _ _) (fcmp _ (fcmp_zero_cond_not_eq cond) x y))) (if (zero_value x)) (let ((rn Reg y) (vec_size VectorSize (vector_size ty))) (value_reg (not (fcmeq0 rn vec_size) vec_size)))) -(rule 1 (lower (has_type ty @ (multi_lane _ _) (fcmp (fcmp_zero_cond cond) x y))) +(rule 1 (lower (has_type ty @ (multi_lane _ _) (fcmp _ (fcmp_zero_cond cond) x y))) (if (zero_value x)) (let ((rn Reg y) (vec_size VectorSize (vector_size ty))) (value_reg (float_cmp_zero_swap cond rn vec_size)))) (rule 0 (lower (has_type out_ty - (fcmp cond x @ (value_type (ty_scalar_float in_ty)) y))) + (fcmp _ cond x @ (value_type (ty_scalar_float in_ty)) y))) (with_flags (fpu_cmp (scalar_size in_ty) x y) (materialize_bool_result (fp_cond_code cond)))) -(rule -1 (lower (has_type out_ty (fcmp cond x @ (value_type in_ty) y))) +(rule -1 (lower (has_type out_ty (fcmp _ cond x @ (value_type in_ty) y))) (if (ty_vector_float in_ty)) (vec_cmp x y in_ty (fp_cond_code cond))) ;;;; Rules for `icmp` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 3 (lower (has_type ty @ (multi_lane _ _) (icmp (icmp_zero_cond_not_eq cond) x y))) +(rule 3 (lower (has_type ty @ (multi_lane _ _) (icmp _ (icmp_zero_cond_not_eq cond) x y))) (if (zero_value y)) (let ((rn Reg x) (vec_size VectorSize (vector_size ty))) (value_reg (not (cmeq0 rn vec_size) vec_size)))) -(rule 2 (lower (has_type ty @ (multi_lane _ _) (icmp (icmp_zero_cond cond) x y))) +(rule 2 (lower (has_type ty @ (multi_lane _ _) (icmp _ (icmp_zero_cond cond) x y))) (if (zero_value y)) (let ((rn Reg x) (vec_size VectorSize (vector_size ty))) (value_reg (int_cmp_zero cond rn vec_size)))) -(rule 1 (lower (has_type ty @ (multi_lane _ _) (icmp (icmp_zero_cond_not_eq cond) x y))) +(rule 1 (lower (has_type ty @ (multi_lane _ _) (icmp _ (icmp_zero_cond_not_eq cond) x y))) (if (zero_value x)) (let ((rn Reg y) (vec_size VectorSize (vector_size ty))) (value_reg (not (cmeq0 rn vec_size) vec_size)))) -(rule 0 (lower (has_type ty @ (multi_lane _ _) (icmp (icmp_zero_cond cond) x y))) +(rule 0 (lower (has_type ty @ (multi_lane _ _) (icmp _ (icmp_zero_cond cond) x y))) (if (zero_value x)) (let ((rn Reg y) (vec_size VectorSize (vector_size ty))) (value_reg (int_cmp_zero_swap cond rn vec_size)))) -(rule icmp_8_16_32_64 -1 (lower (icmp cond x @ (value_type in_ty) y)) +(rule icmp_8_16_32_64 -1 (lower (icmp _ cond x @ (value_type in_ty) y)) (lower_icmp_into_reg cond x y in_ty $I8)) ;;;; Rules for `trap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2207,7 +2216,7 @@ ;;;; Rules for `select` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type ty - (select (maybe_uextend (icmp cc + (select _ (maybe_uextend (icmp _ cc x @ (value_type in_ty) y)) rn @@ -2220,7 +2229,7 @@ rm))) (rule (lower (has_type ty - (select (maybe_uextend (fcmp cc x @ (value_type in_ty) y)) + (select _ (maybe_uextend (fcmp _ cc x @ (value_type in_ty) y)) rn rm))) (let ((cond Cond (fp_cond_code cc))) @@ -2228,25 +2237,25 @@ (fpu_cmp (scalar_size in_ty) x y) cond ty rn rm))) -(rule -1 (lower (has_type ty (select rcond @ (value_type $I8) rn rm))) +(rule -1 (lower (has_type ty (select _ rcond @ (value_type $I8) rn rm))) (let ((rcond Reg rcond)) (lower_select (tst_imm $I32 rcond (u64_into_imm_logic $I32 255)) (Cond.Ne) ty rn rm))) -(rule -2 (lower (has_type ty (select rcond @ (value_type (fits_in_32 _)) rn rm))) +(rule -2 (lower (has_type ty (select _ rcond @ (value_type (fits_in_32 _)) rn rm))) (let ((rcond Reg (put_in_reg_zext32 rcond))) (lower_select (cmp (OperandSize.Size32) rcond (zero_reg)) (Cond.Ne) ty rn rm))) -(rule -3 (lower (has_type ty (select rcond @ (value_type (fits_in_64 _)) rn rm))) +(rule -3 (lower (has_type ty (select _ rcond @ (value_type (fits_in_64 _)) rn rm))) (let ((rcond Reg (put_in_reg_zext64 rcond))) (lower_select (cmp (OperandSize.Size64) rcond (zero_reg)) (Cond.Ne) ty rn rm))) -(rule -4 (lower (has_type ty (select rcond @ (value_type $I128) rn rm))) +(rule -4 (lower (has_type ty (select _ rcond @ (value_type $I128) rn rm))) (let ((c ValueRegs (put_in_regs rcond)) (c_lo Reg (value_regs_get c 0)) (c_hi Reg (value_regs_get c 1)) @@ -2257,8 +2266,16 @@ ;;;; Rules for `select_spectre_guard` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(decl maybe_csdb_after_select (ValueRegs) ValueRegs) +(rule (maybe_csdb_after_select dst) + (if-let true (use_csdb)) + (let ((_ InstOutput (side_effect (csdb)))) dst)) +(rule (maybe_csdb_after_select dst) + (if-let false (use_csdb)) + dst) + (rule (lower (has_type ty - (select_spectre_guard (maybe_uextend (icmp cc x @ (value_type in_ty) y)) + (select_spectre_guard _ (maybe_uextend (icmp _ cc x @ (value_type in_ty) y)) if_true if_false))) (let ((comparison FlagsAndCC (lower_icmp_into_flags cc x y in_ty)) @@ -2267,17 +2284,16 @@ (cond_code (flags_and_cc_cc comparison)) ty if_true - if_false)) - (_ InstOutput (side_effect (csdb)))) - dst)) + if_false))) + (maybe_csdb_after_select dst))) -(rule -1 (lower (has_type ty (select_spectre_guard rcond @ (value_type (fits_in_64 _)) rn rm))) +(rule -1 (lower (has_type ty (select_spectre_guard _ rcond @ (value_type (fits_in_64 _)) rn rm))) (let ((rcond Reg (put_in_reg_zext64 rcond))) (lower_select (cmp (OperandSize.Size64) rcond (zero_reg)) (Cond.Ne) ty rn rm))) -(rule -2 (lower (has_type ty (select_spectre_guard rcond @ (value_type $I128) rn rm))) +(rule -2 (lower (has_type ty (select_spectre_guard _ rcond @ (value_type $I128) rn rm))) (let ((c ValueRegs (put_in_regs rcond)) (c_lo Reg (value_regs_get c 0)) (c_hi Reg (value_regs_get c 1)) @@ -2288,41 +2304,38 @@ ;;;; Rules for `vconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_vec128 _) (vconst (u128_from_constant x)))) +(rule (lower (has_type (ty_vec128 _) (vconst _ (u128_from_constant x)))) (constant_f128 x)) -(rule 1 (lower (has_type ty (vconst (u64_from_constant x)))) +(rule 1 (lower (has_type ty (vconst _ (u64_from_constant x)))) (if (ty_vec64 ty)) (constant_f64 x)) ;;;; Rules for `splat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty (splat x @ (value_type in_ty)))) +(rule -1 (lower (has_type ty (splat _ x @ (value_type in_ty)))) (if (ty_int_ref_scalar_64 in_ty)) (vec_dup x (vector_size ty))) -(rule -2 (lower (has_type ty (splat x @ (value_type (ty_scalar_float _))))) +(rule -2 (lower (has_type ty (splat _ x @ (value_type (ty_scalar_float _))))) (vec_dup_from_fpu x (vector_size ty) 0)) -(rule (lower (has_type ty (splat (f32const (u32_from_ieee32 n))))) - (splat_const n (vector_size ty))) - -(rule (lower (has_type ty (splat (f64const (u64_from_ieee64 n))))) +(rule (lower (has_type ty (splat _ (f32const _ (u32_from_ieee32 n))))) (splat_const n (vector_size ty))) -(rule (lower (has_type ty (splat (iconst (u64_from_imm64 n))))) +(rule (lower (has_type ty (splat _ (f64const _ (u64_from_ieee64 n))))) (splat_const n (vector_size ty))) -(rule (lower (has_type ty (splat (ireduce (iconst (u64_from_imm64 n)))))) +(rule (lower (has_type ty (splat _ (iconst _ (u64_from_imm64 n))))) (splat_const n (vector_size ty))) -(rule (lower (has_type ty (splat x @ (load flags _ _)))) +(rule (lower (has_type ty (splat _ x @ (load _ flags _ _)))) (if-let mem_op (is_sinkable_inst x)) (let ((addr Reg (sink_load_into_addr (lane_type ty) mem_op))) (ld1r addr (vector_size ty) flags))) ;;;; Rules for `AtomicLoad` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (valid_atomic_transaction ty) (atomic_load (little_or_native_endian flags) addr))) +(rule (lower (has_type (valid_atomic_transaction ty) (atomic_load _ (little_or_native_endian flags) addr))) (load_acquire ty flags addr)) @@ -2336,116 +2349,116 @@ (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Add) addr src)))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Add) addr src)))) (lse_atomic_rmw (AtomicRMWOp.Add) addr src ty flags)) (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Xor) addr src)))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Xor) addr src)))) (lse_atomic_rmw (AtomicRMWOp.Eor) addr src ty flags)) (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Or) addr src)))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Or) addr src)))) (lse_atomic_rmw (AtomicRMWOp.Set) addr src ty flags)) (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Smax) addr src)))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Smax) addr src)))) (lse_atomic_rmw (AtomicRMWOp.Smax) addr src ty flags)) (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Smin) addr src)))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Smin) addr src)))) (lse_atomic_rmw (AtomicRMWOp.Smin) addr src ty flags)) (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Umax) addr src)))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Umax) addr src)))) (lse_atomic_rmw (AtomicRMWOp.Umax) addr src ty flags)) (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Umin) addr src)))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Umin) addr src)))) (lse_atomic_rmw (AtomicRMWOp.Umin) addr src ty flags)) (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Sub) addr src)))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Sub) addr src)))) (lse_atomic_rmw (AtomicRMWOp.Add) addr (sub ty (zero_reg) src) ty flags)) (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.And) addr src)))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.And) addr src)))) (lse_atomic_rmw (AtomicRMWOp.Clr) addr (eon ty src (zero_reg)) ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Add) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Add) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Add) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Sub) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Sub) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Sub) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.And) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.And) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.And) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Nand) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Nand) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Nand) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Or) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Or) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Orr) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Xor) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Xor) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Eor) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Smin) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Smin) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Smin) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Smax) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Smax) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Smax) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Umin) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Umin) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Umin) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Umax) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Umax) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Umax) addr src ty flags)) (rule (lower (has_type (valid_atomic_transaction ty) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Xchg) addr src))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Xchg) addr src))) (atomic_rmw_loop (AtomicRMWLoopOp.Xchg) addr src ty flags)) ;;;; Rules for `AtomicCAS` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule 1 (lower (and (use_lse) (has_type (valid_atomic_transaction ty) - (atomic_cas (little_or_native_endian flags) addr src1 src2)))) + (atomic_cas _ (little_or_native_endian flags) addr src1 src2)))) (lse_atomic_cas addr src1 src2 ty flags)) (rule (lower (and (has_type (valid_atomic_transaction ty) - (atomic_cas (little_or_native_endian flags) addr src1 src2)))) + (atomic_cas _ (little_or_native_endian flags) addr src1 src2)))) (atomic_cas_loop addr src1 src2 ty flags)) ;;;; Rules for 'fvdemote' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (fvdemote x)) +(rule (lower (fvdemote _ x)) (fcvtn x (ScalarSize.Size32))) ;;;; Rules for `snarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type (ty_vec128_int ty) (snarrow x y))) +(rule 1 (lower (has_type (ty_vec128_int ty) (snarrow _ x y))) (if (zero_value y)) (sqxtn x (lane_size ty))) -(rule 2 (lower (has_type (ty_vec64_int ty) (snarrow x y))) +(rule 2 (lower (has_type (ty_vec64_int ty) (snarrow _ x y))) (let ((dst Reg (mov_vec_elem x y 1 0 (VectorSize.Size64x2)))) (sqxtn dst (lane_size ty)))) -(rule 0 (lower (has_type (ty_vec128_int ty) (snarrow x y))) +(rule 0 (lower (has_type (ty_vec128_int ty) (snarrow _ x y))) (let ((low_half Reg (sqxtn x (lane_size ty))) (result Reg (sqxtn2 low_half y (lane_size ty)))) result)) ;;;; Rules for `unarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type (ty_vec128_int ty) (unarrow x y))) +(rule 1 (lower (has_type (ty_vec128_int ty) (unarrow _ x y))) (if (zero_value y)) (sqxtun x (lane_size ty))) -(rule 2 (lower (has_type (ty_vec64_int ty) (unarrow x y))) +(rule 2 (lower (has_type (ty_vec64_int ty) (unarrow _ x y))) (let ((dst Reg (mov_vec_elem x y 1 0 (VectorSize.Size64x2)))) (sqxtun dst (lane_size ty)))) -(rule 0 (lower (has_type (ty_vec128_int ty) (unarrow x y))) +(rule 0 (lower (has_type (ty_vec128_int ty) (unarrow _ x y))) (let ((low_half Reg (sqxtun x (lane_size ty))) (result Reg (sqxtun2 low_half y (lane_size ty)))) result)) @@ -2453,45 +2466,45 @@ ;;;; Rules for `uunarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type (ty_vec128_int ty) (uunarrow x y))) +(rule 1 (lower (has_type (ty_vec128_int ty) (uunarrow _ x y))) (if (zero_value y)) (uqxtn x (lane_size ty))) -(rule 2 (lower (has_type (ty_vec64_int ty) (uunarrow x y))) +(rule 2 (lower (has_type (ty_vec64_int ty) (uunarrow _ x y))) (let ((dst Reg (mov_vec_elem x y 1 0 (VectorSize.Size64x2)))) (uqxtn dst (lane_size ty)))) -(rule 0 (lower (has_type (ty_vec128_int ty) (uunarrow x y))) +(rule 0 (lower (has_type (ty_vec128_int ty) (uunarrow _ x y))) (let ((low_half Reg (uqxtn x (lane_size ty))) (result Reg (uqxtn2 low_half y (lane_size ty)))) result)) ;;;; Rules for `swiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (swiden_low x))) +(rule (lower (has_type ty (swiden_low _ x))) (vec_extend (VecExtendOp.Sxtl) x false (lane_size ty))) ;;;; Rules for `swiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type (ty_vec128 ty) (swiden_high x))) +(rule 1 (lower (has_type (ty_vec128 ty) (swiden_high _ x))) (vec_extend (VecExtendOp.Sxtl) x true (lane_size ty))) -(rule (lower (has_type ty (swiden_high x))) +(rule (lower (has_type ty (swiden_high _ x))) (if (ty_vec64 ty)) (let ((tmp Reg (fpu_move_from_vec x 1 (VectorSize.Size32x2)))) (vec_extend (VecExtendOp.Sxtl) tmp false (lane_size ty)))) ;;;; Rules for `uwiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (uwiden_low x))) +(rule (lower (has_type ty (uwiden_low _ x))) (vec_extend (VecExtendOp.Uxtl) x false (lane_size ty))) ;;;; Rules for `uwiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type (ty_vec128 ty) (uwiden_high x))) +(rule 1 (lower (has_type (ty_vec128 ty) (uwiden_high _ x))) (vec_extend (VecExtendOp.Uxtl) x true (lane_size ty))) -(rule (lower (has_type ty (uwiden_high x))) +(rule (lower (has_type ty (uwiden_high _ x))) (if (ty_vec64 ty)) (let ((tmp Reg (fpu_move_from_vec x 1 (VectorSize.Size32x2)))) (vec_extend (VecExtendOp.Uxtl) tmp false (lane_size ty)))) @@ -2508,23 +2521,23 @@ ;;;; Rules for `func_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (func_addr (func_ref_data _ extname dist _))) +(rule (lower (func_addr _ (func_ref_data _ extname dist _))) (load_ext_name (box_external_name extname) 0 dist)) ;;;; Rules for `symbol_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (symbol_value (symbol_value_data extname dist offset))) +(rule (lower (symbol_value _ (symbol_value_data extname dist offset))) (load_ext_name (box_external_name extname) offset dist)) ;;; Rules for `get_{frame,stack}_pointer` and `get_return_address` ;;;;;;;;;;;;; -(rule (lower (get_frame_pointer)) +(rule (lower (get_frame_pointer _)) (aarch64_fp)) -(rule (lower (get_stack_pointer)) +(rule (lower (get_stack_pointer _)) (aarch64_sp)) -(rule (lower (get_return_address)) +(rule (lower (get_return_address _)) (aarch64_link)) ;; Rules for `call` and `call_indirect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2627,92 +2640,92 @@ ;;;; Rules for loads ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule load_i8_aarch64_uload8 (lower - (has_type $I8 (load (little_or_native_endian flags) address offset))) + (has_type $I8 (load _ (little_or_native_endian flags) address offset))) (aarch64_uload8 (amode $I8 address offset) flags)) (rule load_i16_aarch64_uload16 (lower - (has_type $I16 (load (little_or_native_endian flags) address offset))) + (has_type $I16 (load _ (little_or_native_endian flags) address offset))) (aarch64_uload16 (amode $I16 address offset) flags)) (rule load_i32_aarch64_uload32 (lower - (has_type $I32 (load (little_or_native_endian flags) address offset))) + (has_type $I32 (load _ (little_or_native_endian flags) address offset))) (aarch64_uload32 (amode $I32 address offset) flags)) (rule load_i64_aarch64_uload64 (lower - (has_type $I64 (load (little_or_native_endian flags) address offset))) + (has_type $I64 (load _ (little_or_native_endian flags) address offset))) (aarch64_uload64 (amode $I64 address offset) flags)) (rule (lower - (has_type $I128 (load (little_or_native_endian flags) address offset))) + (has_type $I128 (load _ (little_or_native_endian flags) address offset))) (aarch64_loadp64 (pair_amode address offset) flags)) (rule -1 (lower - (has_type (ty_float_or_vec (ty_16 _)) (load (little_or_native_endian flags) address offset))) + (has_type (ty_float_or_vec (ty_16 _)) (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload16 (amode $F16 address offset) flags)) (rule -2 (lower - (has_type (ty_float_or_vec (ty_32 _)) (load (little_or_native_endian flags) address offset))) + (has_type (ty_float_or_vec (ty_32 _)) (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload32 (amode $F32 address offset) flags)) (rule -3 (lower - (has_type (ty_float_or_vec (ty_64 _)) (load (little_or_native_endian flags) address offset))) + (has_type (ty_float_or_vec (ty_64 _)) (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload64 (amode $F64 address offset) flags)) (rule -4 (lower - (has_type (ty_float_or_vec (ty_128 _)) (load (little_or_native_endian flags) address offset))) + (has_type (ty_float_or_vec (ty_128 _)) (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload128 (amode $F128 address offset) flags)) (rule -5 (lower (has_type (ty_dyn_vec64 _) - (load (little_or_native_endian flags) address offset))) + (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload64 (amode $F64 address offset) flags)) (rule -6 (lower (has_type (ty_dyn_vec128 _) - (load (little_or_native_endian flags) address offset))) + (load _ (little_or_native_endian flags) address offset))) (aarch64_fpuload128 (amode $I8X16 address offset) flags)) (rule (lower - (uload8 (little_or_native_endian flags) address offset)) + (uload8 _ (little_or_native_endian flags) address offset)) (aarch64_uload8 (amode $I8 address offset) flags)) (rule (lower - (sload8 (little_or_native_endian flags) address offset)) + (sload8 _ (little_or_native_endian flags) address offset)) (aarch64_sload8 (amode $I8 address offset) flags)) (rule (lower - (uload16 (little_or_native_endian flags) address offset)) + (uload16 _ (little_or_native_endian flags) address offset)) (aarch64_uload16 (amode $I16 address offset) flags)) (rule (lower - (sload16 (little_or_native_endian flags) address offset)) + (sload16 _ (little_or_native_endian flags) address offset)) (aarch64_sload16 (amode $I16 address offset) flags)) (rule (lower - (uload32 (little_or_native_endian flags) address offset)) + (uload32 _ (little_or_native_endian flags) address offset)) (aarch64_uload32 (amode $I32 address offset) flags)) (rule (lower - (sload32 (little_or_native_endian flags) address offset)) + (sload32 _ (little_or_native_endian flags) address offset)) (aarch64_sload32 (amode $I32 address offset) flags)) (rule (lower - (sload8x8 (little_or_native_endian flags) address offset)) + (sload8x8 _ (little_or_native_endian flags) address offset)) (vec_extend (VecExtendOp.Sxtl) (aarch64_fpuload64 (amode $F64 address offset) flags) false (ScalarSize.Size16))) (rule (lower - (uload8x8 (little_or_native_endian flags) address offset)) + (uload8x8 _ (little_or_native_endian flags) address offset)) (vec_extend (VecExtendOp.Uxtl) (aarch64_fpuload64 (amode $F64 address offset) flags) false (ScalarSize.Size16))) (rule (lower - (sload16x4 (little_or_native_endian flags) address offset)) + (sload16x4 _ (little_or_native_endian flags) address offset)) (vec_extend (VecExtendOp.Sxtl) (aarch64_fpuload64 (amode $F64 address offset) flags) false (ScalarSize.Size32))) (rule (lower - (uload16x4 (little_or_native_endian flags) address offset)) + (uload16x4 _ (little_or_native_endian flags) address offset)) (vec_extend (VecExtendOp.Uxtl) (aarch64_fpuload64 (amode $F64 address offset) flags) false (ScalarSize.Size32))) (rule (lower - (sload32x2 (little_or_native_endian flags) address offset)) + (sload32x2 _ (little_or_native_endian flags) address offset)) (vec_extend (VecExtendOp.Sxtl) (aarch64_fpuload64 (amode $F64 address offset) flags) false (ScalarSize.Size64))) (rule (lower - (uload32x2 (little_or_native_endian flags) address offset)) + (uload32x2 _ (little_or_native_endian flags) address offset)) (vec_extend (VecExtendOp.Uxtl) (aarch64_fpuload64 (amode $F64 address offset) flags) false @@ -2785,7 +2798,7 @@ ;;; Rules for `{get,set}_pinned_reg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_pinned_reg)) +(rule (lower (get_pinned_reg _)) (mov_from_preg (preg_pinned))) (rule (lower (set_pinned_reg val)) @@ -2794,66 +2807,66 @@ ;;; Rules for `bitcast` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SIMD&FP <=> SIMD&FP -(rule 7 (lower (has_type (ty_float_or_vec _) (bitcast _ x @ (value_type (ty_float_or_vec _))))) +(rule 7 (lower (has_type (ty_float_or_vec _) (bitcast _ _ x @ (value_type (ty_float_or_vec _))))) x) ; I128 => SIMD&FP -(rule 6 (lower (has_type (ty_float_or_vec _) (bitcast _ x @ (value_type $I128)))) +(rule 6 (lower (has_type (ty_float_or_vec _) (bitcast _ _ x @ (value_type $I128)))) (mov_to_vec (mov_to_fpu (value_regs_get x 0) (ScalarSize.Size64)) (value_regs_get x 1) 1 (VectorSize.Size64x2))) ; SIMD&FP => I128 -(rule 5 (lower (has_type $I128 (bitcast _ x @ (value_type (ty_float_or_vec _))))) +(rule 5 (lower (has_type $I128 (bitcast _ _ x @ (value_type (ty_float_or_vec _))))) (value_regs (mov_from_vec x 0 (ScalarSize.Size64)) (mov_from_vec x 1 (ScalarSize.Size64)))) ; GPR => SIMD&FP -(rule 4 (lower (has_type (ty_float_or_vec _) (bitcast _ x @ (value_type in_ty)))) +(rule 4 (lower (has_type (ty_float_or_vec _) (bitcast _ _ x @ (value_type in_ty)))) (if (ty_int_ref_scalar_64 in_ty)) (mov_to_fpu x (scalar_size in_ty))) ; SIMD&FP => GPR -(rule 3 (lower (has_type out_ty (bitcast _ x @ (value_type (fits_in_64 (ty_float_or_vec _)))))) +(rule 3 (lower (has_type out_ty (bitcast _ _ x @ (value_type (fits_in_64 (ty_float_or_vec _)))))) (if (ty_int_ref_scalar_64 out_ty)) (mov_from_vec x 0 (scalar_size out_ty))) ; GPR <=> GPR -(rule 1 (lower (has_type out_ty (bitcast _ x @ (value_type in_ty)))) +(rule 1 (lower (has_type out_ty (bitcast _ _ x @ (value_type in_ty)))) (if (ty_int_ref_scalar_64 out_ty)) (if (ty_int_ref_scalar_64 in_ty)) x) -(rule 0 (lower (has_type $I128 (bitcast _ x @ (value_type $I128)))) x) +(rule 0 (lower (has_type $I128 (bitcast _ _ x @ (value_type $I128)))) x) ;;; Rules for `extractlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; extractlane with lane 0 can pass through the value unchanged; upper ;; bits are undefined when a narrower type is in a wider register. -(rule 2 (lower (has_type (ty_scalar_float _) (extractlane val (u8_from_uimm8 0)))) +(rule 2 (lower (has_type (ty_scalar_float _) (extractlane _ val (u8_from_uimm8 0)))) val) (rule 0 (lower (has_type (ty_int ty) - (extractlane val + (extractlane _ val (u8_from_uimm8 lane)))) (mov_from_vec val lane (scalar_size ty))) (rule 1 (lower (has_type (ty_scalar_float ty) - (extractlane val @ (value_type vty) + (extractlane _ val @ (value_type vty) (u8_from_uimm8 lane)))) (fpu_move_from_vec val lane (vector_size vty))) ;;; Rules for `insertlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (insertlane vec @ (value_type vty) +(rule 1 (lower (insertlane _ vec @ (value_type vty) val @ (value_type (ty_int _)) (u8_from_uimm8 lane))) (mov_to_vec vec val lane (vector_size vty))) -(rule (lower (insertlane vec @ (value_type vty) +(rule (lower (insertlane _ vec @ (value_type vty) val @ (value_type (ty_scalar_float _)) (u8_from_uimm8 lane))) (mov_vec_elem vec val lane 0 (vector_size vty))) ;;; Rules for `stack_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (stack_addr stack_slot offset)) +(rule (lower (stack_addr _ stack_slot offset)) (compute_stack_addr stack_slot offset)) ;;; Rules for `vhigh_bits` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2867,7 +2880,7 @@ ;; in https://github.com/bytecodealliance/wasmtime/issues/2296, ;; although they are not used here. -(rule (lower (vhigh_bits vec @ (value_type $I8X16))) +(rule (lower (vhigh_bits _ vec @ (value_type $I8X16))) (let ( ;; Replicate the MSB of each of the 16 byte lanes across ;; the whole lane (sshr is an arithmetic right shift). @@ -2891,7 +2904,7 @@ (result Reg (addv zipped (VectorSize.Size16x8)))) (mov_from_vec result 0 (ScalarSize.Size16)))) -(rule (lower (vhigh_bits vec @ (value_type $I16X8))) +(rule (lower (vhigh_bits _ vec @ (value_type $I16X8))) (let ( ;; Replicate the MSB of each of the 8 16-bit lanes across ;; the whole lane (sshr is an arithmetic right shift). @@ -2904,7 +2917,7 @@ (result Reg (addv anded (VectorSize.Size16x8)))) (mov_from_vec result 0 (ScalarSize.Size16)))) -(rule (lower (vhigh_bits vec @ (value_type $I32X4))) +(rule (lower (vhigh_bits _ vec @ (value_type $I32X4))) (let ( ;; Replicate the MSB of each of the 4 32-bit lanes across ;; the whole lane (sshr is an arithmetic right shift). @@ -2917,7 +2930,7 @@ (result Reg (addv anded (VectorSize.Size32x4)))) (mov_from_vec result 0 (ScalarSize.Size32)))) -(rule (lower (vhigh_bits vec @ (value_type $I64X2))) +(rule (lower (vhigh_bits _ vec @ (value_type $I64X2))) (let ( ;; Grab the MSB out of each of the lanes, right-shift to ;; LSB, and add with a left-shift of upper lane's MSB back @@ -2931,7 +2944,7 @@ ;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_64 ty) (uadd_overflow_trap a b tc))) +(rule (lower (has_type (fits_in_64 ty) (uadd_overflow_trap _ a b tc))) (trap_if_overflow (add_with_flags_paired ty a b) tc)) ;;;; Helpers for `*_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3011,15 +3024,15 @@ ;; For values smaller than a register, we do a normal `add` with both arguments ;; zero extended. We then check if the output is the same as itself zero extended. -(rule 1 (lower (has_type (fits_in_16 ty) (uadd_overflow a b))) +(rule 1 (lower (has_type (fits_in_16 ty) (uadd_overflow _ a b))) (overflow_op_small ty a b (ArgumentExtension.Uext) (ALUOp.Add))) ;; For register sized add's we just emit a adds+cset, without further masking. -(rule 2 (lower (has_type (ty_32_or_64 ty) (uadd_overflow a b))) +(rule 2 (lower (has_type (ty_32_or_64 ty) (uadd_overflow _ a b))) (overflow_op_normal ty a b (ALUOp.AddS) (Cond.Hs))) ;; For 128bit integers we emit add+adcs+cset -(rule 0 (lower (has_type $I128 (uadd_overflow x y))) +(rule 0 (lower (has_type $I128 (uadd_overflow _ x y))) (overflow_op_128 x y (ALUOp.AddS) (ALUOp.AdcS) (Cond.Hs))) ;;;; Rules for `sadd_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3028,18 +3041,18 @@ ;; add out, a_ext, b, sxt{b,h} ;; cmp out, out, sxt{b,h} ;; cset of, ne -(rule 1 (lower (has_type (fits_in_16 ty) (sadd_overflow a b))) +(rule 1 (lower (has_type (fits_in_16 ty) (sadd_overflow _ a b))) (overflow_op_small ty a b (ArgumentExtension.Sext) (ALUOp.Add))) ;; adds a, b ;; cset of, vs -(rule 2 (lower (has_type (ty_32_or_64 ty) (sadd_overflow a b))) +(rule 2 (lower (has_type (ty_32_or_64 ty) (sadd_overflow _ a b))) (overflow_op_normal ty a b (ALUOp.AddS) (Cond.Vs))) ;; adds x_lo, y_lo ;; addcs x_hi, y_hi ;; cset of, vs -(rule 0 (lower (has_type $I128 (sadd_overflow x y))) +(rule 0 (lower (has_type $I128 (sadd_overflow _ x y))) (overflow_op_128 x y (ALUOp.AddS) (ALUOp.AdcS) (Cond.Vs))) ;;;; Rules for `usub_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3048,18 +3061,18 @@ ;; sub out, a_ext, b, ext{b,h} ;; cmp out, out, uxt{b,h} ;; cset of, ne -(rule 1 (lower (has_type (fits_in_16 ty) (usub_overflow a b))) +(rule 1 (lower (has_type (fits_in_16 ty) (usub_overflow _ a b))) (overflow_op_small ty a b (ArgumentExtension.Uext) (ALUOp.Sub))) ;; subs a, b ;; cset of, lo -(rule 2 (lower (has_type (ty_32_or_64 ty) (usub_overflow a b))) +(rule 2 (lower (has_type (ty_32_or_64 ty) (usub_overflow _ a b))) (overflow_op_normal ty a b (ALUOp.SubS) (Cond.Lo))) ;; subs x_lo, y_lo ;; sbcs x_hi, y_hi ;; cset of, lo -(rule 0 (lower (has_type $I128 (usub_overflow x y))) +(rule 0 (lower (has_type $I128 (usub_overflow _ x y))) (overflow_op_128 x y (ALUOp.SubS) (ALUOp.SbcS) (Cond.Lo))) ;;;; Rules for `ssub_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3068,18 +3081,18 @@ ;; sub out, a_ext, b, sxt{b,h} ;; cmp out, out, sxt{b,h} ;; cset of, ne -(rule 1 (lower (has_type (fits_in_16 ty) (ssub_overflow a b))) +(rule 1 (lower (has_type (fits_in_16 ty) (ssub_overflow _ a b))) (overflow_op_small ty a b (ArgumentExtension.Sext) (ALUOp.Sub))) ;; subs a, b ;; cset of, vs -(rule 2 (lower (has_type (ty_32_or_64 ty) (ssub_overflow a b))) +(rule 2 (lower (has_type (ty_32_or_64 ty) (ssub_overflow _ a b))) (overflow_op_normal ty a b (ALUOp.SubS) (Cond.Vs))) ;; subs x_lo, y_lo ;; sbcs x_hi, y_hi ;; cset of, vs -(rule 0 (lower (has_type $I128 (ssub_overflow x y))) +(rule 0 (lower (has_type $I128 (ssub_overflow _ x y))) (overflow_op_128 x y (ALUOp.SubS) (ALUOp.SbcS) (Cond.Vs))) ;;;; Rules for `umul_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3089,7 +3102,7 @@ ;; mul out, a_ext, b_ext ;; cmp out, out, uxt{b,h} ;; cset of, ne -(rule 1 (lower (has_type (fits_in_16 ty) (umul_overflow a b))) +(rule 1 (lower (has_type (fits_in_16 ty) (umul_overflow _ a b))) (let ((extend ExtendOp (lower_extend_op ty (ArgumentExtension.Uext))) (a_uext Reg (put_in_reg_zext32 a)) @@ -3105,7 +3118,7 @@ ;; umull out, a, b ;; cmp out, out, uxtw ;; cset of, ne -(rule 2 (lower (has_type $I32 (umul_overflow a b))) +(rule 2 (lower (has_type $I32 (umul_overflow _ a b))) (let ( (out Reg (umaddl a b (zero_reg))) (out_of Reg (with_flags_reg @@ -3119,7 +3132,7 @@ ;; umulh tmp, a, b ;; cmp tmp, #0 ;; cset of, ne -(rule 2 (lower (has_type $I64 (umul_overflow a b))) +(rule 2 (lower (has_type $I64 (umul_overflow _ a b))) (let ( (out Reg (madd $I64 a b (zero_reg))) (tmp Reg (umulh $I64 a b)) @@ -3137,7 +3150,7 @@ ;; mul out, a_ext, b_ext ;; cmp out, out, sxt{b,h} ;; cset of, ne -(rule 1 (lower (has_type (fits_in_16 ty) (smul_overflow a b))) +(rule 1 (lower (has_type (fits_in_16 ty) (smul_overflow _ a b))) (let ((extend ExtendOp (lower_extend_op ty (ArgumentExtension.Sext))) (a_sext Reg (put_in_reg_sext32 a)) @@ -3153,7 +3166,7 @@ ;; smull out, a, b ;; cmp out, out, sxtw ;; cset of, ne -(rule 2 (lower (has_type $I32 (smul_overflow a b))) +(rule 2 (lower (has_type $I32 (smul_overflow _ a b))) (let ( (out Reg (smaddl a b (zero_reg))) (out_of Reg (with_flags_reg @@ -3167,7 +3180,7 @@ ;; smulh tmp, a, b ;; cmp tmp, out, ASR #63 ;; cset of, ne -(rule 2 (lower (has_type $I64 (smul_overflow a b))) +(rule 2 (lower (has_type $I64 (smul_overflow _ a b))) (let ( (out Reg (madd $I64 a b (zero_reg))) (tmp Reg (smulh $I64 a b)) @@ -3180,21 +3193,21 @@ ;;; Rules for `tls_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (tls_model (TlsModel.ElfGd)) (tls_value (symbol_value_data name _ _)))) +(rule (lower (has_type (tls_model (TlsModel.ElfGd)) (tls_value _ (symbol_value_data name _ _)))) (elf_tls_get_addr name)) -(rule (lower (has_type (tls_model (TlsModel.Macho)) (tls_value (symbol_value_data name _ _)))) +(rule (lower (has_type (tls_model (TlsModel.Macho)) (tls_value _ (symbol_value_data name _ _)))) (macho_tls_get_addr name)) ;;; Rules for `fvpromote_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (fvpromote_low val)) +(rule (lower (fvpromote_low _ val)) (vec_rr_long (VecRRLongOp.Fcvtl32) val false)) ;;; Rules for `brif` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `brif` following `icmp` -(rule (lower_branch (brif (maybe_uextend (icmp cc x @ (value_type ty) y)) _ _) (two_targets taken not_taken)) +(rule (lower_branch (brif (maybe_uextend (icmp _ cc x @ (value_type ty) y)) _ _) (two_targets taken not_taken)) (let ((comparison FlagsAndCC (lower_icmp_into_flags cc x y ty)) (cond Cond (cond_code (flags_and_cc_cc comparison)))) (emit_side_effect @@ -3204,7 +3217,7 @@ (cond_br_cond cond)))))) ;; `brif` following `fcmp` -(rule (lower_branch (brif (maybe_uextend (fcmp cc x @ (value_type (ty_scalar_float ty)) y)) _ _) (two_targets taken not_taken)) +(rule (lower_branch (brif (maybe_uextend (fcmp _ cc x @ (value_type (ty_scalar_float ty)) y)) _ _) (two_targets taken not_taken)) (let ((cond Cond (fp_cond_code cc))) (emit_side_effect (with_flags_side_effect (fpu_cmp (scalar_size ty) x y) @@ -3230,14 +3243,14 @@ (cond_br taken not_taken (cond_br_not_zero rt (operand_size $I64))))))) ;; Special lowerings for `tbnz` - "Test bit and Branch if Nonzero" -(rule 1 (lower_branch (brif (band x @ (value_type ty) (u64_from_iconst n)) _ _) +(rule 1 (lower_branch (brif (band _ x @ (value_type ty) (u64_from_iconst n)) _ _) (two_targets taken not_taken)) (if-let bit (test_and_compare_bit_const ty n)) (emit_side_effect (tbnz taken not_taken x bit))) ;; Special lowering for `tbz` - "Test bit and Branch if Zero" -(rule 1 (lower_branch (brif (icmp (IntCC.Equal) - (band x @ (value_type (fits_in_64 ty)) +(rule 1 (lower_branch (brif (icmp _ (IntCC.Equal) + (band _ x @ (value_type (fits_in_64 ty)) (u64_from_iconst n)) (u64_from_iconst 0)) _ _) (two_targets taken not_taken)) @@ -3265,7 +3278,7 @@ ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (a64_label_address succ_label))) diff --git a/cranelift/codegen/src/isa/aarch64/lower.rs b/cranelift/codegen/src/isa/aarch64/lower.rs index 6d51a380433f..27c48ec54777 100644 --- a/cranelift/codegen/src/isa/aarch64/lower.rs +++ b/cranelift/codegen/src/isa/aarch64/lower.rs @@ -9,11 +9,9 @@ use crate::ir::Inst as IRInst; use crate::ir::condcodes::{FloatCC, IntCC}; -use crate::ir::pcc::{FactContext, PccResult}; use crate::ir::{Opcode, Value}; use crate::isa::aarch64::AArch64Backend; use crate::isa::aarch64::inst::*; -use crate::isa::aarch64::pcc; use crate::machinst::lower::*; use crate::machinst::*; @@ -130,16 +128,4 @@ impl LowerBackend for AArch64Backend { fn maybe_pinned_reg(&self) -> Option { Some(regs::pinned_reg()) } - - fn check_fact( - &self, - ctx: &FactContext<'_>, - vcode: &mut VCode, - inst: InsnIndex, - state: &mut pcc::FactFlowState, - ) -> PccResult<()> { - pcc::check(ctx, vcode, inst, state) - } - - type FactFlowState = pcc::FactFlowState; } diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs index 3e82af2a9c8e..b7f6b84e94e8 100644 --- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs +++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs @@ -181,6 +181,10 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> { self.backend.isa_flags.has_fp16() } + fn use_csdb(&mut self) -> bool { + self.backend.isa_flags.use_csdb() + } + fn move_wide_const_from_u64(&mut self, ty: Type, n: u64) -> Option { let bits = ty.bits(); let n = if bits < 64 { @@ -381,10 +385,6 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> { }, size, }); - if self.backend.flags.enable_pcc() { - self.lower_ctx - .add_range_fact(rd.to_reg(), 64, running_value, running_value); - } // Emit a `movk` instruction for each remaining slice of the desired // constant that does not match the initial value constructed above. @@ -400,10 +400,6 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> { size, }); running_value = replace(running_value, bits, shift); - if self.backend.flags.enable_pcc() { - self.lower_ctx - .add_range_fact(rd.to_reg(), 64, running_value, running_value); - } } } diff --git a/cranelift/codegen/src/isa/aarch64/lower_dynamic_neon.isle b/cranelift/codegen/src/isa/aarch64/lower_dynamic_neon.isle index 64da0ba11f84..8c9c21ed4234 100644 --- a/cranelift/codegen/src/isa/aarch64/lower_dynamic_neon.isle +++ b/cranelift/codegen/src/isa/aarch64/lower_dynamic_neon.isle @@ -1,98 +1,98 @@ ;;;; Rules for `iadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -4 (lower (has_type ty @ (dynamic_lane _ _) (iadd x y))) +(rule -4 (lower (has_type ty @ (dynamic_lane _ _) (iadd _ x y))) (value_reg (add_vec (put_in_reg x) (put_in_reg y) (vector_size ty)))) ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -5 (lower (has_type ty @ (dynamic_lane _ _) (isub x y))) +(rule -5 (lower (has_type ty @ (dynamic_lane _ _) (isub _ x y))) (value_reg (sub_vec (put_in_reg x) (put_in_reg y) (vector_size ty)))) ;;;; Rules for `imul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -4 (lower (has_type (lane_fits_in_32 ty @ (dynamic_lane _ _)) (imul x y))) +(rule -4 (lower (has_type (lane_fits_in_32 ty @ (dynamic_lane _ _)) (imul _ x y))) (value_reg (vec_rrr (VecALUOp.Mul) (put_in_reg x) (put_in_reg y) (vector_size ty)))) ;;;; Rules for `fadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fadd x y))) +(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fadd _ x y))) (value_reg (vec_rrr (VecALUOp.Fadd) (put_in_reg x) (put_in_reg y) (vector_size ty)))) ;;;; Rules for `fsub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fsub x y))) +(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fsub _ x y))) (value_reg (vec_rrr (VecALUOp.Fsub) (put_in_reg x) (put_in_reg y) (vector_size ty)))) ;;;; Rules for `fmul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fmul x y))) +(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fmul _ x y))) (value_reg (vec_rrr (VecALUOp.Fmul) (put_in_reg x) (put_in_reg y) (vector_size ty)))) ;;;; Rules for `fdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fdiv x y))) +(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fdiv _ x y))) (value_reg (vec_rrr (VecALUOp.Fdiv) (put_in_reg x) (put_in_reg y) (vector_size ty)))) ;;;; Rules for `fmin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fmin x y))) +(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fmin _ x y))) (value_reg (vec_rrr (VecALUOp.Fmin) (put_in_reg x) (put_in_reg y) (vector_size ty)))) ;;;; Rules for `fmax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fmax x y))) +(rule -2 (lower (has_type ty @ (dynamic_lane _ _) (fmax _ x y))) (value_reg (vec_rrr (VecALUOp.Fmax) (put_in_reg x) (put_in_reg y) (vector_size ty)))) ;;;; Rules for `snarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (has_type (ty_dyn128_int ty) (snarrow x y))) +(rule -2 (lower (has_type (ty_dyn128_int ty) (snarrow _ x y))) (if-let _ (zero_value y)) (sqxtn x (lane_size ty))) -(rule -1 (lower (has_type (ty_dyn64_int ty) (snarrow x y))) +(rule -1 (lower (has_type (ty_dyn64_int ty) (snarrow _ x y))) (let ((dst Reg (mov_vec_elem x y 1 0 (VectorSize.Size64x2)))) (sqxtn dst (lane_size ty)))) -(rule -3 (lower (has_type (ty_dyn128_int ty) (snarrow x y))) +(rule -3 (lower (has_type (ty_dyn128_int ty) (snarrow _ x y))) (let ((low_half Reg (sqxtn x (lane_size ty))) (result Reg (sqxtn2 low_half y (lane_size ty)))) result)) ;;;; Rules for `unarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (has_type (ty_dyn128_int ty) (unarrow x y))) +(rule -2 (lower (has_type (ty_dyn128_int ty) (unarrow _ x y))) (if-let _ (zero_value y)) (sqxtun x (lane_size ty))) -(rule -1 (lower (has_type (ty_dyn64_int ty) (unarrow x y))) +(rule -1 (lower (has_type (ty_dyn64_int ty) (unarrow _ x y))) (let ((dst Reg (mov_vec_elem x y 1 0 (VectorSize.Size64x2)))) (sqxtun dst (lane_size ty)))) -(rule -3 (lower (has_type (ty_dyn128_int ty) (unarrow x y))) +(rule -3 (lower (has_type (ty_dyn128_int ty) (unarrow _ x y))) (let ((low_half Reg (sqxtun x (lane_size ty))) (result Reg (sqxtun2 low_half y (lane_size ty)))) result)) ;;;; Rules for `uunarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (has_type (ty_dyn128_int ty) (uunarrow x y))) +(rule -2 (lower (has_type (ty_dyn128_int ty) (uunarrow _ x y))) (if-let _ (zero_value y)) (uqxtn x (lane_size ty))) -(rule -1 (lower (has_type (ty_dyn64_int ty) (uunarrow x y))) +(rule -1 (lower (has_type (ty_dyn64_int ty) (uunarrow _ x y))) (let ((dst Reg (mov_vec_elem x y 1 0 (VectorSize.Size64x2)))) (uqxtn dst (lane_size ty)))) -(rule -3 (lower (has_type (ty_dyn128_int ty) (uunarrow x y))) +(rule -3 (lower (has_type (ty_dyn128_int ty) (uunarrow _ x y))) (let ((low_half Reg (uqxtn x (lane_size ty))) (result Reg (uqxtn2 low_half y (lane_size ty)))) result)) ;;; Rules for `dynamic_stack_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (dynamic_stack_addr stack_slot)) +(rule (lower (dynamic_stack_addr _ stack_slot)) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (abi_dynamic_stackslot_addr dst stack_slot)))) (value_reg dst))) ;;; Rules for `extract_vector` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (extract_vector x 0)) +(rule (lower (extract_vector _ x 0)) (value_reg (put_in_reg x))) ;;;; Rules for `swiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty (swiden_high x))) +(rule -1 (lower (has_type ty (swiden_high _ x))) (vec_extend (VecExtendOp.Sxtl) x true (lane_size ty))) ;;;; Rules for `uwiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type ty (uwiden_high x))) +(rule -1 (lower (has_type ty (uwiden_high _ x))) (vec_extend (VecExtendOp.Uxtl) x true (lane_size ty))) diff --git a/cranelift/codegen/src/isa/aarch64/mod.rs b/cranelift/codegen/src/isa/aarch64/mod.rs index e90a8aecfa57..c1b0746b3481 100644 --- a/cranelift/codegen/src/isa/aarch64/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/mod.rs @@ -6,9 +6,11 @@ use crate::isa::aarch64::settings as aarch64_settings; #[cfg(feature = "unwind")] use crate::isa::unwind::systemv; use crate::isa::{Builder as IsaBuilder, FunctionAlignment, IsaFlagsHashKey, TargetIsa}; +#[cfg(feature = "unwind")] +use crate::machinst::CompiledCode; use crate::machinst::{ - CompiledCode, CompiledCodeStencil, MachInst, MachTextSectionBuilder, Reg, SigSet, - TextSectionBuilder, VCode, compile, + CompiledCodeStencil, MachInst, MachTextSectionBuilder, Reg, SigSet, TextSectionBuilder, VCode, + compile, }; use crate::result::CodegenResult; use crate::settings as shared_settings; @@ -16,13 +18,14 @@ use alloc::string::String; use alloc::{boxed::Box, vec::Vec}; use core::fmt; use cranelift_control::ControlPlane; -use target_lexicon::{Aarch64Architecture, Architecture, OperatingSystem, Triple}; +#[cfg(feature = "unwind")] +use target_lexicon::OperatingSystem; +use target_lexicon::{Aarch64Architecture, Architecture, Triple}; // New backend: mod abi; pub mod inst; mod lower; -mod pcc; pub mod settings; use self::inst::EmitInfo; @@ -56,7 +59,7 @@ impl AArch64Backend { domtree: &DominatorTree, ctrl_plane: &mut ControlPlane, ) -> CodegenResult<(VCode, regalloc2::Output)> { - let emit_info = EmitInfo::new(self.flags.clone()); + let emit_info = EmitInfo::new(self.flags.clone(), self.isa_flags.clone()); let sigs = SigSet::new::(func, &self.flags)?; let abi = abi::AArch64Callee::new(func, self, &self.isa_flags, &sigs)?; compile::compile::(func, domtree, self, abi, emit_info, sigs, ctrl_plane) diff --git a/cranelift/codegen/src/isa/aarch64/pcc.rs b/cranelift/codegen/src/isa/aarch64/pcc.rs deleted file mode 100644 index e5ee1919803e..000000000000 --- a/cranelift/codegen/src/isa/aarch64/pcc.rs +++ /dev/null @@ -1,570 +0,0 @@ -//! Proof-carrying code checking for AArch64 VCode. - -use crate::ir::MemFlags; -use crate::ir::pcc::*; -use crate::ir::types::*; -use crate::isa::aarch64::inst::Inst; -use crate::isa::aarch64::inst::args::{Cond, PairAMode, ShiftOp}; -use crate::isa::aarch64::inst::regs::zero_reg; -use crate::isa::aarch64::inst::{ALUOp, MoveWideOp}; -use crate::isa::aarch64::inst::{AMode, ExtendOp}; -use crate::machinst::Reg; -use crate::machinst::pcc::*; -use crate::machinst::{InsnIndex, VCode}; -use crate::trace; - -fn extend_fact(ctx: &FactContext, value: &Fact, mode: ExtendOp) -> Option { - match mode { - ExtendOp::UXTB => ctx.uextend(value, 8, 64), - ExtendOp::UXTH => ctx.uextend(value, 16, 64), - ExtendOp::UXTW => ctx.uextend(value, 32, 64), - ExtendOp::UXTX => Some(value.clone()), - ExtendOp::SXTB => ctx.sextend(value, 8, 64), - ExtendOp::SXTH => ctx.sextend(value, 16, 64), - ExtendOp::SXTW => ctx.sextend(value, 32, 64), - ExtendOp::SXTX => None, - } -} - -/// Flow-state between facts. -#[derive(Clone, Debug, Default)] -pub struct FactFlowState { - cmp_flags: Option<(Fact, Fact)>, -} - -pub(crate) fn check( - ctx: &FactContext, - vcode: &mut VCode, - inst_idx: InsnIndex, - state: &mut FactFlowState, -) -> PccResult<()> { - let inst = &vcode[inst_idx]; - trace!("Checking facts on inst: {:?}", inst); - - // We only persist flag state for one instruction, because we - // can't exhaustively enumerate all flags-effecting ops; so take - // the `cmp_state` here and perhaps use it below but don't let it - // remain. - let cmp_flags = state.cmp_flags.take(); - trace!(" * with cmp_flags = {cmp_flags:?}"); - - match *inst { - Inst::Args { .. } => { - // Defs on the args have "axiomatic facts": we trust the - // ABI code to pass through the values unharmed, so the - // facts given to us in the CLIF should still be true. - Ok(()) - } - Inst::ULoad8 { rd, ref mem, flags } - | Inst::SLoad8 { rd, ref mem, flags } - | Inst::ULoad16 { rd, ref mem, flags } - | Inst::SLoad16 { rd, ref mem, flags } - | Inst::ULoad32 { rd, ref mem, flags } - | Inst::SLoad32 { rd, ref mem, flags } - | Inst::ULoad64 { rd, ref mem, flags } => { - let access_ty = inst.mem_type().unwrap(); - check_load(ctx, Some(rd.to_reg()), flags, mem, vcode, access_ty) - } - Inst::FpuLoad16 { ref mem, flags, .. } - | Inst::FpuLoad32 { ref mem, flags, .. } - | Inst::FpuLoad64 { ref mem, flags, .. } - | Inst::FpuLoad128 { ref mem, flags, .. } => { - let access_ty = inst.mem_type().unwrap(); - check_load(ctx, None, flags, mem, vcode, access_ty) - } - Inst::LoadP64 { ref mem, flags, .. } => check_load_pair(ctx, flags, mem, vcode, 16), - Inst::FpuLoadP64 { ref mem, flags, .. } => check_load_pair(ctx, flags, mem, vcode, 16), - Inst::FpuLoadP128 { ref mem, flags, .. } => check_load_pair(ctx, flags, mem, vcode, 32), - Inst::VecLoadReplicate { - rn, flags, size, .. - } => check_load_addr(ctx, flags, rn, vcode, size.lane_size().ty()), - Inst::LoadAcquire { - access_ty, - rn, - flags, - .. - } => check_load_addr(ctx, flags, rn, vcode, access_ty), - - Inst::Store8 { rd, ref mem, flags } - | Inst::Store16 { rd, ref mem, flags } - | Inst::Store32 { rd, ref mem, flags } - | Inst::Store64 { rd, ref mem, flags } => { - let access_ty = inst.mem_type().unwrap(); - check_store(ctx, Some(rd), flags, mem, vcode, access_ty) - } - Inst::FpuStore16 { ref mem, flags, .. } - | Inst::FpuStore32 { ref mem, flags, .. } - | Inst::FpuStore64 { ref mem, flags, .. } - | Inst::FpuStore128 { ref mem, flags, .. } => { - let access_ty = inst.mem_type().unwrap(); - check_store(ctx, None, flags, mem, vcode, access_ty) - } - Inst::StoreP64 { ref mem, flags, .. } => check_store_pair(ctx, flags, mem, vcode, 16), - Inst::FpuStoreP64 { ref mem, flags, .. } => check_store_pair(ctx, flags, mem, vcode, 16), - Inst::FpuStoreP128 { ref mem, flags, .. } => check_store_pair(ctx, flags, mem, vcode, 32), - Inst::StoreRelease { - access_ty, - rn, - flags, - .. - } => check_store_addr(ctx, flags, rn, vcode, access_ty), - - Inst::AluRRR { - alu_op: ALUOp::Add | ALUOp::AddS, - size, - rd, - rn, - rm, - } => check_binop(ctx, vcode, 64, rd, rn, rm, |rn, rm| { - clamp_range( - ctx, - 64, - size.bits().into(), - ctx.add(rn, rm, size.bits().into()), - ) - }), - Inst::AluRRImm12 { - alu_op: ALUOp::Add | ALUOp::AddS, - size, - rd, - rn, - imm12, - } => check_unop(ctx, vcode, 64, rd, rn, |rn| { - let imm12: i64 = imm12.value().into(); - clamp_range( - ctx, - 64, - size.bits().into(), - ctx.offset(&rn, size.bits().into(), imm12), - ) - }), - Inst::AluRRImm12 { - alu_op: ALUOp::Sub, - size, - rd, - rn, - imm12, - } => check_unop(ctx, vcode, 64, rd, rn, |rn| { - let imm12: i64 = imm12.value().into(); - clamp_range( - ctx, - 64, - size.bits().into(), - ctx.offset(&rn, size.bits().into(), -imm12), - ) - }), - Inst::AluRRR { - alu_op: ALUOp::Sub, - size, - rd, - rn, - rm, - } => check_binop(ctx, vcode, 64, rd, rn, rm, |rn, rm| { - if let Some(k) = rm.as_const(64) { - clamp_range( - ctx, - 64, - size.bits().into(), - ctx.offset(rn, size.bits().into(), -(k as i64)), - ) - } else { - clamp_range(ctx, 64, size.bits().into(), None) - } - }), - Inst::AluRRRShift { - alu_op: ALUOp::Add | ALUOp::AddS, - size, - rd, - rn, - rm, - shiftop, - } if shiftop.op() == ShiftOp::LSL && has_fact(vcode, rn) && has_fact(vcode, rm) => { - check_binop(ctx, vcode, 64, rd, rn, rm, |rn, rm| { - let rm_shifted = fail_if_missing(ctx.shl( - &rm, - size.bits().into(), - shiftop.amt().value().into(), - ))?; - clamp_range( - ctx, - 64, - size.bits().into(), - ctx.add(&rn, &rm_shifted, size.bits().into()), - ) - }) - } - Inst::AluRRRExtend { - alu_op: ALUOp::Add | ALUOp::AddS, - size, - rd, - rn, - rm, - extendop, - } if has_fact(vcode, rn) && has_fact(vcode, rm) => { - check_binop(ctx, vcode, 64, rd, rn, rm, |rn, rm| { - let rm_extended = fail_if_missing(extend_fact(ctx, rm, extendop))?; - clamp_range( - ctx, - 64, - size.bits().into(), - ctx.add(&rn, &rm_extended, size.bits().into()), - ) - }) - } - Inst::AluRRImmShift { - alu_op: ALUOp::Lsl, - size, - rd, - rn, - immshift, - } if has_fact(vcode, rn) => check_unop(ctx, vcode, 64, rd, rn, |rn| { - clamp_range( - ctx, - 64, - size.bits().into(), - ctx.shl(&rn, size.bits().into(), immshift.value().into()), - ) - }), - Inst::Extend { - rd, - rn, - signed: false, - from_bits, - to_bits, - } if has_fact(vcode, rn) => check_unop(ctx, vcode, 64, rd, rn, |rn| { - clamp_range( - ctx, - 64, - to_bits.into(), - ctx.uextend(&rn, from_bits.into(), to_bits.into()), - ) - }), - - Inst::AluRRR { - alu_op: ALUOp::SubS, - size, - rd, - rn, - rm, - } if rd.to_reg() == zero_reg() => { - // Compare. - let rn = get_fact_or_default(vcode, rn, size.bits().into()); - let rm = get_fact_or_default(vcode, rm, size.bits().into()); - state.cmp_flags = Some((rn, rm)); - Ok(()) - } - - Inst::AluRRImmLogic { - alu_op: ALUOp::Orr, - size, - rd, - rn, - imml, - } if rn == zero_reg() => { - let constant = imml.value(); - check_constant(ctx, vcode, rd, size.bits().into(), constant) - } - - Inst::AluRRR { rd, size, .. } - | Inst::AluRRImm12 { rd, size, .. } - | Inst::AluRRRShift { rd, size, .. } - | Inst::AluRRRExtend { rd, size, .. } - | Inst::AluRRImmLogic { rd, size, .. } - | Inst::AluRRImmShift { rd, size, .. } => check_output(ctx, vcode, rd, &[], |_vcode| { - clamp_range(ctx, 64, size.bits().into(), None) - }), - - Inst::Extend { - rd, - from_bits, - to_bits, - .. - } => check_output(ctx, vcode, rd, &[], |_vcode| { - clamp_range(ctx, to_bits.into(), from_bits.into(), None) - }), - - Inst::MovWide { - op: MoveWideOp::MovZ, - imm, - size: _, - rd, - } => { - let constant = u64::from(imm.bits) << (imm.shift * 16); - check_constant(ctx, vcode, rd, 64, constant) - } - - Inst::MovWide { - op: MoveWideOp::MovN, - imm, - size, - rd, - } => { - let constant = !(u64::from(imm.bits) << (imm.shift * 16)) & size.max_value(); - check_constant(ctx, vcode, rd, 64, constant) - } - - Inst::MovK { rd, rn, imm, .. } => { - let input = get_fact_or_default(vcode, rn, 64); - if let Some(input_constant) = input.as_const(64) { - let mask = 0xffff << (imm.shift * 16); - let constant = u64::from(imm.bits) << (imm.shift * 16); - let constant = (input_constant & !mask) | constant; - check_constant(ctx, vcode, rd, 64, constant) - } else { - check_output(ctx, vcode, rd, &[], |_vcode| { - Ok(Some(Fact::max_range_for_width(64))) - }) - } - } - - Inst::CSel { rd, cond, rn, rm } - if (cond == Cond::Hs || cond == Cond::Hi) && cmp_flags.is_some() => - { - let (cmp_lhs, cmp_rhs) = cmp_flags.unwrap(); - trace!("CSel: cmp {cond:?} ({cmp_lhs:?}, {cmp_rhs:?})"); - - check_output(ctx, vcode, rd, &[], |vcode| { - // We support transitivity-based reasoning. If the - // comparison establishes that - // - // (x+K1) <= (y+K2) - // - // then on the true-side of the select we can edit the maximum - // in a DynamicMem or DynamicRange by replacing x's with y's - // with appropriate offsets -- this widens the range. - // - // Likewise, on the false-side of the select we can - // replace y's with x's -- this also widens the range. On - // the false side we know the inequality is strict, so we - // can offset by one. - - // True side: lhs >= rhs (Hs) or lhs > rhs (Hi). - let rn = get_fact_or_default(vcode, rn, 64); - let lhs_kind = match cond { - Cond::Hs => InequalityKind::Loose, - Cond::Hi => InequalityKind::Strict, - _ => unreachable!(), - }; - let rn = ctx.apply_inequality(&rn, &cmp_lhs, &cmp_rhs, lhs_kind); - // false side: rhs < lhs (Hs) or rhs <= lhs (Hi). - let rm = get_fact_or_default(vcode, rm, 64); - let rhs_kind = match cond { - Cond::Hs => InequalityKind::Strict, - Cond::Hi => InequalityKind::Loose, - _ => unreachable!(), - }; - let rm = ctx.apply_inequality(&rm, &cmp_rhs, &cmp_lhs, rhs_kind); - let union = ctx.union(&rn, &rm); - // Union the two facts. - clamp_range(ctx, 64, 64, union) - }) - } - - _ if vcode.inst_defines_facts(inst_idx) => Err(PccError::UnsupportedFact), - - _ => Ok(()), - } -} - -fn check_load( - ctx: &FactContext, - rd: Option, - flags: MemFlags, - addr: &AMode, - vcode: &VCode, - ty: Type, -) -> PccResult<()> { - let result_fact = rd.and_then(|rd| vcode.vreg_fact(rd.into())); - let bits = u16::try_from(ty.bits()).unwrap(); - check_addr( - ctx, - flags, - addr, - vcode, - ty, - LoadOrStore::Load { - result_fact, - from_bits: bits, - to_bits: bits, - }, - ) -} - -fn check_store( - ctx: &FactContext, - rd: Option, - flags: MemFlags, - addr: &AMode, - vcode: &VCode, - ty: Type, -) -> PccResult<()> { - let stored_fact = rd.and_then(|rd| vcode.vreg_fact(rd.into())); - check_addr( - ctx, - flags, - addr, - vcode, - ty, - LoadOrStore::Store { stored_fact }, - ) -} - -fn check_addr<'a>( - ctx: &FactContext, - flags: MemFlags, - addr: &AMode, - vcode: &VCode, - ty: Type, - op: LoadOrStore<'a>, -) -> PccResult<()> { - if !flags.checked() { - return Ok(()); - } - - trace!("check_addr: {:?}", addr); - - let check = |addr: &Fact, ty: Type| -> PccResult<()> { - match op { - LoadOrStore::Load { - result_fact, - from_bits, - to_bits, - } => { - let loaded_fact = - clamp_range(ctx, to_bits, from_bits, ctx.load(addr, ty)?.cloned())?; - trace!( - "checking a load: loaded_fact = {loaded_fact:?} result_fact = {result_fact:?}" - ); - if ctx.subsumes_fact_optionals(loaded_fact.as_ref(), result_fact) { - Ok(()) - } else { - Err(PccError::UnsupportedFact) - } - } - LoadOrStore::Store { stored_fact } => ctx.store(addr, ty, stored_fact), - } - }; - - match addr { - &AMode::RegReg { rn, rm } => { - let rn = get_fact_or_default(vcode, rn, 64); - let rm = get_fact_or_default(vcode, rm, 64); - let sum = fail_if_missing(ctx.add(&rn, &rm, 64))?; - trace!("rn = {rn:?} rm = {rm:?} sum = {sum:?}"); - check(&sum, ty) - } - &AMode::RegScaled { rn, rm } => { - let rn = get_fact_or_default(vcode, rn, 64); - let rm = get_fact_or_default(vcode, rm, 64); - let rm_scaled = fail_if_missing(ctx.scale(&rm, 64, ty.bytes()))?; - let sum = fail_if_missing(ctx.add(&rn, &rm_scaled, 64))?; - check(&sum, ty) - } - &AMode::RegScaledExtended { rn, rm, extendop } => { - let rn = get_fact_or_default(vcode, rn, 64); - let rm = get_fact_or_default(vcode, rm, 64); - let rm_extended = fail_if_missing(extend_fact(ctx, &rm, extendop))?; - let rm_scaled = fail_if_missing(ctx.scale(&rm_extended, 64, ty.bytes()))?; - let sum = fail_if_missing(ctx.add(&rn, &rm_scaled, 64))?; - check(&sum, ty) - } - &AMode::RegExtended { rn, rm, extendop } => { - let rn = get_fact_or_default(vcode, rn, 64); - let rm = get_fact_or_default(vcode, rm, 64); - let rm_extended = fail_if_missing(extend_fact(ctx, &rm, extendop))?; - let sum = fail_if_missing(ctx.add(&rn, &rm_extended, 64))?; - check(&sum, ty)?; - Ok(()) - } - &AMode::Unscaled { rn, simm9 } => { - let rn = get_fact_or_default(vcode, rn, 64); - let sum = fail_if_missing(ctx.offset(&rn, 64, simm9.value.into()))?; - check(&sum, ty) - } - &AMode::UnsignedOffset { rn, uimm12 } => { - let rn = get_fact_or_default(vcode, rn, 64); - // N.B.: the architecture scales the immediate in the - // encoded instruction by the size of the loaded type, so - // e.g. an offset field of 4095 can mean a load of offset - // 32760 (= 4095 * 8) for I64s. The `UImm12Scaled` type - // stores the *scaled* value, so we don't need to multiply - // (again) by the type's size here. - let offset: u64 = uimm12.value().into(); - // This `unwrap()` will always succeed because the value - // will always be positive and much smaller than - // `i64::MAX` (see above). - let sum = fail_if_missing(ctx.offset(&rn, 64, i64::try_from(offset).unwrap()))?; - check(&sum, ty) - } - &AMode::Label { .. } | &AMode::Const { .. } => { - // Always accept: labels and constants must be within the - // generated code (else they won't be resolved). - Ok(()) - } - &AMode::RegOffset { rn, off, .. } => { - let rn = get_fact_or_default(vcode, rn, 64); - let sum = fail_if_missing(ctx.offset(&rn, 64, off))?; - check(&sum, ty) - } - &AMode::SPOffset { .. } - | &AMode::FPOffset { .. } - | &AMode::IncomingArg { .. } - | &AMode::SlotOffset { .. } - | &AMode::SPPostIndexed { .. } - | &AMode::SPPreIndexed { .. } => { - // We trust ABI code (for now!) and no lowering rules - // lower input value accesses directly to these. - Ok(()) - } - } -} - -fn check_load_pair( - _ctx: &FactContext, - _flags: MemFlags, - _addr: &PairAMode, - _vcode: &VCode, - _size: u8, -) -> PccResult<()> { - Err(PccError::UnimplementedInst) -} - -fn check_store_pair( - _ctx: &FactContext, - _flags: MemFlags, - _addr: &PairAMode, - _vcode: &VCode, - _size: u8, -) -> PccResult<()> { - Err(PccError::UnimplementedInst) -} - -fn check_load_addr( - ctx: &FactContext, - flags: MemFlags, - reg: Reg, - vcode: &VCode, - ty: Type, -) -> PccResult<()> { - if !flags.checked() { - return Ok(()); - } - let fact = get_fact_or_default(vcode, reg, 64); - let _output_fact = ctx.load(&fact, ty)?; - Ok(()) -} - -fn check_store_addr( - ctx: &FactContext, - flags: MemFlags, - reg: Reg, - vcode: &VCode, - ty: Type, -) -> PccResult<()> { - if !flags.checked() { - return Ok(()); - } - let fact = get_fact_or_default(vcode, reg, 64); - let _output_fact = ctx.store(&fact, ty, None)?; - Ok(()) -} diff --git a/cranelift/codegen/src/isa/pulley_shared/abi.rs b/cranelift/codegen/src/isa/pulley_shared/abi.rs index 7290d6eaf01a..6e192c02394f 100644 --- a/cranelift/codegen/src/isa/pulley_shared/abi.rs +++ b/cranelift/codegen/src/isa/pulley_shared/abi.rs @@ -165,7 +165,7 @@ where fn gen_load_stack(mem: StackAMode, into_reg: Writable, ty: Type) -> Self::I { let mut flags = MemFlags::trusted(); - // Stack loads/stores of vectors always use little-endianess to avoid + // Stack loads/stores of vectors always use little-endianness to avoid // implementing a byte-swap of vectors on big-endian platforms. if ty.is_vector() { flags.set_endianness(ir::Endianness::Little); @@ -175,7 +175,7 @@ where fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Self::I { let mut flags = MemFlags::trusted(); - // Stack loads/stores of vectors always use little-endianess to avoid + // Stack loads/stores of vectors always use little-endianness to avoid // implementing a byte-swap of vectors on big-endian platforms. if ty.is_vector() { flags.set_endianness(ir::Endianness::Little); diff --git a/cranelift/codegen/src/isa/pulley_shared/inst.isle b/cranelift/codegen/src/isa/pulley_shared/inst.isle index 53c71b2096cc..ba96439db1d7 100644 --- a/cranelift/codegen/src/isa/pulley_shared/inst.isle +++ b/cranelift/codegen/src/isa/pulley_shared/inst.isle @@ -182,7 +182,7 @@ (type ValueOffset (enum (Both (value Value) (offset i32)))) (decl pure amode_base (Value Offset32) ValueOffset) (rule (amode_base addr (offset32 offset)) (ValueOffset.Both addr offset)) -(rule 1 (amode_base (iadd addr (i32_from_iconst b)) (offset32 offset)) +(rule 1 (amode_base (iadd _ addr (i32_from_iconst b)) (offset32 offset)) (if-let new_offset (i32_checked_add b offset)) (ValueOffset.Both addr new_offset)) @@ -270,7 +270,7 @@ (type SinkableLoad (enum (Load (inst Inst) (ty Type) (addr Value) (offset u8)))) (decl pure partial sinkable_load (Value) SinkableLoad) (rule (sinkable_load value @ (value_type ty)) - (if-let inst @ (load flags addr (offset32 offset)) (is_sinkable_inst value)) + (if-let inst @ (load _ flags addr (offset32 offset)) (is_sinkable_inst value)) (if-let true (is_native_endianness (endianness flags))) (if-let true (memflags_nontrapping flags)) (if-let offset8 (i32_try_into_u8 offset)) @@ -345,7 +345,7 @@ ;; used if the oob condition is true. Otherwise the raw address is used for the ;; load/store. (decl pure partial wasm_g32 (Value Offset32 MemFlags Type) G32) -(rule (wasm_g32 (select oob (u64_from_iconst 0) raw_addr) (offset32 0) flags ty) +(rule (wasm_g32 (select _ oob (u64_from_iconst 0) raw_addr) (offset32 0) flags ty) ;; This must be a wasm load/store according to `MemFlags`, namely that it's ;; got a "HEAP_OUT_OF_BOUNDS" trap code and it's little-endian. (if-let true (memflags_is_wasm flags)) @@ -356,7 +356,7 @@ ;; Next see if the `oob` and `raw_addr` combination match. This will attempt ;; extract a full bounds check from these values. If everything succeeds the ;; final step is then to extract an 8-bit offset of the load/store operation, - ;; if appplicable, assuming that the constants used in various places all line + ;; if applicable, assuming that the constants used in various places all line ;; up just right. (if-let (OobSelect.All base bound wasm_addr access_size_plus_offset) (wasm_oob_select oob host_addr)) @@ -381,8 +381,8 @@ (decl pure host_offset (Value) HostOffset) (type HostOffset (enum (All (a Value) (b i32)))) (rule 0 (host_offset val) (HostOffset.All val 0)) -(rule 1 (host_offset (iadd val @ (iadd _ _) (i32_from_iconst n))) (HostOffset.All val n)) -(rule 2 (host_offset (iadd (i32_from_iconst n) val @ (iadd _ _))) (HostOffset.All val n)) +(rule 1 (host_offset (iadd _ val @ (iadd _ _ _) (i32_from_iconst n))) (HostOffset.All val n)) +(rule 2 (host_offset (iadd _ (i32_from_iconst n) val @ (iadd _ _ _))) (HostOffset.All val n)) ;; Helper to test whether the first argument `oob` contains a condition for ;; matching whether second argument `addr` is out-of-bounds. Searches for a @@ -392,23 +392,23 @@ ;; 32-bit hosts: search either side of the `iadd` for the base address within ;; `oob` to see if it's a matching condition. -(rule 0 (wasm_oob_select oob (iadd base wasm_addr @ (value_type $I32))) +(rule 0 (wasm_oob_select oob (iadd _ base wasm_addr @ (value_type $I32))) (if-let (OobCond.All bound n) (wasm_oob_cond wasm_addr oob)) (if-let (PointerWidth.PointerWidth32) (pointer_width)) (OobSelect.All base bound wasm_addr n)) -(rule 1 (wasm_oob_select oob (iadd wasm_addr @ (value_type $I32) base)) +(rule 1 (wasm_oob_select oob (iadd _ wasm_addr @ (value_type $I32) base)) (if-let (OobCond.All bound n) (wasm_oob_cond wasm_addr oob)) (if-let (PointerWidth.PointerWidth32) (pointer_width)) (OobSelect.All base bound wasm_addr n)) ;; 64-bit hosts: also search either side, but the wasm address must also be ;; a `uextend` from a 32-bit value. -(rule 0 (wasm_oob_select oob (iadd base wasm_addr_ext @ (value_type $I64))) +(rule 0 (wasm_oob_select oob (iadd _ base wasm_addr_ext @ (value_type $I64))) (if-let (OobCond.All bound n) (wasm_oob_cond wasm_addr_ext oob)) (if-let wasm_addr (wasm32_addr_for_host64 wasm_addr_ext)) (if-let (PointerWidth.PointerWidth64) (pointer_width)) (OobSelect.All base bound wasm_addr n)) -(rule 1 (wasm_oob_select oob (iadd wasm_addr_ext @ (value_type $I64) base)) +(rule 1 (wasm_oob_select oob (iadd _ wasm_addr_ext @ (value_type $I64) base)) (if-let (OobCond.All bound n) (wasm_oob_cond wasm_addr_ext oob)) (if-let wasm_addr (wasm32_addr_for_host64 wasm_addr_ext)) (if-let (PointerWidth.PointerWidth64) (pointer_width)) @@ -419,7 +419,7 @@ ;; extended from 32-bits. That means that this only matches `(uextend ...)` or ;; an `iconst` which already fits in 32 bits. (decl pure partial wasm32_addr_for_host64 (Value) Value) -(rule (wasm32_addr_for_host64 (uextend addr @ (value_type $I32))) addr) +(rule (wasm32_addr_for_host64 (uextend _ addr @ (value_type $I32))) addr) (rule (wasm32_addr_for_host64 val @ (u32_from_iconst _)) val) ;; Helper to search for the first argument, `wasm_addr`, within the oob @@ -427,9 +427,9 @@ ;; of the address against a particular bound. (decl pure partial wasm_oob_cond (Value Value) OobCond) (type OobCond (enum (All (a Value) (b u64)))) -(rule (wasm_oob_cond wasm_addr (ugt wasm_addr (isub bound (u64_from_iconst n)))) +(rule (wasm_oob_cond wasm_addr (ugt wasm_addr (isub _ bound (u64_from_iconst n)))) (OobCond.All bound n)) -(rule (wasm_oob_cond wasm_addr (ult (isub bound (u64_from_iconst n)) wasm_addr)) +(rule (wasm_oob_cond wasm_addr (ult (isub _ bound (u64_from_iconst n)) wasm_addr)) (OobCond.All bound n)) ;;;; Newtypes for Different Register Classes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs b/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs index 74082b4260c3..d99f71cabaf1 100644 --- a/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs +++ b/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs @@ -125,7 +125,7 @@ fn pulley_emit

( P: PulleyTargetKind, { match inst { - // Pseduo-instructions that don't actually encode to anything. + // Pseudo-instructions that don't actually encode to anything. Inst::Args { .. } | Inst::Rets { .. } | Inst::DummyUse { .. } => {} Inst::TrapIf { cond, code } => { diff --git a/cranelift/codegen/src/isa/pulley_shared/lower.isle b/cranelift/codegen/src/isa/pulley_shared/lower.isle index 00b0a2badf68..d6df01f9df7d 100644 --- a/cranelift/codegen/src/isa/pulley_shared/lower.isle +++ b/cranelift/codegen/src/isa/pulley_shared/lower.isle @@ -20,10 +20,10 @@ (Cond.IfXneq64I32 val 0)) ;; Peel away explicit `uextend` values to take a look at the inner value. -(rule 2 (lower_cond (uextend val)) (lower_cond val)) +(rule 2 (lower_cond (uextend _ val)) (lower_cond val)) ;; Conditional branches on `icmp`s. -(rule 2 (lower_cond (icmp cc a b @ (value_type $I32))) (lower_cond_icmp32 cc a b)) -(rule 2 (lower_cond (icmp cc a b @ (value_type $I64))) (lower_cond_icmp64 cc a b)) +(rule 2 (lower_cond (icmp _ cc a b @ (value_type $I32))) (lower_cond_icmp32 cc a b)) +(rule 2 (lower_cond (icmp _ cc a b @ (value_type $I64))) (lower_cond_icmp64 cc a b)) (decl lower_cond_icmp32 (IntCC Value Value) Cond) (rule (lower_cond_icmp32 (IntCC.Equal) a b) (Cond.IfXeq32 a b)) @@ -124,16 +124,16 @@ ;;;; Rules for `get_stack_pointer` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_stack_pointer)) +(rule (lower (get_stack_pointer _)) (pulley_get_special (sp_reg))) ;;;; Rules for `get_frame_pointer` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_frame_pointer)) (pulley_xmov_fp)) +(rule (lower (get_frame_pointer _)) (pulley_xmov_fp)) ;;;; Rules for `get_return_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_return_address)) (pulley_xmov_lr)) +(rule (lower (get_return_address _)) (pulley_xmov_lr)) ;;;; Rules for `return` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -232,43 +232,43 @@ ;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (iconst c))) (imm (i64_sextend_imm64 ty c))) +(rule (lower (has_type ty (iconst _ c))) (imm (i64_sextend_imm64 ty c))) ;;;; Rules for `f32const`;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f32const (u32_from_ieee32 x))) (pulley_fconst32 x)) +(rule (lower (f32const _ (u32_from_ieee32 x))) (pulley_fconst32 x)) ;;;; Rules for `f64const`;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f64const (u64_from_ieee64 x))) (pulley_fconst64 x)) +(rule (lower (f64const _ (u64_from_ieee64 x))) (pulley_fconst64 x)) ;;;; Rules for `iadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int (fits_in_64 ty)) (iadd a b))) (pulley_xadd32 a b)) -(rule 1 (lower (has_type $I64 (iadd a b))) (pulley_xadd64 a b)) +(rule 0 (lower (has_type (ty_int (fits_in_64 ty)) (iadd _ a b))) (pulley_xadd32 a b)) +(rule 1 (lower (has_type $I64 (iadd _ a b))) (pulley_xadd64 a b)) ;; Fold constants into the instruction if possible -(rule 10 (lower (has_type (ty_int (fits_in_32 _)) (iadd a (u32_from_iconst b)))) +(rule 10 (lower (has_type (ty_int (fits_in_32 _)) (iadd _ a (u32_from_iconst b)))) (pulley_xadd32_u32 a b)) -(rule 11 (lower (has_type (ty_int (fits_in_32 _)) (iadd a (u8_from_iconst b)))) +(rule 11 (lower (has_type (ty_int (fits_in_32 _)) (iadd _ a (u8_from_iconst b)))) (pulley_xadd32_u8 a b)) -(rule 12 (lower (has_type $I64 (iadd a (u32_from_iconst b)))) +(rule 12 (lower (has_type $I64 (iadd _ a (u32_from_iconst b)))) (pulley_xadd64_u32 a b)) -(rule 13 (lower (has_type $I64 (iadd a (u8_from_iconst b)))) +(rule 13 (lower (has_type $I64 (iadd _ a (u8_from_iconst b)))) (pulley_xadd64_u8 a b)) ;; If the rhs is a constant and the negated version can fit within a smaller ;; constant then switch this to a subtraction with the negated constant. -(rule 14 (lower (has_type (ty_int (fits_in_32 _)) (iadd a b))) +(rule 14 (lower (has_type (ty_int (fits_in_32 _)) (iadd _ a b))) (if-let c (u32_from_negated_iconst b)) (pulley_xsub32_u32 a c)) -(rule 15 (lower (has_type $I64 (iadd a b))) +(rule 15 (lower (has_type $I64 (iadd _ a b))) (if-let c (u32_from_negated_iconst b)) (pulley_xsub64_u32 a c)) -(rule 16 (lower (has_type (ty_int (fits_in_32 _)) (iadd a b))) +(rule 16 (lower (has_type (ty_int (fits_in_32 _)) (iadd _ a b))) (if-let c (u8_from_negated_iconst b)) (pulley_xsub32_u8 a c)) -(rule 17 (lower (has_type $I64 (iadd a b))) +(rule 17 (lower (has_type $I64 (iadd _ a b))) (if-let c (u8_from_negated_iconst b)) (pulley_xsub64_u8 a c)) @@ -289,7 +289,7 @@ neg_u32) ;; 128-bit addition -(rule 1 (lower (has_type $I128 (iadd a b))) +(rule 1 (lower (has_type $I128 (iadd _ a b))) (let ((a ValueRegs a) (b ValueRegs b)) (pulley_xadd128 @@ -299,60 +299,60 @@ (value_regs_get b 1)))) ;; vector addition -(rule 1 (lower (has_type $I8X16 (iadd a b))) (pulley_vaddi8x16 a b)) -(rule 1 (lower (has_type $I16X8 (iadd a b))) (pulley_vaddi16x8 a b)) -(rule 1 (lower (has_type $I32X4 (iadd a b))) (pulley_vaddi32x4 a b)) -(rule 1 (lower (has_type $I64X2 (iadd a b))) (pulley_vaddi64x2 a b)) +(rule 1 (lower (has_type $I8X16 (iadd _ a b))) (pulley_vaddi8x16 a b)) +(rule 1 (lower (has_type $I16X8 (iadd _ a b))) (pulley_vaddi16x8 a b)) +(rule 1 (lower (has_type $I32X4 (iadd _ a b))) (pulley_vaddi32x4 a b)) +(rule 1 (lower (has_type $I64X2 (iadd _ a b))) (pulley_vaddi64x2 a b)) -(rule 1 (lower (has_type $I8X16 (sadd_sat a b))) (pulley_vaddi8x16_sat a b)) -(rule 1 (lower (has_type $I8X16 (uadd_sat a b))) (pulley_vaddu8x16_sat a b)) -(rule 1 (lower (has_type $I16X8 (sadd_sat a b))) (pulley_vaddi16x8_sat a b)) -(rule 1 (lower (has_type $I16X8 (uadd_sat a b))) (pulley_vaddu16x8_sat a b)) +(rule 1 (lower (has_type $I8X16 (sadd_sat _ a b))) (pulley_vaddi8x16_sat a b)) +(rule 1 (lower (has_type $I8X16 (uadd_sat _ a b))) (pulley_vaddu8x16_sat a b)) +(rule 1 (lower (has_type $I16X8 (sadd_sat _ a b))) (pulley_vaddi16x8_sat a b)) +(rule 1 (lower (has_type $I16X8 (uadd_sat _ a b))) (pulley_vaddu16x8_sat a b)) ;; Specialized lowerings for multiply-and-add -(rule 2 (lower (has_type $I32 (iadd (imul a b) c))) (pulley_xmadd32 a b c)) -(rule 3 (lower (has_type $I32 (iadd c (imul a b)))) (pulley_xmadd32 a b c)) -(rule 2 (lower (has_type $I64 (iadd (imul a b) c))) (pulley_xmadd64 a b c)) -(rule 3 (lower (has_type $I64 (iadd c (imul a b)))) (pulley_xmadd64 a b c)) +(rule 2 (lower (has_type $I32 (iadd _ (imul _ a b) c))) (pulley_xmadd32 a b c)) +(rule 3 (lower (has_type $I32 (iadd _ c (imul _ a b)))) (pulley_xmadd32 a b c)) +(rule 2 (lower (has_type $I64 (iadd _ (imul _ a b) c))) (pulley_xmadd64 a b c)) +(rule 3 (lower (has_type $I64 (iadd _ c (imul _ a b)))) (pulley_xmadd64 a b c)) ;;;; Rules for `iadd_pairwise` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I16X8 (iadd_pairwise a b))) (pulley_vaddpairwisei16x8_s a b)) -(rule (lower (has_type $I32X4 (iadd_pairwise a b))) (pulley_vaddpairwisei32x4_s a b)) +(rule (lower (has_type $I16X8 (iadd_pairwise _ a b))) (pulley_vaddpairwisei16x8_s a b)) +(rule (lower (has_type $I32X4 (iadd_pairwise _ a b))) (pulley_vaddpairwisei32x4_s a b)) ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (isub a b))) (pulley_xsub32 a b)) -(rule 1 (lower (has_type $I64 (isub a b))) (pulley_xsub64 a b)) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (isub _ a b))) (pulley_xsub32 a b)) +(rule 1 (lower (has_type $I64 (isub _ a b))) (pulley_xsub64 a b)) ;; Fold a rhs constant into the instruction if possible. -(rule 2 (lower (has_type (ty_int (fits_in_32 _)) (isub a (u32_from_iconst b)))) +(rule 2 (lower (has_type (ty_int (fits_in_32 _)) (isub _ a (u32_from_iconst b)))) (pulley_xsub32_u32 a b)) -(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (isub a (u8_from_iconst b)))) +(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (isub _ a (u8_from_iconst b)))) (pulley_xsub32_u8 a b)) -(rule 4 (lower (has_type $I64 (isub a (u32_from_iconst b)))) +(rule 4 (lower (has_type $I64 (isub _ a (u32_from_iconst b)))) (pulley_xsub64_u32 a b)) -(rule 5 (lower (has_type $I64 (isub a (u8_from_iconst b)))) +(rule 5 (lower (has_type $I64 (isub _ a (u8_from_iconst b)))) (pulley_xsub64_u8 a b)) ;; If the rhs is a constant and the negated version can fit within a smaller ;; constant then switch this to an addition with the negated constant. -(rule 6 (lower (has_type (ty_int (fits_in_32 _)) (isub a b))) +(rule 6 (lower (has_type (ty_int (fits_in_32 _)) (isub _ a b))) (if-let c (u32_from_negated_iconst b)) (pulley_xadd32_u32 a c)) -(rule 7 (lower (has_type $I64 (isub a b))) +(rule 7 (lower (has_type $I64 (isub _ a b))) (if-let c (u32_from_negated_iconst b)) (pulley_xadd64_u32 a c)) -(rule 8 (lower (has_type (ty_int (fits_in_32 _)) (isub a b))) +(rule 8 (lower (has_type (ty_int (fits_in_32 _)) (isub _ a b))) (if-let c (u8_from_negated_iconst b)) (pulley_xadd32_u8 a c)) -(rule 9 (lower (has_type $I64 (isub a b))) +(rule 9 (lower (has_type $I64 (isub _ a b))) (if-let c (u8_from_negated_iconst b)) (pulley_xadd64_u8 a c)) ;; 128-bit subtraction -(rule 1 (lower (has_type $I128 (isub a b))) +(rule 1 (lower (has_type $I128 (isub _ a b))) (let ((a ValueRegs a) (b ValueRegs b)) (pulley_xsub128 @@ -362,40 +362,40 @@ (value_regs_get b 1)))) ;; vector subtraction -(rule 1 (lower (has_type $I8X16 (isub a b))) (pulley_vsubi8x16 a b)) -(rule 1 (lower (has_type $I16X8 (isub a b))) (pulley_vsubi16x8 a b)) -(rule 1 (lower (has_type $I32X4 (isub a b))) (pulley_vsubi32x4 a b)) -(rule 1 (lower (has_type $I64X2 (isub a b))) (pulley_vsubi64x2 a b)) +(rule 1 (lower (has_type $I8X16 (isub _ a b))) (pulley_vsubi8x16 a b)) +(rule 1 (lower (has_type $I16X8 (isub _ a b))) (pulley_vsubi16x8 a b)) +(rule 1 (lower (has_type $I32X4 (isub _ a b))) (pulley_vsubi32x4 a b)) +(rule 1 (lower (has_type $I64X2 (isub _ a b))) (pulley_vsubi64x2 a b)) -(rule 1 (lower (has_type $I8X16 (ssub_sat a b))) (pulley_vsubi8x16_sat a b)) -(rule 1 (lower (has_type $I8X16 (usub_sat a b))) (pulley_vsubu8x16_sat a b)) -(rule 1 (lower (has_type $I16X8 (ssub_sat a b))) (pulley_vsubi16x8_sat a b)) -(rule 1 (lower (has_type $I16X8 (usub_sat a b))) (pulley_vsubu16x8_sat a b)) +(rule 1 (lower (has_type $I8X16 (ssub_sat _ a b))) (pulley_vsubi8x16_sat a b)) +(rule 1 (lower (has_type $I8X16 (usub_sat _ a b))) (pulley_vsubu8x16_sat a b)) +(rule 1 (lower (has_type $I16X8 (ssub_sat _ a b))) (pulley_vsubi16x8_sat a b)) +(rule 1 (lower (has_type $I16X8 (usub_sat _ a b))) (pulley_vsubu16x8_sat a b)) ;;;; Rules for `imul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8 (imul a b))) (pulley_xmul32 a b)) -(rule (lower (has_type $I16 (imul a b))) (pulley_xmul32 a b)) -(rule (lower (has_type $I32 (imul a b))) (pulley_xmul32 a b)) -(rule (lower (has_type $I64 (imul a b))) (pulley_xmul64 a b)) +(rule (lower (has_type $I8 (imul _ a b))) (pulley_xmul32 a b)) +(rule (lower (has_type $I16 (imul _ a b))) (pulley_xmul32 a b)) +(rule (lower (has_type $I32 (imul _ a b))) (pulley_xmul32 a b)) +(rule (lower (has_type $I64 (imul _ a b))) (pulley_xmul64 a b)) -(rule 1 (lower (has_type (ty_int (fits_in_32 _)) (imul a (i32_from_iconst b)))) +(rule 1 (lower (has_type (ty_int (fits_in_32 _)) (imul _ a (i32_from_iconst b)))) (pulley_xmul32_s32 a b)) -(rule 2 (lower (has_type $I64 (imul a (i32_from_iconst b)))) +(rule 2 (lower (has_type $I64 (imul _ a (i32_from_iconst b)))) (pulley_xmul64_s32 a b)) -(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (imul a (i8_from_iconst b)))) +(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (imul _ a (i8_from_iconst b)))) (pulley_xmul32_s8 a b)) -(rule 4 (lower (has_type $I64 (imul a (i8_from_iconst b)))) +(rule 4 (lower (has_type $I64 (imul _ a (i8_from_iconst b)))) (pulley_xmul64_s8 a b)) ;; 128-bit (or wide) multiplication -(rule 4 (lower (has_type $I128 (imul (uextend a) (uextend b)))) +(rule 4 (lower (has_type $I128 (imul _ (uextend _ a) (uextend _ b)))) (pulley_xwidemul64_u (zext64 a) (zext64 b))) -(rule 4 (lower (has_type $I128 (imul (sextend a) (sextend b)))) +(rule 4 (lower (has_type $I128 (imul _ (sextend _ a) (sextend _ b)))) (pulley_xwidemul64_s (sext64 a) (sext64 b))) ;; for I128 -(rule (lower (has_type $I128 (imul x y))) +(rule (lower (has_type $I128 (imul _ x y))) (let ((x_regs ValueRegs x) (x_lo XReg (value_regs_get x_regs 0)) @@ -420,106 +420,106 @@ (value_regs wide_lo result_hi))) ;; vector multiplication -(rule (lower (has_type $I8X16 (imul a b))) (pulley_vmuli8x16 a b)) -(rule (lower (has_type $I16X8 (imul a b))) (pulley_vmuli16x8 a b)) -(rule (lower (has_type $I32X4 (imul a b))) (pulley_vmuli32x4 a b)) -(rule (lower (has_type $I64X2 (imul a b))) (pulley_vmuli64x2 a b)) +(rule (lower (has_type $I8X16 (imul _ a b))) (pulley_vmuli8x16 a b)) +(rule (lower (has_type $I16X8 (imul _ a b))) (pulley_vmuli16x8 a b)) +(rule (lower (has_type $I32X4 (imul _ a b))) (pulley_vmuli32x4 a b)) +(rule (lower (has_type $I64X2 (imul _ a b))) (pulley_vmuli64x2 a b)) ;;;; Rules for `umulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8 (umulhi a b))) +(rule (lower (has_type $I8 (umulhi _ a b))) (if-let (u6_from_u8 shift) (u64_try_into_u8 8)) (pulley_xshr32_u_u6 (pulley_xmul32 (zext32 a) (zext32 b)) shift)) -(rule (lower (has_type $I16 (umulhi a b))) +(rule (lower (has_type $I16 (umulhi _ a b))) (if-let (u6_from_u8 shift) (u64_try_into_u8 16)) (pulley_xshr32_u_u6 (pulley_xmul32 (zext32 a) (zext32 b)) shift)) -(rule (lower (has_type $I32 (umulhi a b))) +(rule (lower (has_type $I32 (umulhi _ a b))) (if-let (u6_from_u8 shift) (u64_try_into_u8 32)) (pulley_xshr64_u_u6 (pulley_xmul64 (zext64 a) (zext64 b)) shift)) -(rule (lower (has_type $I64 (umulhi a b))) +(rule (lower (has_type $I64 (umulhi _ a b))) (pulley_xmulhi64_u a b)) ;;;; Rules for `smulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8 (smulhi a b))) +(rule (lower (has_type $I8 (smulhi _ a b))) (if-let (u6_from_u8 shift) (u64_try_into_u8 8)) (pulley_xshr32_s_u6 (pulley_xmul32 (sext32 a) (sext32 b)) shift)) -(rule (lower (has_type $I16 (smulhi a b))) +(rule (lower (has_type $I16 (smulhi _ a b))) (if-let (u6_from_u8 shift) (u64_try_into_u8 16)) (pulley_xshr32_s_u6 (pulley_xmul32 (sext32 a) (sext32 b)) shift)) -(rule (lower (has_type $I32 (smulhi a b))) +(rule (lower (has_type $I32 (smulhi _ a b))) (if-let (u6_from_u8 shift) (u64_try_into_u8 32)) (pulley_xshr64_s_u6 (pulley_xmul64 (sext64 a) (sext64 b)) shift)) -(rule (lower (has_type $I64 (smulhi a b))) +(rule (lower (has_type $I64 (smulhi _ a b))) (pulley_xmulhi64_s a b)) ;;;; Rules for `sqmul_round_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I16X8 (sqmul_round_sat a b))) (pulley_vqmulrsi16x8 a b)) +(rule (lower (has_type $I16X8 (sqmul_round_sat _ a b))) (pulley_vqmulrsi16x8 a b)) ;;;; Rules for `sdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (sdiv a b))) +(rule 0 (lower (has_type (fits_in_32 _) (sdiv _ a b))) (pulley_xdiv32_s (sext32 a) (sext32 b))) -(rule 1 (lower (has_type $I64 (sdiv a b))) (pulley_xdiv64_s a b)) +(rule 1 (lower (has_type $I64 (sdiv _ a b))) (pulley_xdiv64_s a b)) ;;;; Rules for `srem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (srem a b))) +(rule 0 (lower (has_type (fits_in_32 _) (srem _ a b))) (pulley_xrem32_s (sext32 a) (sext32 b))) -(rule 1 (lower (has_type $I64 (srem a b))) (pulley_xrem64_s a b)) +(rule 1 (lower (has_type $I64 (srem _ a b))) (pulley_xrem64_s a b)) ;;;; Rules for `udiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (udiv a b))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (udiv _ a b))) (pulley_xdiv32_u (zext32 a) (zext32 b))) -(rule 1 (lower (has_type $I64 (udiv a b))) (pulley_xdiv64_u a b)) +(rule 1 (lower (has_type $I64 (udiv _ a b))) (pulley_xdiv64_u a b)) ;;;; Rules for `urem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (urem a b))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (urem _ a b))) (pulley_xrem32_u (zext32 a) (zext32 b))) -(rule 1 (lower (has_type $I64 (urem a b))) (pulley_xrem64_u a b)) +(rule 1 (lower (has_type $I64 (urem _ a b))) (pulley_xrem64_u a b)) ;;;; Rules for `avg_round` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8X16 (avg_round a b))) (pulley_vavground8x16 a b)) -(rule (lower (has_type $I16X8 (avg_round a b))) (pulley_vavground16x8 a b)) +(rule (lower (has_type $I8X16 (avg_round _ a b))) (pulley_vavground8x16 a b)) +(rule (lower (has_type $I16X8 (avg_round _ a b))) (pulley_vavground16x8 a b)) ;;;; Rules for `ishl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8 (ishl a b))) +(rule (lower (has_type $I8 (ishl _ a b))) (pulley_xshl32 a (pulley_xband32_s8 b 7))) -(rule (lower (has_type $I16 (ishl a b))) +(rule (lower (has_type $I16 (ishl _ a b))) (pulley_xshl32 a (pulley_xband32_s8 b 15))) -(rule (lower (has_type $I32 (ishl a b))) +(rule (lower (has_type $I32 (ishl _ a b))) (pulley_xshl32 a b)) -(rule (lower (has_type $I64 (ishl a b))) +(rule (lower (has_type $I64 (ishl _ a b))) (pulley_xshl64 a b)) ;; Special-case constant shift amounts. -(rule 1 (lower (has_type $I32 (ishl a b))) +(rule 1 (lower (has_type $I32 (ishl _ a b))) (if-let n (u6_shift_from_iconst b)) (pulley_xshl32_u6 a n)) -(rule 1 (lower (has_type $I64 (ishl a b))) +(rule 1 (lower (has_type $I64 (ishl _ a b))) (if-let n (u6_shift_from_iconst b)) (pulley_xshl64_u6 a n)) ;; vector shifts -(rule (lower (has_type $I8X16 (ishl a b))) (pulley_vshli8x16 a b)) -(rule (lower (has_type $I16X8 (ishl a b))) (pulley_vshli16x8 a b)) -(rule (lower (has_type $I32X4 (ishl a b))) (pulley_vshli32x4 a b)) -(rule (lower (has_type $I64X2 (ishl a b))) (pulley_vshli64x2 a b)) +(rule (lower (has_type $I8X16 (ishl _ a b))) (pulley_vshli8x16 a b)) +(rule (lower (has_type $I16X8 (ishl _ a b))) (pulley_vshli16x8 a b)) +(rule (lower (has_type $I32X4 (ishl _ a b))) (pulley_vshli32x4 a b)) +(rule (lower (has_type $I64X2 (ishl _ a b))) (pulley_vshli64x2 a b)) ;; Helper to extract a constant from `Value`, mask it to 6 bits, and then make a ;; `U6`. @@ -533,211 +533,211 @@ ;;;; Rules for `ushr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8 (ushr a b))) +(rule (lower (has_type $I8 (ushr _ a b))) (pulley_xshr32_u (zext32 a) (pulley_xband32_s8 b 7))) -(rule (lower (has_type $I16 (ushr a b))) +(rule (lower (has_type $I16 (ushr _ a b))) (pulley_xshr32_u (zext32 a) (pulley_xband32_s8 b 15))) -(rule (lower (has_type $I32 (ushr a b))) +(rule (lower (has_type $I32 (ushr _ a b))) (pulley_xshr32_u a b)) -(rule (lower (has_type $I64 (ushr a b))) +(rule (lower (has_type $I64 (ushr _ a b))) (pulley_xshr64_u a b)) ;; Special-case constant shift amounts. -(rule 1 (lower (has_type $I32 (ushr a b))) +(rule 1 (lower (has_type $I32 (ushr _ a b))) (if-let n (u6_shift_from_iconst b)) (pulley_xshr32_u_u6 a n)) -(rule 1 (lower (has_type $I64 (ushr a b))) +(rule 1 (lower (has_type $I64 (ushr _ a b))) (if-let n (u6_shift_from_iconst b)) (pulley_xshr64_u_u6 a n)) ;; vector shifts -(rule (lower (has_type $I8X16 (ushr a b))) (pulley_vshri8x16_u a b)) -(rule (lower (has_type $I16X8 (ushr a b))) (pulley_vshri16x8_u a b)) -(rule (lower (has_type $I32X4 (ushr a b))) (pulley_vshri32x4_u a b)) -(rule (lower (has_type $I64X2 (ushr a b))) (pulley_vshri64x2_u a b)) +(rule (lower (has_type $I8X16 (ushr _ a b))) (pulley_vshri8x16_u a b)) +(rule (lower (has_type $I16X8 (ushr _ a b))) (pulley_vshri16x8_u a b)) +(rule (lower (has_type $I32X4 (ushr _ a b))) (pulley_vshri32x4_u a b)) +(rule (lower (has_type $I64X2 (ushr _ a b))) (pulley_vshri64x2_u a b)) ;;;; Rules for `sshr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8 (sshr a b))) +(rule (lower (has_type $I8 (sshr _ a b))) (pulley_xshr32_u (sext32 a) (pulley_xband32_s8 b 7))) -(rule (lower (has_type $I16 (sshr a b))) +(rule (lower (has_type $I16 (sshr _ a b))) (pulley_xshr32_u (sext32 a) (pulley_xband32_s8 b 15))) -(rule (lower (has_type $I32 (sshr a b))) +(rule (lower (has_type $I32 (sshr _ a b))) (pulley_xshr32_s a b)) -(rule (lower (has_type $I64 (sshr a b))) +(rule (lower (has_type $I64 (sshr _ a b))) (pulley_xshr64_s a b)) ;; Special-case constant shift amounts. -(rule 1 (lower (has_type $I32 (sshr a b))) +(rule 1 (lower (has_type $I32 (sshr _ a b))) (if-let n (u6_shift_from_iconst b)) (pulley_xshr32_s_u6 a n)) -(rule 1 (lower (has_type $I64 (sshr a b))) +(rule 1 (lower (has_type $I64 (sshr _ a b))) (if-let n (u6_shift_from_iconst b)) (pulley_xshr64_s_u6 a n)) ;; vector shifts -(rule (lower (has_type $I8X16 (sshr a b))) (pulley_vshri8x16_s a b)) -(rule (lower (has_type $I16X8 (sshr a b))) (pulley_vshri16x8_s a b)) -(rule (lower (has_type $I32X4 (sshr a b))) (pulley_vshri32x4_s a b)) -(rule (lower (has_type $I64X2 (sshr a b))) (pulley_vshri64x2_s a b)) +(rule (lower (has_type $I8X16 (sshr _ a b))) (pulley_vshri8x16_s a b)) +(rule (lower (has_type $I16X8 (sshr _ a b))) (pulley_vshri16x8_s a b)) +(rule (lower (has_type $I32X4 (sshr _ a b))) (pulley_vshri32x4_s a b)) +(rule (lower (has_type $I64X2 (sshr _ a b))) (pulley_vshri64x2_s a b)) ;;;; Rules for `band` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (band a b))) (pulley_xband32 a b)) -(rule 1 (lower (has_type $I64 (band a b))) (pulley_xband64 a b)) +(rule 0 (lower (has_type (fits_in_32 _) (band _ a b))) (pulley_xband32 a b)) +(rule 1 (lower (has_type $I64 (band _ a b))) (pulley_xband64 a b)) -(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (band a (i32_from_iconst b)))) +(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (band _ a (i32_from_iconst b)))) (pulley_xband32_s32 a b)) -(rule 4 (lower (has_type $I64 (band a (i32_from_iconst b)))) +(rule 4 (lower (has_type $I64 (band _ a (i32_from_iconst b)))) (pulley_xband64_s32 a b)) -(rule 5 (lower (has_type (ty_int (fits_in_32 _)) (band a (i8_from_iconst b)))) +(rule 5 (lower (has_type (ty_int (fits_in_32 _)) (band _ a (i8_from_iconst b)))) (pulley_xband32_s8 a b)) -(rule 6 (lower (has_type $I64 (band a (i8_from_iconst b)))) +(rule 6 (lower (has_type $I64 (band _ a (i8_from_iconst b)))) (pulley_xband64_s8 a b)) -(rule 2 (lower (has_type (ty_vec128 _) (band a b))) +(rule 2 (lower (has_type (ty_vec128 _) (band _ a b))) (pulley_vband128 a b)) ;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (bor a b))) (pulley_xbor32 a b)) -(rule 1 (lower (has_type $I64 (bor a b))) (pulley_xbor64 a b)) +(rule 0 (lower (has_type (fits_in_32 _) (bor _ a b))) (pulley_xbor32 a b)) +(rule 1 (lower (has_type $I64 (bor _ a b))) (pulley_xbor64 a b)) -(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (bor a (i32_from_iconst b)))) +(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (bor _ a (i32_from_iconst b)))) (pulley_xbor32_s32 a b)) -(rule 4 (lower (has_type $I64 (bor a (i32_from_iconst b)))) +(rule 4 (lower (has_type $I64 (bor _ a (i32_from_iconst b)))) (pulley_xbor64_s32 a b)) -(rule 5 (lower (has_type (ty_int (fits_in_32 _)) (bor a (i8_from_iconst b)))) +(rule 5 (lower (has_type (ty_int (fits_in_32 _)) (bor _ a (i8_from_iconst b)))) (pulley_xbor32_s8 a b)) -(rule 6 (lower (has_type $I64 (bor a (i8_from_iconst b)))) +(rule 6 (lower (has_type $I64 (bor _ a (i8_from_iconst b)))) (pulley_xbor64_s8 a b)) -(rule 2 (lower (has_type (ty_vec128 _) (bor a b))) +(rule 2 (lower (has_type (ty_vec128 _) (bor _ a b))) (pulley_vbor128 a b)) ;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (bxor a b))) (pulley_xbxor32 a b)) -(rule 1 (lower (has_type $I64 (bxor a b))) (pulley_xbxor64 a b)) +(rule 0 (lower (has_type (fits_in_32 _) (bxor _ a b))) (pulley_xbxor32 a b)) +(rule 1 (lower (has_type $I64 (bxor _ a b))) (pulley_xbxor64 a b)) -(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (bxor a (i32_from_iconst b)))) +(rule 3 (lower (has_type (ty_int (fits_in_32 _)) (bxor _ a (i32_from_iconst b)))) (pulley_xbxor32_s32 a b)) -(rule 4 (lower (has_type $I64 (bxor a (i32_from_iconst b)))) +(rule 4 (lower (has_type $I64 (bxor _ a (i32_from_iconst b)))) (pulley_xbxor64_s32 a b)) -(rule 5 (lower (has_type (ty_int (fits_in_32 _)) (bxor a (i8_from_iconst b)))) +(rule 5 (lower (has_type (ty_int (fits_in_32 _)) (bxor _ a (i8_from_iconst b)))) (pulley_xbxor32_s8 a b)) -(rule 6 (lower (has_type $I64 (bxor a (i8_from_iconst b)))) +(rule 6 (lower (has_type $I64 (bxor _ a (i8_from_iconst b)))) (pulley_xbxor64_s8 a b)) -(rule 2 (lower (has_type (ty_vec128 _) (bxor a b))) +(rule 2 (lower (has_type (ty_vec128 _) (bxor _ a b))) (pulley_vbxor128 a b)) ;;;; Rules for `bnot` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (bnot a))) +(rule 0 (lower (has_type (fits_in_32 _) (bnot _ a))) (pulley_xbnot32 a)) -(rule 1 (lower (has_type $I64 (bnot a))) +(rule 1 (lower (has_type $I64 (bnot _ a))) (pulley_xbnot64 a)) -(rule 2 (lower (has_type (ty_vec128 _) (bnot a))) +(rule 2 (lower (has_type (ty_vec128 _) (bnot _ a))) (pulley_vbnot128 a)) ;;;; Rules for `bitselect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_vec128 _) (bitselect c x y))) +(rule (lower (has_type (ty_vec128 _) (bitselect _ c x y))) (pulley_vbitselect128 c x y)) ;;;; Rules for `umin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (umin a b))) +(rule 0 (lower (has_type (fits_in_32 _) (umin _ a b))) (pulley_xmin32_u (zext32 a) (zext32 b))) -(rule 1 (lower (has_type $I64 (umin a b))) (pulley_xmin64_u a b)) -(rule 1 (lower (has_type $I8X16 (umin a b))) (pulley_vmin8x16_u a b)) -(rule 1 (lower (has_type $I16X8 (umin a b))) (pulley_vmin16x8_u a b)) -(rule 1 (lower (has_type $I32X4 (umin a b))) (pulley_vmin32x4_u a b)) +(rule 1 (lower (has_type $I64 (umin _ a b))) (pulley_xmin64_u a b)) +(rule 1 (lower (has_type $I8X16 (umin _ a b))) (pulley_vmin8x16_u a b)) +(rule 1 (lower (has_type $I16X8 (umin _ a b))) (pulley_vmin16x8_u a b)) +(rule 1 (lower (has_type $I32X4 (umin _ a b))) (pulley_vmin32x4_u a b)) ;;;; Rules for `smin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (smin a b))) +(rule 0 (lower (has_type (fits_in_32 _) (smin _ a b))) (pulley_xmin32_s (sext32 a) (sext32 b))) -(rule 1 (lower (has_type $I64 (smin a b))) (pulley_xmin64_s a b)) -(rule 1 (lower (has_type $I8X16 (smin a b))) (pulley_vmin8x16_s a b)) -(rule 1 (lower (has_type $I16X8 (smin a b))) (pulley_vmin16x8_s a b)) -(rule 1 (lower (has_type $I32X4 (smin a b))) (pulley_vmin32x4_s a b)) +(rule 1 (lower (has_type $I64 (smin _ a b))) (pulley_xmin64_s a b)) +(rule 1 (lower (has_type $I8X16 (smin _ a b))) (pulley_vmin8x16_s a b)) +(rule 1 (lower (has_type $I16X8 (smin _ a b))) (pulley_vmin16x8_s a b)) +(rule 1 (lower (has_type $I32X4 (smin _ a b))) (pulley_vmin32x4_s a b)) ;;;; Rules for `umax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (umax a b))) +(rule 0 (lower (has_type (fits_in_32 _) (umax _ a b))) (pulley_xmax32_u (zext32 a) (zext32 b))) -(rule 1 (lower (has_type $I64 (umax a b))) (pulley_xmax64_u a b)) -(rule 1 (lower (has_type $I8X16 (umax a b))) (pulley_vmax8x16_u a b)) -(rule 1 (lower (has_type $I16X8 (umax a b))) (pulley_vmax16x8_u a b)) -(rule 1 (lower (has_type $I32X4 (umax a b))) (pulley_vmax32x4_u a b)) +(rule 1 (lower (has_type $I64 (umax _ a b))) (pulley_xmax64_u a b)) +(rule 1 (lower (has_type $I8X16 (umax _ a b))) (pulley_vmax8x16_u a b)) +(rule 1 (lower (has_type $I16X8 (umax _ a b))) (pulley_vmax16x8_u a b)) +(rule 1 (lower (has_type $I32X4 (umax _ a b))) (pulley_vmax32x4_u a b)) ;;;; Rules for `smax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (smax a b))) +(rule 0 (lower (has_type (fits_in_32 _) (smax _ a b))) (pulley_xmax32_s (sext32 a) (sext32 b))) -(rule 1 (lower (has_type $I64 (smax a b))) (pulley_xmax64_s a b)) -(rule 1 (lower (has_type $I8X16 (smax a b))) (pulley_vmax8x16_s a b)) -(rule 1 (lower (has_type $I16X8 (smax a b))) (pulley_vmax16x8_s a b)) -(rule 1 (lower (has_type $I32X4 (smax a b))) (pulley_vmax32x4_s a b)) +(rule 1 (lower (has_type $I64 (smax _ a b))) (pulley_xmax64_s a b)) +(rule 1 (lower (has_type $I8X16 (smax _ a b))) (pulley_vmax8x16_s a b)) +(rule 1 (lower (has_type $I16X8 (smax _ a b))) (pulley_vmax16x8_s a b)) +(rule 1 (lower (has_type $I32X4 (smax _ a b))) (pulley_vmax32x4_s a b)) ;;;; Rules for `bmask` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (bmask a @ (value_type (fits_in_32 _))))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (bmask _ a @ (value_type (fits_in_32 _))))) (pulley_xbmask32 (zext32 a))) -(rule 1 (lower (has_type $I64 (bmask a))) +(rule 1 (lower (has_type $I64 (bmask _ a))) (pulley_xbmask64 (zext64 a))) -(rule 2 (lower (bmask a @ (value_type $I64))) +(rule 2 (lower (bmask _ a @ (value_type $I64))) (pulley_xbmask64 a)) ;;;; Rules for `ctz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8 (ctz a))) +(rule (lower (has_type $I8 (ctz _ a))) (pulley_xctz32 (pulley_xbor32_s32 a 0x100))) -(rule (lower (has_type $I16 (ctz a))) +(rule (lower (has_type $I16 (ctz _ a))) (pulley_xctz32 (pulley_xbor32_s32 a 0x10000))) -(rule (lower (has_type $I32 (ctz a))) (pulley_xctz32 a)) -(rule (lower (has_type $I64 (ctz a))) (pulley_xctz64 a)) +(rule (lower (has_type $I32 (ctz _ a))) (pulley_xctz32 a)) +(rule (lower (has_type $I64 (ctz _ a))) (pulley_xctz64 a)) ;;;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8 (clz a))) +(rule (lower (has_type $I8 (clz _ a))) (pulley_xsub32_u8 (pulley_xclz32 (zext32 a)) 24)) -(rule (lower (has_type $I16 (clz a))) +(rule (lower (has_type $I16 (clz _ a))) (pulley_xsub32_u8 (pulley_xclz32 (zext32 a)) 16)) -(rule (lower (has_type $I32 (clz a))) (pulley_xclz32 a)) -(rule (lower (has_type $I64 (clz a))) (pulley_xclz64 a)) +(rule (lower (has_type $I32 (clz _ a))) (pulley_xclz32 a)) +(rule (lower (has_type $I64 (clz _ a))) (pulley_xclz64 a)) ;;;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (popcnt a))) (pulley_xpopcnt32 (zext32 a))) -(rule 1 (lower (has_type $I64 (popcnt a))) (pulley_xpopcnt64 a)) -(rule 1 (lower (has_type $I8X16 (popcnt a))) (pulley_vpopcnt8x16 a)) +(rule 0 (lower (has_type (fits_in_32 _) (popcnt _ a))) (pulley_xpopcnt32 (zext32 a))) +(rule 1 (lower (has_type $I64 (popcnt _ a))) (pulley_xpopcnt64 a)) +(rule 1 (lower (has_type $I8X16 (popcnt _ a))) (pulley_vpopcnt8x16 a)) ;;;; Rules for `rotl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I32 (rotl a b))) (pulley_xrotl32 a b)) -(rule (lower (has_type $I64 (rotl a b))) (pulley_xrotl64 a b)) +(rule (lower (has_type $I32 (rotl _ a b))) (pulley_xrotl32 a b)) +(rule (lower (has_type $I64 (rotl _ a b))) (pulley_xrotl64 a b)) ;;;; Rules for `rotr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I32 (rotr a b))) (pulley_xrotr32 a b)) -(rule (lower (has_type $I64 (rotr a b))) (pulley_xrotr64 a b)) +(rule (lower (has_type $I32 (rotr _ a b))) (pulley_xrotr32 a b)) +(rule (lower (has_type $I64 (rotr _ a b))) (pulley_xrotr64 a b)) ;;;; Rules for `icmp` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (icmp cc a b @ (value_type (ty_int ty)))) +(rule (lower (icmp _ cc a b @ (value_type (ty_int ty)))) (lower_icmp ty cc a b)) ; Recursion: bounded since only recursive rules swap condition code order from @@ -848,7 +848,7 @@ ;; vector comparisons -(rule 1 (lower (icmp cc a @ (value_type (ty_vec128 ty)) b)) +(rule 1 (lower (icmp _ cc a @ (value_type (ty_vec128 ty)) b)) (lower_vcmp ty cc a b)) ; Recursion: bounded since only recursive rules swap condition code order from @@ -891,10 +891,10 @@ ;;;; Rules for `fcmp` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (fcmp cc a b @ (value_type (ty_scalar_float ty)))) +(rule (lower (fcmp _ cc a b @ (value_type (ty_scalar_float ty)))) (lower_fcmp ty cc a b)) -(rule 1 (lower (fcmp cc a b @ (value_type (ty_vec128 ty)))) +(rule 1 (lower (fcmp _ cc a b @ (value_type (ty_vec128 ty)))) (lower_vfcmp ty cc a b)) ; Recursion: bounded since recursive rules only implement certain condition @@ -960,73 +960,73 @@ ;;;; Rules for `load` and friends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_int (fits_in_64 ty)) (load flags addr offset))) +(rule (lower (has_type (ty_int (fits_in_64 ty)) (load _ flags addr offset))) (gen_xload addr offset flags ty (ExtKind.None))) -(rule 1 (lower (has_type (ty_scalar_float ty) (load flags addr offset))) +(rule 1 (lower (has_type (ty_scalar_float ty) (load _ flags addr offset))) (gen_fload addr offset flags ty)) -(rule 3 (lower (has_type $I128 (load flags addr offset))) +(rule 3 (lower (has_type $I128 (load _ flags addr offset))) (if-let offsetp8 (i32_checked_add offset 8)) (let ((lo XReg (pulley_xload (amode addr offset) $I64 flags)) (hi XReg (pulley_xload (amode addr offsetp8) $I64 flags))) (value_regs lo hi))) -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (uload8 flags addr offset))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (uload8 _ flags addr offset))) (gen_xload addr offset flags $I8 (ExtKind.Zero32))) -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (uload16 flags addr offset))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (uload16 _ flags addr offset))) (gen_xload addr offset flags $I16 (ExtKind.Zero32))) -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (uload32 flags addr offset))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (uload32 _ flags addr offset))) (gen_xload addr offset flags $I32 (ExtKind.None))) -(rule 1 (lower (has_type $I64 (uload8 flags addr offset))) +(rule 1 (lower (has_type $I64 (uload8 _ flags addr offset))) (gen_xload addr offset flags $I8 (ExtKind.Zero64))) -(rule 1 (lower (has_type $I64 (uload16 flags addr offset))) +(rule 1 (lower (has_type $I64 (uload16 _ flags addr offset))) (gen_xload addr offset flags $I16 (ExtKind.Zero64))) -(rule 1 (lower (has_type $I64 (uload32 flags addr offset))) +(rule 1 (lower (has_type $I64 (uload32 _ flags addr offset))) (gen_xload addr offset flags $I32 (ExtKind.Zero64))) -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (sload8 flags addr offset))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (sload8 _ flags addr offset))) (gen_xload addr offset flags $I8 (ExtKind.Sign32))) -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (sload16 flags addr offset))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (sload16 _ flags addr offset))) (gen_xload addr offset flags $I16 (ExtKind.Sign32))) -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (sload32 flags addr offset))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (sload32 _ flags addr offset))) (gen_xload addr offset flags $I32 (ExtKind.None))) -(rule 1 (lower (has_type $I64 (sload8 flags addr offset))) +(rule 1 (lower (has_type $I64 (sload8 _ flags addr offset))) (gen_xload addr offset flags $I8 (ExtKind.Sign64))) -(rule 1 (lower (has_type $I64 (sload16 flags addr offset))) +(rule 1 (lower (has_type $I64 (sload16 _ flags addr offset))) (gen_xload addr offset flags $I16 (ExtKind.Sign64))) -(rule 1 (lower (has_type $I64 (sload32 flags addr offset))) +(rule 1 (lower (has_type $I64 (sload32 _ flags addr offset))) (gen_xload addr offset flags $I32 (ExtKind.Sign64))) -(rule 2 (lower (has_type (ty_vec128 ty) (load flags addr offset))) +(rule 2 (lower (has_type (ty_vec128 ty) (load _ flags addr offset))) (gen_vload addr offset flags ty (VExtKind.None))) -(rule (lower (has_type ty (sload8x8 flags addr offset))) +(rule (lower (has_type ty (sload8x8 _ flags addr offset))) (gen_vload addr offset flags ty (VExtKind.S8x8))) -(rule (lower (has_type ty (uload8x8 flags addr offset))) +(rule (lower (has_type ty (uload8x8 _ flags addr offset))) (gen_vload addr offset flags ty (VExtKind.U8x8))) -(rule (lower (has_type ty (sload16x4 flags addr offset))) +(rule (lower (has_type ty (sload16x4 _ flags addr offset))) (gen_vload addr offset flags ty (VExtKind.S16x4))) -(rule (lower (has_type ty (uload16x4 flags addr offset))) +(rule (lower (has_type ty (uload16x4 _ flags addr offset))) (gen_vload addr offset flags ty (VExtKind.U16x4))) -(rule (lower (has_type ty (sload32x2 flags addr offset))) +(rule (lower (has_type ty (sload32x2 _ flags addr offset))) (gen_vload addr offset flags ty (VExtKind.S32x2))) -(rule (lower (has_type ty (uload32x2 flags addr offset))) +(rule (lower (has_type ty (uload32x2 _ flags addr offset))) (gen_vload addr offset flags ty (VExtKind.U32x2))) ;; Helper to generate an `xload*` instruction, of which there are many. This @@ -1293,7 +1293,7 @@ ;;;; Rules for `stack_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (stack_addr stack_slot offset)) +(rule (lower (stack_addr _ stack_slot offset)) (lower_stack_addr stack_slot offset)) (decl lower_stack_addr (StackSlot Offset32) XReg) @@ -1304,24 +1304,24 @@ ;;;; Rules for `uextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (uextend val))) +(rule 0 (lower (has_type (fits_in_32 _) (uextend _ val))) (zext32 val)) -(rule 1 (lower (has_type $I64 (uextend val))) +(rule 1 (lower (has_type $I64 (uextend _ val))) (zext64 val)) -(rule 1 (lower (has_type $I128 (uextend val))) +(rule 1 (lower (has_type $I128 (uextend _ val))) (value_regs (zext64 val) (pulley_xzero))) ;;;; Rules for `sextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (sextend val))) +(rule 0 (lower (has_type (fits_in_32 _) (sextend _ val))) (sext32 val)) -(rule 1 (lower (has_type $I64 (sextend val))) +(rule 1 (lower (has_type $I64 (sextend _ val))) (sext64 val)) -(rule 1 (lower (has_type $I128 (sextend val))) +(rule 1 (lower (has_type $I128 (sextend _ val))) (if-let (u6_from_u8 shift) (u64_try_into_u8 63)) (let ((lo XReg (sext64 val)) (hi XReg (pulley_xshr64_s_u6 lo shift))) @@ -1329,56 +1329,56 @@ ;;;; Rules for `ireduce` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_64 _ty) (ireduce src))) +(rule (lower (has_type (fits_in_64 _ty) (ireduce _ src))) src) ;;;; Rules for `iconcat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I128 (iconcat a b))) +(rule (lower (has_type $I128 (iconcat _ a b))) (value_regs a b)) ;;;; Rules for `isplit` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (isplit x @ (value_type $I128))) +(rule (lower (isplit _ x @ (value_type $I128))) (let ((lo XReg (value_regs_get x 0)) (hi XReg (value_regs_get x 1))) (output_pair lo hi))) ;;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I32 (uadd_overflow_trap a b tc))) +(rule (lower (has_type $I32 (uadd_overflow_trap _ a b tc))) (pulley_xadd32_uoverflow_trap a b tc)) -(rule (lower (has_type $I64 (uadd_overflow_trap a b tc))) +(rule (lower (has_type $I64 (uadd_overflow_trap _ a b tc))) (pulley_xadd64_uoverflow_trap a b tc)) ;;;; Rules for `uadd_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I32 (uadd_overflow a b))) +(rule (lower (has_type $I32 (uadd_overflow _ a b))) (let ((sum XReg (pulley_xadd32 a b)) (is_overflow XReg (pulley_xult32 sum a))) (output_pair sum is_overflow))) -(rule (lower (has_type $I64 (uadd_overflow a b))) +(rule (lower (has_type $I64 (uadd_overflow _ a b))) (let ((sum XReg (pulley_xadd64 a b)) (is_overflow XReg (pulley_xult64 sum a))) (output_pair sum is_overflow))) ;;;; Rules for `select` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (select c a b))) +(rule 0 (lower (has_type (ty_int (fits_in_32 _)) (select _ c a b))) (pulley_xselect32 (emit_cond (lower_cond c)) a b)) -(rule 1 (lower (has_type $I64 (select c a b))) +(rule 1 (lower (has_type $I64 (select _ c a b))) (pulley_xselect64 (emit_cond (lower_cond c)) a b)) -(rule 1 (lower (has_type $F32 (select c a b))) +(rule 1 (lower (has_type $F32 (select _ c a b))) (pulley_fselect32 (emit_cond (lower_cond c)) a b)) -(rule 1 (lower (has_type $F64 (select c a b))) +(rule 1 (lower (has_type $F64 (select _ c a b))) (pulley_fselect64 (emit_cond (lower_cond c)) a b)) -(rule 2 (lower (has_type (ty_vec128 _) (select c a b))) +(rule 2 (lower (has_type (ty_vec128 _) (select _ c a b))) (pulley_vselect (emit_cond (lower_cond c)) a b)) ;; Helper to emit a conditional into a register itself. @@ -1426,388 +1426,388 @@ ;;;; Rules for `bitcast` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (bitcast _flags val @ (value_type $I32)))) +(rule (lower (has_type $F32 (bitcast _ _flags val @ (value_type $I32)))) (pulley_bitcast_float_from_int_32 val)) -(rule (lower (has_type $F64 (bitcast _flags val @ (value_type $I64)))) +(rule (lower (has_type $F64 (bitcast _ _flags val @ (value_type $I64)))) (pulley_bitcast_float_from_int_64 val)) -(rule (lower (has_type $I32 (bitcast _flags val @ (value_type $F32)))) +(rule (lower (has_type $I32 (bitcast _ _flags val @ (value_type $F32)))) (pulley_bitcast_int_from_float_32 val)) -(rule (lower (has_type $I64 (bitcast _flags val @ (value_type $F64)))) +(rule (lower (has_type $I64 (bitcast _ _flags val @ (value_type $F64)))) (pulley_bitcast_int_from_float_64 val)) -(rule 1 (lower (has_type (ty_vec128 _) (bitcast _flags val @ (value_type (ty_vec128 _))))) +(rule 1 (lower (has_type (ty_vec128 _) (bitcast _ _flags val @ (value_type (ty_vec128 _))))) val) -(rule 2 (lower (has_type ty (bitcast _flags a @ (value_type ty)))) a) +(rule 2 (lower (has_type ty (bitcast _ _flags a @ (value_type ty)))) a) ;;;; Rules for `fcvt_to_{u,s}int` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I32 (fcvt_to_uint val @ (value_type $F32)))) +(rule (lower (has_type $I32 (fcvt_to_uint _ val @ (value_type $F32)))) (pulley_x32_from_f32_u val)) -(rule (lower (has_type $I32 (fcvt_to_uint val @ (value_type $F64)))) +(rule (lower (has_type $I32 (fcvt_to_uint _ val @ (value_type $F64)))) (pulley_x32_from_f64_u val)) -(rule (lower (has_type $I64 (fcvt_to_uint val @ (value_type $F32)))) +(rule (lower (has_type $I64 (fcvt_to_uint _ val @ (value_type $F32)))) (pulley_x64_from_f32_u val)) -(rule (lower (has_type $I64 (fcvt_to_uint val @ (value_type $F64)))) +(rule (lower (has_type $I64 (fcvt_to_uint _ val @ (value_type $F64)))) (pulley_x64_from_f64_u val)) -(rule (lower (has_type $I32 (fcvt_to_sint val @ (value_type $F32)))) +(rule (lower (has_type $I32 (fcvt_to_sint _ val @ (value_type $F32)))) (pulley_x32_from_f32_s val)) -(rule (lower (has_type $I32 (fcvt_to_sint val @ (value_type $F64)))) +(rule (lower (has_type $I32 (fcvt_to_sint _ val @ (value_type $F64)))) (pulley_x32_from_f64_s val)) -(rule (lower (has_type $I64 (fcvt_to_sint val @ (value_type $F32)))) +(rule (lower (has_type $I64 (fcvt_to_sint _ val @ (value_type $F32)))) (pulley_x64_from_f32_s val)) -(rule (lower (has_type $I64 (fcvt_to_sint val @ (value_type $F64)))) +(rule (lower (has_type $I64 (fcvt_to_sint _ val @ (value_type $F64)))) (pulley_x64_from_f64_s val)) ;;;; Rules for `fcvt_from_{u,s}int` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type $F32 (fcvt_from_uint val @ (value_type (fits_in_32 _))))) +(rule 0 (lower (has_type $F32 (fcvt_from_uint _ val @ (value_type (fits_in_32 _))))) (pulley_f32_from_x32_u (zext32 val))) -(rule 1 (lower (has_type $F32 (fcvt_from_uint val @ (value_type $I64)))) +(rule 1 (lower (has_type $F32 (fcvt_from_uint _ val @ (value_type $I64)))) (pulley_f32_from_x64_u val)) -(rule 0 (lower (has_type $F64 (fcvt_from_uint val @ (value_type (fits_in_32 _))))) +(rule 0 (lower (has_type $F64 (fcvt_from_uint _ val @ (value_type (fits_in_32 _))))) (pulley_f64_from_x32_u (zext32 val))) -(rule 1 (lower (has_type $F64 (fcvt_from_uint val @ (value_type $I64)))) +(rule 1 (lower (has_type $F64 (fcvt_from_uint _ val @ (value_type $I64)))) (pulley_f64_from_x64_u val)) -(rule 0 (lower (has_type $F32 (fcvt_from_sint val @ (value_type (fits_in_32 _))))) +(rule 0 (lower (has_type $F32 (fcvt_from_sint _ val @ (value_type (fits_in_32 _))))) (pulley_f32_from_x32_s (sext32 val))) -(rule 1 (lower (has_type $F32 (fcvt_from_sint val @ (value_type $I64)))) +(rule 1 (lower (has_type $F32 (fcvt_from_sint _ val @ (value_type $I64)))) (pulley_f32_from_x64_s val)) -(rule 0 (lower (has_type $F64 (fcvt_from_sint val @ (value_type (fits_in_32 _))))) +(rule 0 (lower (has_type $F64 (fcvt_from_sint _ val @ (value_type (fits_in_32 _))))) (pulley_f64_from_x32_s (sext32 val))) -(rule 1 (lower (has_type $F64 (fcvt_from_sint val @ (value_type $I64)))) +(rule 1 (lower (has_type $F64 (fcvt_from_sint _ val @ (value_type $I64)))) (pulley_f64_from_x64_s val)) -(rule (lower (has_type $F32X4 (fcvt_from_sint val @ (value_type $I32X4)))) +(rule (lower (has_type $F32X4 (fcvt_from_sint _ val @ (value_type $I32X4)))) (pulley_vf32x4_from_i32x4_s val)) -(rule (lower (has_type $F32X4 (fcvt_from_uint val @ (value_type $I32X4)))) +(rule (lower (has_type $F32X4 (fcvt_from_uint _ val @ (value_type $I32X4)))) (pulley_vf32x4_from_i32x4_u val)) -(rule (lower (has_type $F64X2 (fcvt_from_sint val @ (value_type $I64X2)))) +(rule (lower (has_type $F64X2 (fcvt_from_sint _ val @ (value_type $I64X2)))) (pulley_vf64x2_from_i64x2_s val)) -(rule (lower (has_type $F64X2 (fcvt_from_uint val @ (value_type $I64X2)))) +(rule (lower (has_type $F64X2 (fcvt_from_uint _ val @ (value_type $I64X2)))) (pulley_vf64x2_from_i64x2_u val)) ;;;; Rules for `fcvt_to_{u,s}int_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I32 (fcvt_to_uint_sat val @ (value_type $F32)))) +(rule (lower (has_type $I32 (fcvt_to_uint_sat _ val @ (value_type $F32)))) (pulley_x32_from_f32_u_sat val)) -(rule (lower (has_type $I32 (fcvt_to_uint_sat val @ (value_type $F64)))) +(rule (lower (has_type $I32 (fcvt_to_uint_sat _ val @ (value_type $F64)))) (pulley_x32_from_f64_u_sat val)) -(rule (lower (has_type $I64 (fcvt_to_uint_sat val @ (value_type $F32)))) +(rule (lower (has_type $I64 (fcvt_to_uint_sat _ val @ (value_type $F32)))) (pulley_x64_from_f32_u_sat val)) -(rule (lower (has_type $I64 (fcvt_to_uint_sat val @ (value_type $F64)))) +(rule (lower (has_type $I64 (fcvt_to_uint_sat _ val @ (value_type $F64)))) (pulley_x64_from_f64_u_sat val)) -(rule (lower (has_type $I32 (fcvt_to_sint_sat val @ (value_type $F32)))) +(rule (lower (has_type $I32 (fcvt_to_sint_sat _ val @ (value_type $F32)))) (pulley_x32_from_f32_s_sat val)) -(rule (lower (has_type $I32 (fcvt_to_sint_sat val @ (value_type $F64)))) +(rule (lower (has_type $I32 (fcvt_to_sint_sat _ val @ (value_type $F64)))) (pulley_x32_from_f64_s_sat val)) -(rule (lower (has_type $I64 (fcvt_to_sint_sat val @ (value_type $F32)))) +(rule (lower (has_type $I64 (fcvt_to_sint_sat _ val @ (value_type $F32)))) (pulley_x64_from_f32_s_sat val)) -(rule (lower (has_type $I64 (fcvt_to_sint_sat val @ (value_type $F64)))) +(rule (lower (has_type $I64 (fcvt_to_sint_sat _ val @ (value_type $F64)))) (pulley_x64_from_f64_s_sat val)) -(rule (lower (has_type $I32X4 (fcvt_to_sint_sat val @ (value_type $F32X4)))) +(rule (lower (has_type $I32X4 (fcvt_to_sint_sat _ val @ (value_type $F32X4)))) (pulley_vi32x4_from_f32x4_s val)) -(rule (lower (has_type $I32X4 (fcvt_to_uint_sat val @ (value_type $F32X4)))) +(rule (lower (has_type $I32X4 (fcvt_to_uint_sat _ val @ (value_type $F32X4)))) (pulley_vi32x4_from_f32x4_u val)) -(rule (lower (has_type $I64X2 (fcvt_to_sint_sat val @ (value_type $F64X2)))) +(rule (lower (has_type $I64X2 (fcvt_to_sint_sat _ val @ (value_type $F64X2)))) (pulley_vi64x2_from_f64x2_s val)) -(rule (lower (has_type $I64X2 (fcvt_to_uint_sat val @ (value_type $F64X2)))) +(rule (lower (has_type $I64X2 (fcvt_to_uint_sat _ val @ (value_type $F64X2)))) (pulley_vi64x2_from_f64x2_u val)) ;;;; Rules for `fdemote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fdemote val @ (value_type $F64)))) +(rule (lower (has_type $F32 (fdemote _ val @ (value_type $F64)))) (pulley_f32_from_f64 val)) ;;;; Rules for `fpromote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F64 (fpromote val @ (value_type $F32)))) +(rule (lower (has_type $F64 (fpromote _ val @ (value_type $F32)))) (pulley_f64_from_f32 val)) ;;;; Rules for `fcopysign` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fcopysign a b))) +(rule (lower (has_type $F32 (fcopysign _ a b))) (pulley_fcopysign32 a b)) -(rule (lower (has_type $F64 (fcopysign a b))) +(rule (lower (has_type $F64 (fcopysign _ a b))) (pulley_fcopysign64 a b)) ;;;; Rules for `fadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fadd a b))) (pulley_fadd32 a b)) -(rule (lower (has_type $F64 (fadd a b))) (pulley_fadd64 a b)) -(rule (lower (has_type $F32X4 (fadd a b))) (pulley_vaddf32x4 a b)) -(rule (lower (has_type $F64X2 (fadd a b))) (pulley_vaddf64x2 a b)) +(rule (lower (has_type $F32 (fadd _ a b))) (pulley_fadd32 a b)) +(rule (lower (has_type $F64 (fadd _ a b))) (pulley_fadd64 a b)) +(rule (lower (has_type $F32X4 (fadd _ a b))) (pulley_vaddf32x4 a b)) +(rule (lower (has_type $F64X2 (fadd _ a b))) (pulley_vaddf64x2 a b)) ;;;; Rules for `fsub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fsub a b))) (pulley_fsub32 a b)) -(rule (lower (has_type $F64 (fsub a b))) (pulley_fsub64 a b)) -(rule (lower (has_type $F32X4 (fsub a b))) (pulley_vsubf32x4 a b)) -(rule (lower (has_type $F64X2 (fsub a b))) (pulley_vsubf64x2 a b)) +(rule (lower (has_type $F32 (fsub _ a b))) (pulley_fsub32 a b)) +(rule (lower (has_type $F64 (fsub _ a b))) (pulley_fsub64 a b)) +(rule (lower (has_type $F32X4 (fsub _ a b))) (pulley_vsubf32x4 a b)) +(rule (lower (has_type $F64X2 (fsub _ a b))) (pulley_vsubf64x2 a b)) ;;;; Rules for `fmul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fmul a b))) (pulley_fmul32 a b)) -(rule (lower (has_type $F64 (fmul a b))) (pulley_fmul64 a b)) -(rule (lower (has_type $F32X4 (fmul a b))) (pulley_vmulf32x4 a b)) -(rule (lower (has_type $F64X2 (fmul a b))) (pulley_vmulf64x2 a b)) +(rule (lower (has_type $F32 (fmul _ a b))) (pulley_fmul32 a b)) +(rule (lower (has_type $F64 (fmul _ a b))) (pulley_fmul64 a b)) +(rule (lower (has_type $F32X4 (fmul _ a b))) (pulley_vmulf32x4 a b)) +(rule (lower (has_type $F64X2 (fmul _ a b))) (pulley_vmulf64x2 a b)) ;;;; Rules for `fdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fdiv a b))) (pulley_fdiv32 a b)) -(rule (lower (has_type $F64 (fdiv a b))) (pulley_fdiv64 a b)) -(rule (lower (has_type $F32X4 (fdiv a b))) (pulley_vdivf32x4 a b)) -(rule (lower (has_type $F64X2 (fdiv a b))) (pulley_vdivf64x2 a b)) +(rule (lower (has_type $F32 (fdiv _ a b))) (pulley_fdiv32 a b)) +(rule (lower (has_type $F64 (fdiv _ a b))) (pulley_fdiv64 a b)) +(rule (lower (has_type $F32X4 (fdiv _ a b))) (pulley_vdivf32x4 a b)) +(rule (lower (has_type $F64X2 (fdiv _ a b))) (pulley_vdivf64x2 a b)) ;;;; Rules for `fmax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fmax a b))) (pulley_fmaximum32 a b)) -(rule (lower (has_type $F64 (fmax a b))) (pulley_fmaximum64 a b)) -(rule (lower (has_type $F32X4 (fmax a b))) (pulley_vmaximumf32x4 a b)) -(rule (lower (has_type $F64X2 (fmax a b))) (pulley_vmaximumf64x2 a b)) +(rule (lower (has_type $F32 (fmax _ a b))) (pulley_fmaximum32 a b)) +(rule (lower (has_type $F64 (fmax _ a b))) (pulley_fmaximum64 a b)) +(rule (lower (has_type $F32X4 (fmax _ a b))) (pulley_vmaximumf32x4 a b)) +(rule (lower (has_type $F64X2 (fmax _ a b))) (pulley_vmaximumf64x2 a b)) ;;;; Rules for `fmin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fmin a b))) (pulley_fminimum32 a b)) -(rule (lower (has_type $F64 (fmin a b))) (pulley_fminimum64 a b)) -(rule (lower (has_type $F32X4 (fmin a b))) (pulley_vminimumf32x4 a b)) -(rule (lower (has_type $F64X2 (fmin a b))) (pulley_vminimumf64x2 a b)) +(rule (lower (has_type $F32 (fmin _ a b))) (pulley_fminimum32 a b)) +(rule (lower (has_type $F64 (fmin _ a b))) (pulley_fminimum64 a b)) +(rule (lower (has_type $F32X4 (fmin _ a b))) (pulley_vminimumf32x4 a b)) +(rule (lower (has_type $F64X2 (fmin _ a b))) (pulley_vminimumf64x2 a b)) ;;;; Rules for `trunc` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (trunc a))) (pulley_ftrunc32 a)) -(rule (lower (has_type $F64 (trunc a))) (pulley_ftrunc64 a)) -(rule (lower (has_type $F32X4 (trunc a))) (pulley_vtrunc32x4 a)) -(rule (lower (has_type $F64X2 (trunc a))) (pulley_vtrunc64x2 a)) +(rule (lower (has_type $F32 (trunc _ a))) (pulley_ftrunc32 a)) +(rule (lower (has_type $F64 (trunc _ a))) (pulley_ftrunc64 a)) +(rule (lower (has_type $F32X4 (trunc _ a))) (pulley_vtrunc32x4 a)) +(rule (lower (has_type $F64X2 (trunc _ a))) (pulley_vtrunc64x2 a)) ;;;; Rules for `floor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (floor a))) (pulley_ffloor32 a)) -(rule (lower (has_type $F64 (floor a))) (pulley_ffloor64 a)) -(rule (lower (has_type $F32X4 (floor a))) +(rule (lower (has_type $F32 (floor _ a))) (pulley_ffloor32 a)) +(rule (lower (has_type $F64 (floor _ a))) (pulley_ffloor64 a)) +(rule (lower (has_type $F32X4 (floor _ a))) (pulley_vfloor32x4 a)) -(rule (lower (has_type $F64X2 (floor a))) +(rule (lower (has_type $F64X2 (floor _ a))) (pulley_vfloor64x2 a)) ;;;; Rules for `ceil` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (ceil a))) (pulley_fceil32 a)) -(rule (lower (has_type $F64 (ceil a))) (pulley_fceil64 a)) -(rule (lower (has_type $F64X2 (ceil a))) +(rule (lower (has_type $F32 (ceil _ a))) (pulley_fceil32 a)) +(rule (lower (has_type $F64 (ceil _ a))) (pulley_fceil64 a)) +(rule (lower (has_type $F64X2 (ceil _ a))) (pulley_vceil64x2 a)) -(rule (lower (has_type $F32X4 (ceil a))) +(rule (lower (has_type $F32X4 (ceil _ a))) (pulley_vceil32x4 a)) ;;;; Rules for `nearest` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (nearest a))) (pulley_fnearest32 a)) -(rule (lower (has_type $F64 (nearest a))) (pulley_fnearest64 a)) -(rule (lower (has_type $F32X4 (nearest a))) +(rule (lower (has_type $F32 (nearest _ a))) (pulley_fnearest32 a)) +(rule (lower (has_type $F64 (nearest _ a))) (pulley_fnearest64 a)) +(rule (lower (has_type $F32X4 (nearest _ a))) (pulley_vnearest32x4 a)) -(rule (lower (has_type $F64X2 (nearest a))) +(rule (lower (has_type $F64X2 (nearest _ a))) (pulley_vnearest64x2 a)) ;;;; Rules for `sqrt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (sqrt a))) (pulley_fsqrt32 a)) -(rule (lower (has_type $F64 (sqrt a))) (pulley_fsqrt64 a)) -(rule (lower (has_type $F32X4 (sqrt a))) +(rule (lower (has_type $F32 (sqrt _ a))) (pulley_fsqrt32 a)) +(rule (lower (has_type $F64 (sqrt _ a))) (pulley_fsqrt64 a)) +(rule (lower (has_type $F32X4 (sqrt _ a))) (pulley_vsqrt32x4 a)) -(rule (lower (has_type $F64X2 (sqrt a))) +(rule (lower (has_type $F64X2 (sqrt _ a))) (pulley_vsqrt64x2 a)) ;;;; Rules for `fneg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fneg a))) (pulley_fneg32 a)) -(rule (lower (has_type $F64 (fneg a))) (pulley_fneg64 a)) -(rule (lower (has_type $F32X4 (fneg a))) (pulley_vnegf32x4 a)) -(rule (lower (has_type $F64X2 (fneg a))) (pulley_vnegf64x2 a)) +(rule (lower (has_type $F32 (fneg _ a))) (pulley_fneg32 a)) +(rule (lower (has_type $F64 (fneg _ a))) (pulley_fneg64 a)) +(rule (lower (has_type $F32X4 (fneg _ a))) (pulley_vnegf32x4 a)) +(rule (lower (has_type $F64X2 (fneg _ a))) (pulley_vnegf64x2 a)) ;;;; Rules for `ineg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (ineg a))) (pulley_xneg32 (sext32 a))) -(rule 1 (lower (has_type $I64 (ineg a))) (pulley_xneg64 a)) +(rule 0 (lower (has_type (fits_in_32 _) (ineg _ a))) (pulley_xneg32 (sext32 a))) +(rule 1 (lower (has_type $I64 (ineg _ a))) (pulley_xneg64 a)) ;; vector negation -(rule 1 (lower (has_type $I8X16 (ineg a))) (pulley_vneg8x16 a)) -(rule 1 (lower (has_type $I16X8 (ineg a))) (pulley_vneg16x8 a)) -(rule 1 (lower (has_type $I32X4 (ineg a))) (pulley_vneg32x4 a)) -(rule 1 (lower (has_type $I64X2 (ineg a))) (pulley_vneg64x2 a)) +(rule 1 (lower (has_type $I8X16 (ineg _ a))) (pulley_vneg8x16 a)) +(rule 1 (lower (has_type $I16X8 (ineg _ a))) (pulley_vneg16x8 a)) +(rule 1 (lower (has_type $I32X4 (ineg _ a))) (pulley_vneg32x4 a)) +(rule 1 (lower (has_type $I64X2 (ineg _ a))) (pulley_vneg64x2 a)) ;;;; Rules for `fabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fabs a))) (pulley_fabs32 a)) -(rule (lower (has_type $F64 (fabs a))) (pulley_fabs64 a)) -(rule (lower (has_type $F32X4 (fabs a))) (pulley_vabsf32x4 a)) -(rule (lower (has_type $F64X2 (fabs a))) (pulley_vabsf64x2 a)) +(rule (lower (has_type $F32 (fabs _ a))) (pulley_fabs32 a)) +(rule (lower (has_type $F64 (fabs _ a))) (pulley_fabs64 a)) +(rule (lower (has_type $F32X4 (fabs _ a))) (pulley_vabsf32x4 a)) +(rule (lower (has_type $F64X2 (fabs _ a))) (pulley_vabsf64x2 a)) ;;;; Rules for `vconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_vec128 _) (vconst (u128_from_constant a)))) (pulley_vconst128 a)) +(rule (lower (has_type (ty_vec128 _) (vconst _ (u128_from_constant a)))) (pulley_vconst128 a)) ;;;; Rules for `bswap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I16 (bswap a))) +(rule (lower (has_type $I16 (bswap _ a))) (if-let (u6_from_u8 shift) (u64_try_into_u8 16)) (pulley_xshr32_u_u6 (pulley_bswap32 a) shift)) -(rule (lower (has_type $I32 (bswap a))) (pulley_bswap32 a)) -(rule (lower (has_type $I64 (bswap a))) (pulley_bswap64 a)) +(rule (lower (has_type $I32 (bswap _ a))) (pulley_bswap32 a)) +(rule (lower (has_type $I64 (bswap _ a))) (pulley_bswap64 a)) ;;;; Rules for `iabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 _) (iabs a))) (pulley_xabs32 (sext32 a))) -(rule 1 (lower (has_type $I64 (iabs a))) (pulley_xabs64 a)) -(rule 1 (lower (has_type $I8X16 (iabs a))) (pulley_vabs8x16 a)) -(rule 1 (lower (has_type $I16X8 (iabs a))) (pulley_vabs16x8 a)) -(rule 1 (lower (has_type $I32X4 (iabs a))) (pulley_vabs32x4 a)) -(rule 1 (lower (has_type $I64X2 (iabs a))) (pulley_vabs64x2 a)) +(rule 0 (lower (has_type (fits_in_32 _) (iabs _ a))) (pulley_xabs32 (sext32 a))) +(rule 1 (lower (has_type $I64 (iabs _ a))) (pulley_xabs64 a)) +(rule 1 (lower (has_type $I8X16 (iabs _ a))) (pulley_vabs8x16 a)) +(rule 1 (lower (has_type $I16X8 (iabs _ a))) (pulley_vabs16x8 a)) +(rule 1 (lower (has_type $I32X4 (iabs _ a))) (pulley_vabs32x4 a)) +(rule 1 (lower (has_type $I64X2 (iabs _ a))) (pulley_vabs64x2 a)) ;;;; Rules for `splat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8X16 (splat a))) (pulley_vsplatx8 a)) -(rule (lower (has_type $I16X8 (splat a))) (pulley_vsplatx16 a)) -(rule (lower (has_type $I32X4 (splat a))) (pulley_vsplatx32 a)) -(rule (lower (has_type $I64X2 (splat a))) (pulley_vsplatx64 a)) -(rule (lower (has_type $F32X4 (splat a))) (pulley_vsplatf32 a)) -(rule (lower (has_type $F64X2 (splat a))) (pulley_vsplatf64 a)) +(rule (lower (has_type $I8X16 (splat _ a))) (pulley_vsplatx8 a)) +(rule (lower (has_type $I16X8 (splat _ a))) (pulley_vsplatx16 a)) +(rule (lower (has_type $I32X4 (splat _ a))) (pulley_vsplatx32 a)) +(rule (lower (has_type $I64X2 (splat _ a))) (pulley_vsplatx64 a)) +(rule (lower (has_type $F32X4 (splat _ a))) (pulley_vsplatf32 a)) +(rule (lower (has_type $F64X2 (splat _ a))) (pulley_vsplatf64 a)) ;;;; Rules for `vhigh_bits` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_32 _) (vhigh_bits a @ (value_type $I8X16)))) +(rule (lower (has_type (fits_in_32 _) (vhigh_bits _ a @ (value_type $I8X16)))) (pulley_vbitmask8x16 a)) -(rule (lower (has_type (fits_in_32 _) (vhigh_bits a @ (value_type $I16X8)))) +(rule (lower (has_type (fits_in_32 _) (vhigh_bits _ a @ (value_type $I16X8)))) (pulley_vbitmask16x8 a)) -(rule (lower (has_type (fits_in_32 _) (vhigh_bits a @ (value_type $I32X4)))) +(rule (lower (has_type (fits_in_32 _) (vhigh_bits _ a @ (value_type $I32X4)))) (pulley_vbitmask32x4 a)) -(rule (lower (has_type (fits_in_32 _) (vhigh_bits a @ (value_type $I64X2)))) +(rule (lower (has_type (fits_in_32 _) (vhigh_bits _ a @ (value_type $I64X2)))) (pulley_vbitmask64x2 a)) ;;;; Rules for `vall_true`; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (vall_true a @ (value_type $I8X16))) (pulley_valltrue8x16 a)) -(rule (lower (vall_true a @ (value_type $I16X8))) (pulley_valltrue16x8 a)) -(rule (lower (vall_true a @ (value_type $I32X4))) (pulley_valltrue32x4 a)) -(rule (lower (vall_true a @ (value_type $I64X2))) (pulley_valltrue64x2 a)) -(rule (lower (vall_true a @ (value_type $F32X4))) (pulley_valltrue32x4 a)) -(rule (lower (vall_true a @ (value_type $F64X2))) (pulley_valltrue64x2 a)) +(rule (lower (vall_true _ a @ (value_type $I8X16))) (pulley_valltrue8x16 a)) +(rule (lower (vall_true _ a @ (value_type $I16X8))) (pulley_valltrue16x8 a)) +(rule (lower (vall_true _ a @ (value_type $I32X4))) (pulley_valltrue32x4 a)) +(rule (lower (vall_true _ a @ (value_type $I64X2))) (pulley_valltrue64x2 a)) +(rule (lower (vall_true _ a @ (value_type $F32X4))) (pulley_valltrue32x4 a)) +(rule (lower (vall_true _ a @ (value_type $F64X2))) (pulley_valltrue64x2 a)) ;;;; Rules for `vany_true`; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (vany_true a @ (value_type $I8X16))) (pulley_vanytrue8x16 a)) -(rule (lower (vany_true a @ (value_type $I16X8))) (pulley_vanytrue16x8 a)) -(rule (lower (vany_true a @ (value_type $I32X4))) (pulley_vanytrue32x4 a)) -(rule (lower (vany_true a @ (value_type $I64X2))) (pulley_vanytrue64x2 a)) -(rule (lower (vany_true a @ (value_type $F32X4))) (pulley_vanytrue32x4 a)) -(rule (lower (vany_true a @ (value_type $F64X2))) (pulley_vanytrue64x2 a)) +(rule (lower (vany_true _ a @ (value_type $I8X16))) (pulley_vanytrue8x16 a)) +(rule (lower (vany_true _ a @ (value_type $I16X8))) (pulley_vanytrue16x8 a)) +(rule (lower (vany_true _ a @ (value_type $I32X4))) (pulley_vanytrue32x4 a)) +(rule (lower (vany_true _ a @ (value_type $I64X2))) (pulley_vanytrue64x2 a)) +(rule (lower (vany_true _ a @ (value_type $F32X4))) (pulley_vanytrue32x4 a)) +(rule (lower (vany_true _ a @ (value_type $F64X2))) (pulley_vanytrue64x2 a)) ;;;; Rules for `swiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (swiden_low a @ (value_type $I8X16))) (pulley_vwidenlow8x16_s a)) -(rule (lower (swiden_low a @ (value_type $I16X8))) (pulley_vwidenlow16x8_s a)) -(rule (lower (swiden_low a @ (value_type $I32X4))) (pulley_vwidenlow32x4_s a)) +(rule (lower (swiden_low _ a @ (value_type $I8X16))) (pulley_vwidenlow8x16_s a)) +(rule (lower (swiden_low _ a @ (value_type $I16X8))) (pulley_vwidenlow16x8_s a)) +(rule (lower (swiden_low _ a @ (value_type $I32X4))) (pulley_vwidenlow32x4_s a)) ;;;; Rules for `swiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (swiden_high a @ (value_type $I8X16))) (pulley_vwidenhigh8x16_s a)) -(rule (lower (swiden_high a @ (value_type $I16X8))) (pulley_vwidenhigh16x8_s a)) -(rule (lower (swiden_high a @ (value_type $I32X4))) (pulley_vwidenhigh32x4_s a)) +(rule (lower (swiden_high _ a @ (value_type $I8X16))) (pulley_vwidenhigh8x16_s a)) +(rule (lower (swiden_high _ a @ (value_type $I16X8))) (pulley_vwidenhigh16x8_s a)) +(rule (lower (swiden_high _ a @ (value_type $I32X4))) (pulley_vwidenhigh32x4_s a)) ;;;; Rules for `uwiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (uwiden_low a @ (value_type $I8X16))) (pulley_vwidenlow8x16_u a)) -(rule (lower (uwiden_low a @ (value_type $I16X8))) (pulley_vwidenlow16x8_u a)) -(rule (lower (uwiden_low a @ (value_type $I32X4))) (pulley_vwidenlow32x4_u a)) +(rule (lower (uwiden_low _ a @ (value_type $I8X16))) (pulley_vwidenlow8x16_u a)) +(rule (lower (uwiden_low _ a @ (value_type $I16X8))) (pulley_vwidenlow16x8_u a)) +(rule (lower (uwiden_low _ a @ (value_type $I32X4))) (pulley_vwidenlow32x4_u a)) ;;;; Rules for `uwiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (uwiden_high a @ (value_type $I8X16))) (pulley_vwidenhigh8x16_u a)) -(rule (lower (uwiden_high a @ (value_type $I16X8))) (pulley_vwidenhigh16x8_u a)) -(rule (lower (uwiden_high a @ (value_type $I32X4))) (pulley_vwidenhigh32x4_u a)) +(rule (lower (uwiden_high _ a @ (value_type $I8X16))) (pulley_vwidenhigh8x16_u a)) +(rule (lower (uwiden_high _ a @ (value_type $I16X8))) (pulley_vwidenhigh16x8_u a)) +(rule (lower (uwiden_high _ a @ (value_type $I32X4))) (pulley_vwidenhigh32x4_u a)) ;;;; Rules for `snarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (snarrow a @ (value_type $I16X8) b)) (pulley_vnarrow16x8_s a b)) -(rule (lower (snarrow a @ (value_type $I32X4) b)) (pulley_vnarrow32x4_s a b)) -(rule (lower (snarrow a @ (value_type $I64X2) b)) (pulley_vnarrow64x2_s a b)) +(rule (lower (snarrow _ a @ (value_type $I16X8) b)) (pulley_vnarrow16x8_s a b)) +(rule (lower (snarrow _ a @ (value_type $I32X4) b)) (pulley_vnarrow32x4_s a b)) +(rule (lower (snarrow _ a @ (value_type $I64X2) b)) (pulley_vnarrow64x2_s a b)) ;;;; Rules for `unarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (unarrow a @ (value_type $I16X8) b)) (pulley_vnarrow16x8_u a b)) -(rule (lower (unarrow a @ (value_type $I32X4) b)) (pulley_vnarrow32x4_u a b)) +(rule (lower (unarrow _ a @ (value_type $I16X8) b)) (pulley_vnarrow16x8_u a b)) +(rule (lower (unarrow _ a @ (value_type $I32X4) b)) (pulley_vnarrow32x4_u a b)) ;;;; Rules for `uunarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (uunarrow a @ (value_type $I64X2) b)) (pulley_vunarrow64x2_u a b)) +(rule (lower (uunarrow _ a @ (value_type $I64X2) b)) (pulley_vunarrow64x2_u a b)) ;;;; Rules for `fvpromote_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (fvpromote_low a @ (value_type $F32X4))) (pulley_vfpromotelow a)) +(rule (lower (fvpromote_low _ a @ (value_type $F32X4))) (pulley_vfpromotelow a)) ;;;; Rules for `fvdemote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (fvdemote a @ (value_type $F64X2))) (pulley_vfdemote a)) +(rule (lower (fvdemote _ a @ (value_type $F64X2))) (pulley_vfdemote a)) ;;;; Rules for `extractlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (extractlane a @ (value_type $I8X16) (u8_from_uimm8 lane))) +(rule (lower (extractlane _ a @ (value_type $I8X16) (u8_from_uimm8 lane))) (pulley_xextractv8x16 a lane)) -(rule (lower (extractlane a @ (value_type $I16X8) (u8_from_uimm8 lane))) +(rule (lower (extractlane _ a @ (value_type $I16X8) (u8_from_uimm8 lane))) (pulley_xextractv16x8 a lane)) -(rule (lower (extractlane a @ (value_type $I32X4) (u8_from_uimm8 lane))) +(rule (lower (extractlane _ a @ (value_type $I32X4) (u8_from_uimm8 lane))) (pulley_xextractv32x4 a lane)) -(rule (lower (extractlane a @ (value_type $I64X2) (u8_from_uimm8 lane))) +(rule (lower (extractlane _ a @ (value_type $I64X2) (u8_from_uimm8 lane))) (pulley_xextractv64x2 a lane)) -(rule (lower (extractlane a @ (value_type $F32X4) (u8_from_uimm8 lane))) +(rule (lower (extractlane _ a @ (value_type $F32X4) (u8_from_uimm8 lane))) (pulley_fextractv32x4 a lane)) -(rule (lower (extractlane a @ (value_type $F64X2) (u8_from_uimm8 lane))) +(rule (lower (extractlane _ a @ (value_type $F64X2) (u8_from_uimm8 lane))) (pulley_fextractv64x2 a lane)) ;;;; Rules for `insertlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (insertlane a @ (value_type $I8X16) b (u8_from_uimm8 lane))) +(rule (lower (insertlane _ a @ (value_type $I8X16) b (u8_from_uimm8 lane))) (pulley_vinsertx8 a b lane)) -(rule (lower (insertlane a @ (value_type $I16X8) b (u8_from_uimm8 lane))) +(rule (lower (insertlane _ a @ (value_type $I16X8) b (u8_from_uimm8 lane))) (pulley_vinsertx16 a b lane)) -(rule (lower (insertlane a @ (value_type $I32X4) b (u8_from_uimm8 lane))) +(rule (lower (insertlane _ a @ (value_type $I32X4) b (u8_from_uimm8 lane))) (pulley_vinsertx32 a b lane)) -(rule (lower (insertlane a @ (value_type $I64X2) b (u8_from_uimm8 lane))) +(rule (lower (insertlane _ a @ (value_type $I64X2) b (u8_from_uimm8 lane))) (pulley_vinsertx64 a b lane)) -(rule (lower (insertlane a @ (value_type $F32X4) b (u8_from_uimm8 lane))) +(rule (lower (insertlane _ a @ (value_type $F32X4) b (u8_from_uimm8 lane))) (pulley_vinsertf32 a b lane)) -(rule (lower (insertlane a @ (value_type $F64X2) b (u8_from_uimm8 lane))) +(rule (lower (insertlane _ a @ (value_type $F64X2) b (u8_from_uimm8 lane))) (pulley_vinsertf64 a b lane)) ;;;; Rules for `scalar_to_vector` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1816,47 +1816,47 @@ ;; generates a bytecode-wise relatively inefficient lowering. Should be ;; relatively easy to optimize if necessary in the future. -(rule (lower (scalar_to_vector a @ (value_type $I8))) +(rule (lower (scalar_to_vector _ a @ (value_type $I8))) (pulley_vinsertx8 (pulley_vconst128 0) a 0)) -(rule (lower (scalar_to_vector a @ (value_type $I16))) +(rule (lower (scalar_to_vector _ a @ (value_type $I16))) (pulley_vinsertx16 (pulley_vconst128 0) a 0)) -(rule (lower (scalar_to_vector a @ (value_type $I32))) +(rule (lower (scalar_to_vector _ a @ (value_type $I32))) (pulley_vinsertx32 (pulley_vconst128 0) a 0)) -(rule (lower (scalar_to_vector a @ (value_type $I64))) +(rule (lower (scalar_to_vector _ a @ (value_type $I64))) (pulley_vinsertx64 (pulley_vconst128 0) a 0)) -(rule (lower (scalar_to_vector a @ (value_type $F32))) +(rule (lower (scalar_to_vector _ a @ (value_type $F32))) (pulley_vinsertf32 (pulley_vconst128 0) a 0)) -(rule (lower (scalar_to_vector a @ (value_type $F64))) +(rule (lower (scalar_to_vector _ a @ (value_type $F64))) (pulley_vinsertf64 (pulley_vconst128 0) a 0)) ;;;; Rules for `shuffle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8X16 (shuffle a b (u128_from_immediate mask)))) +(rule (lower (has_type $I8X16 (shuffle _ a b (u128_from_immediate mask)))) (pulley_vshuffle a b mask)) ;;;; Rules for `swizzle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type $I8X16 (swizzle a b))) (pulley_vswizzlei8x16 a b)) +(rule 1 (lower (has_type $I8X16 (swizzle _ a b))) (pulley_vswizzlei8x16 a b)) ;;;; Rules for `fma` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32X4 (fma a b c))) (pulley_vfma32x4 a b c)) -(rule (lower (has_type $F64X2 (fma a b c))) (pulley_vfma64x2 a b c)) +(rule (lower (has_type $F32X4 (fma _ a b c))) (pulley_vfma32x4 a b c)) +(rule (lower (has_type $F64X2 (fma _ a b c))) (pulley_vfma64x2 a b c)) ;; Rules for `symbol_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (symbol_value (symbol_value_data extname dist offset))) +(rule (lower (symbol_value _ (symbol_value_data extname dist offset))) (load_ext_name extname offset dist)) ;; Rules for `func_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (func_addr (func_ref_data _ extname dist _))) +(rule (lower (func_addr _ (func_ref_data _ extname dist _))) (load_ext_name extname 0 dist)) ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (pulley_label_address succ_label))) diff --git a/cranelift/codegen/src/isa/pulley_shared/lower.rs b/cranelift/codegen/src/isa/pulley_shared/lower.rs index 8dd5a0cea50c..2039c7de8dd3 100644 --- a/cranelift/codegen/src/isa/pulley_shared/lower.rs +++ b/cranelift/codegen/src/isa/pulley_shared/lower.rs @@ -31,6 +31,4 @@ where // Pulley does not support this feature right now. None } - - type FactFlowState = (); } diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 7633633b3ad9..80521217ed31 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -1983,7 +1983,7 @@ ;; value (first sign-extending to handle narrow widths). (decl pure partial imm12_from_negated_value (Value) Imm12) (rule - (imm12_from_negated_value (has_type ty (iconst n))) + (imm12_from_negated_value (has_type ty (iconst _ n))) (if-let (imm12_from_u64 imm) (i64_cast_unsigned (i64_wrapping_neg (i64_sextend_imm64 ty n)))) imm) @@ -2030,14 +2030,14 @@ ;; Like imm5_from_value, but first negates the `Value`. (decl pure partial imm5_from_negated_value (Value) Imm5) -(rule (imm5_from_negated_value (has_type ty (iconst n))) +(rule (imm5_from_negated_value (has_type ty (iconst _ n))) (if-let (imm5_from_i64 imm) (i64_wrapping_neg (i64_sextend_imm64 ty n))) imm) ;; Constructor that matches a `Value` equivalent to a replicated Imm5 on all lanes. (decl pure partial replicated_imm5 (Value) Imm5) -(rule (replicated_imm5 (splat (imm5_from_value n))) n) -(rule (replicated_imm5 (vconst (u128_from_constant n128))) +(rule (replicated_imm5 (splat _ (imm5_from_value n))) n) +(rule (replicated_imm5 (vconst _ (u128_from_constant n128))) (if-let (u128_replicated_u64 n64) n128) (if-let (u64_replicated_u32 n32) n64) (if-let (u32_replicated_u16 n16) n32) @@ -2047,10 +2047,10 @@ ;; Like replicated_imm5, but first negates the `Value`. (decl pure partial negated_replicated_imm5 (Value) Imm5) -(rule (negated_replicated_imm5 (splat n)) +(rule (negated_replicated_imm5 (splat _ n)) (if-let imm5 (imm5_from_negated_value n)) imm5) -(rule (negated_replicated_imm5 (vconst (u128_from_constant n128))) +(rule (negated_replicated_imm5 (vconst _ (u128_from_constant n128))) (if-let (u128_replicated_u64 n64) n128) (if-let (u64_replicated_u32 n32) n64) (if-let (u32_replicated_u16 n16) n32) @@ -2062,8 +2062,8 @@ ;; Constructor that matches a `Value` equivalent to a replicated UImm5 on all lanes. (decl pure partial replicated_uimm5 (Value) UImm5) -(rule (replicated_uimm5 (splat (uimm5_from_value n))) n) -(rule 1 (replicated_uimm5 (vconst (u128_from_constant n128))) +(rule (replicated_uimm5 (splat _ (uimm5_from_value n))) n) +(rule 1 (replicated_uimm5 (vconst _ (u128_from_constant n128))) (if-let (u128_replicated_u64 n64) n128) (if-let (u64_replicated_u32 n32) n64) (if-let (u32_replicated_u16 n16) n32) @@ -2074,7 +2074,7 @@ ;; Helper to go directly from a `Value`, when it's an `iconst`, to an `UImm5`. (decl uimm5_from_value (UImm5) Value) (extractor (uimm5_from_value n) - (iconst (u64_from_imm64 (uimm5_from_u64 n)))) + (iconst _ (u64_from_imm64 (uimm5_from_u64 n)))) ;; Extract a `UImm5` from an `u8`. (decl pure partial uimm5_from_u8 (UImm5) u8) @@ -2309,24 +2309,24 @@ ;; When extending our backend always extends to the full register width, so ;; there's no need to extend-an-extend. -(rule 1 (val_already_extended (ExtendOp.Zero) (uextend _)) true) -(rule 1 (val_already_extended (ExtendOp.Signed) (sextend _)) true) +(rule 1 (val_already_extended (ExtendOp.Zero) (uextend _ _)) true) +(rule 1 (val_already_extended (ExtendOp.Signed) (sextend _ _)) true) ;; The result of `icmp`/`fcmp` is zero or one, meaning that it's already sign ;; extended to the full register width. -(rule 1 (val_already_extended _ (icmp _ _ _)) true) -(rule 1 (val_already_extended _ (fcmp _ _ _)) true) +(rule 1 (val_already_extended _ (icmp _ _ _ _)) true) +(rule 1 (val_already_extended _ (fcmp _ _ _ _)) true) ;; The lowering for these operations always sign-extend their results due to the ;; use of the `*w` instructions in RV64I. Note that this requires that the ;; extension is from 32 to 64, 16/8-bit operations are explicitly excluded here. ;; There are no native instructions for the 16/8 bit operations so they must ;; fall through to actual sign extension above. -(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (ishl _ _))) true) -(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (ushr _ _))) true) -(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (sshr _ _))) true) -(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (iadd _ _))) true) -(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (isub _ _))) true) +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (ishl _ _ _))) true) +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (ushr _ _ _))) true) +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (sshr _ _ _))) true) +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (iadd _ _ _))) true) +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (isub _ _ _))) true) (type ExtendOp (enum @@ -2418,10 +2418,10 @@ ;; ;; We can't recurse into `amode` again since that could cause stack overflows. ;; See: https://github.com/bytecodealliance/wasmtime/pull/6968 -(rule 1 (amode (iadd addr (i32_from_iconst y)) offset) +(rule 1 (amode (iadd _ addr (i32_from_iconst y)) offset) (if-let new_offset (i32_checked_add y offset)) (amode_inner addr new_offset)) -(rule 2 (amode (iadd (i32_from_iconst x) addr) offset) +(rule 2 (amode (iadd _ (i32_from_iconst x) addr) offset) (if-let new_offset (i32_checked_add x offset)) (amode_inner addr new_offset)) @@ -2434,15 +2434,15 @@ (gen_reg_offset_amode r offset)) ;; If the value is a `get_frame_pointer`, we can just use the offset from that. -(rule 1 (amode_inner (get_frame_pointer) offset) +(rule 1 (amode_inner (get_frame_pointer _) offset) (gen_fp_offset_amode offset)) ;; If the value is a `get_stack_pointer`, we can just use the offset from that. -(rule 1 (amode_inner (get_stack_pointer) offset) +(rule 1 (amode_inner (get_stack_pointer _) offset) (gen_sp_offset_amode offset)) ;; Similarly if the value is a `stack_addr` we can also turn that into an sp offset. -(rule 1 (amode_inner (stack_addr ss ss_offset) amode_offset) +(rule 1 (amode_inner (stack_addr _ ss ss_offset) amode_offset) (if-let combined_offset (i32_checked_add ss_offset amode_offset)) (gen_stack_slot_amode ss combined_offset)) @@ -2462,7 +2462,7 @@ (decl sinkable_load (Inst Type MemFlags Value Offset32) Value) (extractor (sinkable_load inst ty flags addr offset) (and - (load (little_or_native_endian flags) addr offset) + (load _ (little_or_native_endian flags) addr offset) (sinkable_inst (has_type ty inst)))) ;; Returns a canonical type for a LoadOP. We only return I64 or F64. @@ -2933,9 +2933,9 @@ ;; If the input value is itself an `icmp` or `fcmp` we can avoid generating the ;; result of the comparison and instead move the comparison directly into the ;; `IntegerCompare` that's returned. -(rule 2 (is_nonzero_cmp (maybe_uextend (icmp cc a b @ (value_type (fits_in_64 _))))) +(rule 2 (is_nonzero_cmp (maybe_uextend (icmp _ cc a b @ (value_type (fits_in_64 _))))) (icmp_to_int_compare cc a b)) -(rule 2 (is_nonzero_cmp (maybe_uextend (fcmp cc a @ (value_type ty) b))) +(rule 2 (is_nonzero_cmp (maybe_uextend (fcmp _ cc a @ (value_type ty) b))) (fcmp_to_float_compare cc ty a b)) ;; Creates an `IntegerCompare` from an `icmp` node's parts. This will extend @@ -3163,8 +3163,8 @@ ;; be omitted because the result of the icmp or fcmp is a 0 or 1 directly. This ;; means we can go straight to the `neg` instruction to produce the final ;; result. -(rule 2 (gen_bmask val @ (maybe_uextend (icmp _ _ _))) (rv_neg val)) -(rule 2 (gen_bmask val @ (maybe_uextend (fcmp _ _ _))) (rv_neg val)) +(rule 2 (gen_bmask val @ (maybe_uextend (icmp _ _ _ _))) (rv_neg val)) +(rule 2 (gen_bmask val @ (maybe_uextend (fcmp _ _ _ _))) (rv_neg val)) (decl lower_bmask (Value Type) ValueRegs) (rule 0 (lower_bmask val (fits_in_64 _)) diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs index 918a4854b3a3..cc6e668b911a 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/args.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs @@ -1563,8 +1563,11 @@ impl AtomicOP { ///Atomic Memory ordering. #[derive(Copy, Clone, Debug)] pub enum AMO { + #[allow(dead_code, reason = "used only in emit tests for now")] Relax = 0b00, + #[allow(dead_code, reason = "used only in emit tests for now")] Release = 0b01, + #[allow(dead_code, reason = "used only in emit tests for now")] Acquire = 0b10, SeqCst = 0b11, } diff --git a/cranelift/codegen/src/isa/riscv64/inst/vector.rs b/cranelift/codegen/src/isa/riscv64/inst/vector.rs index 269e6481b506..bc803939f172 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/vector.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/vector.rs @@ -1,4 +1,3 @@ -use crate::Reg; use crate::isa::riscv64::lower::isle::generated_code::VecAluOpRRRR; use crate::isa::riscv64::lower::isle::generated_code::{ VecAMode, VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecAluOpRRRImm5, VecAvl, @@ -1060,12 +1059,6 @@ impl fmt::Display for VecAluOpRImm5 { } impl VecAMode { - pub fn get_base_register(&self) -> Option { - match self { - VecAMode::UnitStride { base, .. } => base.get_base_register(), - } - } - pub fn get_operands(&mut self, collector: &mut impl OperandVisitor) { match self { VecAMode::UnitStride { base, .. } => base.get_operands(collector), diff --git a/cranelift/codegen/src/isa/riscv64/inst_vector.isle b/cranelift/codegen/src/isa/riscv64/inst_vector.isle index 4070b8f33c0e..9a4034a13da1 100644 --- a/cranelift/codegen/src/isa/riscv64/inst_vector.isle +++ b/cranelift/codegen/src/isa/riscv64/inst_vector.isle @@ -1589,10 +1589,10 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) x y) (rv_vmseq_vv x y (unmasked) ty)) -(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) x (splat y)) +(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) x (splat _ y)) (rv_vmseq_vx x y (unmasked) ty)) -(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) (splat x) y) +(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) (splat _ x) y) (rv_vmseq_vx y x (unmasked) ty)) (rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) x y) @@ -1608,10 +1608,10 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) x y) (rv_vmsne_vv x y (unmasked) ty)) -(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) x (splat y)) +(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) x (splat _ y)) (rv_vmsne_vx x y (unmasked) ty)) -(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) (splat x) y) +(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) (splat _ x) y) (rv_vmsne_vx y x (unmasked) ty)) (rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) x y) @@ -1627,10 +1627,10 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThan) x y) (rv_vmsltu_vv x y (unmasked) ty)) -(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThan) x (splat y)) +(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThan) x (splat _ y)) (rv_vmsltu_vx x y (unmasked) ty)) -(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThan) (splat x) y) +(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThan) (splat _ x) y) (rv_vmsgtu_vx y x (unmasked) ty)) (rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThan) x y) @@ -1642,10 +1642,10 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThan) x y) (rv_vmslt_vv x y (unmasked) ty)) -(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThan) x (splat y)) +(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThan) x (splat _ y)) (rv_vmslt_vx x y (unmasked) ty)) -(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThan) (splat x) y) +(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThan) (splat _ x) y) (rv_vmsgt_vx y x (unmasked) ty)) (rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThan) x y) @@ -1657,7 +1657,7 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThanOrEqual) x y) (rv_vmsleu_vv x y (unmasked) ty)) -(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThanOrEqual) x (splat y)) +(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThanOrEqual) x (splat _ y)) (rv_vmsleu_vx x y (unmasked) ty)) (rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThanOrEqual) x y) @@ -1669,7 +1669,7 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThanOrEqual) x y) (rv_vmsle_vv x y (unmasked) ty)) -(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThanOrEqual) x (splat y)) +(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThanOrEqual) x (splat _ y)) (rv_vmsle_vx x y (unmasked) ty)) (rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThanOrEqual) x y) @@ -1681,10 +1681,10 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThan) x y) (rv_vmsgtu_vv x y (unmasked) ty)) -(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThan) x (splat y)) +(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThan) x (splat _ y)) (rv_vmsgtu_vx x y (unmasked) ty)) -(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThan) (splat x) y) +(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThan) (splat _ x) y) (rv_vmsltu_vx y x (unmasked) ty)) (rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThan) x y) @@ -1696,10 +1696,10 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThan) x y) (rv_vmsgt_vv x y (unmasked) ty)) -(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThan) x (splat y)) +(rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThan) x (splat _ y)) (rv_vmsgt_vx x y (unmasked) ty)) -(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThan) (splat x) y) +(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThan) (splat _ x) y) (rv_vmslt_vx y x (unmasked) ty)) (rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThan) x y) @@ -1711,7 +1711,7 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThanOrEqual) x y) (rv_vmsgeu_vv x y (unmasked) ty)) -(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThanOrEqual) (splat x) y) +(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThanOrEqual) (splat _ x) y) (rv_vmsleu_vx y x (unmasked) ty)) (rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThanOrEqual) x y) @@ -1723,7 +1723,7 @@ (rule 0 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThanOrEqual) x y) (rv_vmsge_vv x y (unmasked) ty)) -(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThanOrEqual) (splat x) y) +(rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThanOrEqual) (splat _ x) y) (rv_vmsle_vx y x (unmasked) ty)) (rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThanOrEqual) x y) @@ -1735,7 +1735,7 @@ ;; Builds a vector mask corresponding to the FloatCC operation. ;; ;; Recursion: recursive rules implement some condition codes in terms of a -;; smaller set of primtives, which recursive rules would not apply to twice. +;; smaller set of primitives, which recursive rules would not apply to twice. (decl rec gen_fcmp_mask (Type FloatCC Value Value) VReg) ;; FloatCC.Equal @@ -1743,10 +1743,10 @@ (rule 0 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.Equal) x y) (rv_vmfeq_vv x y (unmasked) ty)) -(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.Equal) x (splat y)) +(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.Equal) x (splat _ y)) (rv_vmfeq_vf x y (unmasked) ty)) -(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.Equal) (splat x) y) +(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.Equal) (splat _ x) y) (rv_vmfeq_vf y x (unmasked) ty)) ;; FloatCC.NotEqual @@ -1755,10 +1755,10 @@ (rule 0 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.NotEqual) x y) (rv_vmfne_vv x y (unmasked) ty)) -(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.NotEqual) x (splat y)) +(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.NotEqual) x (splat _ y)) (rv_vmfne_vf x y (unmasked) ty)) -(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.NotEqual) (splat x) y) +(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.NotEqual) (splat _ x) y) (rv_vmfne_vf y x (unmasked) ty)) ;; FloatCC.LessThan @@ -1766,10 +1766,10 @@ (rule 0 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThan) x y) (rv_vmflt_vv x y (unmasked) ty)) -(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThan) x (splat y)) +(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThan) x (splat _ y)) (rv_vmflt_vf x y (unmasked) ty)) -(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThan) (splat x) y) +(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThan) (splat _ x) y) (rv_vmfgt_vf y x (unmasked) ty)) ;; FloatCC.LessThanOrEqual @@ -1777,10 +1777,10 @@ (rule 0 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThanOrEqual) x y) (rv_vmfle_vv x y (unmasked) ty)) -(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThanOrEqual) x (splat y)) +(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThanOrEqual) x (splat _ y)) (rv_vmfle_vf x y (unmasked) ty)) -(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThanOrEqual) (splat x) y) +(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.LessThanOrEqual) (splat _ x) y) (rv_vmfge_vf y x (unmasked) ty)) ;; FloatCC.GreaterThan @@ -1788,10 +1788,10 @@ (rule 0 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThan) x y) (rv_vmfgt_vv x y (unmasked) ty)) -(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThan) x (splat y)) +(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThan) x (splat _ y)) (rv_vmfgt_vf x y (unmasked) ty)) -(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThan) (splat x) y) +(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThan) (splat _ x) y) (rv_vmflt_vf y x (unmasked) ty)) ;; FloatCC.GreaterThanOrEqual @@ -1799,10 +1799,10 @@ (rule 0 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThanOrEqual) x y) (rv_vmfge_vv x y (unmasked) ty)) -(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThanOrEqual) x (splat y)) +(rule 1 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThanOrEqual) x (splat _ y)) (rv_vmfge_vf x y (unmasked) ty)) -(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThanOrEqual) (splat x) y) +(rule 2 (gen_fcmp_mask (ty_vec_fits_in_register ty) (FloatCC.GreaterThanOrEqual) (splat _ x) y) (rv_vmfle_vf y x (unmasked) ty)) ;; FloatCC.Ordered diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 49b56e3d0949..01f0039743c4 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -6,61 +6,61 @@ ;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (iconst (u64_from_imm64 n)))) +(rule (lower (has_type ty (iconst _ (u64_from_imm64 n)))) (imm ty n)) ;; ;;;; Rules for `vconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_supported_vec ty) (vconst n))) +(rule (lower (has_type (ty_supported_vec ty) (vconst _ n))) (gen_constant ty (const_to_vconst n))) ;;;; Rules for `f16const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f16const (u16_from_ieee16 n))) +(rule (lower (f16const _ (u16_from_ieee16 n))) (imm $F16 n)) ;;;; Rules for `f32const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f32const (u32_from_ieee32 n))) +(rule (lower (f32const _ (u32_from_ieee32 n))) (imm $F32 n)) ;;;; Rules for `f64const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f64const (u64_from_ieee64 n))) +(rule (lower (f64const _ (u64_from_ieee64 n))) (imm $F64 n)) ;;;; Rules for `f128const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f128const (u128_from_constant n))) +(rule (lower (f128const _ (u128_from_constant n))) (value_regs (imm $I64 (u128_low_bits n)) (imm $I64 (u128_high_bits n)))) -(rule 1 (lower (f128const (u128_from_constant (u128_replicated_u64 n)))) +(rule 1 (lower (f128const _ (u128_from_constant (u128_replicated_u64 n)))) (let ((r Reg (imm $I64 n))) (value_regs r r))) ;;;; Rules for `iadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Base case, simply adding things in registers. -(rule -1 (lower (has_type (fits_in_32 (ty_int ty)) (iadd x y))) +(rule -1 (lower (has_type (fits_in_32 (ty_int ty)) (iadd _ x y))) (rv_addw x y)) -(rule 0 (lower (has_type $I64 (iadd x y))) +(rule 0 (lower (has_type $I64 (iadd _ x y))) (rv_add x y)) ;; Special cases for when one operand is an immediate that fits in 12 bits. -(rule 1 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd x (imm12_from_value y)))) +(rule 1 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ x (imm12_from_value y)))) (alu_rr_imm12 (select_addi ty) x y)) -(rule 2 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd (imm12_from_value x) y))) +(rule 2 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd _ (imm12_from_value x) y))) (alu_rr_imm12 (select_addi ty) y x)) ;; Special case when one of the operands is uextended ;; Needs `Zba` -(rule 3 (lower (has_type $I64 (iadd x (uextend y @ (value_type $I32))))) +(rule 3 (lower (has_type $I64 (iadd _ x (uextend _ y @ (value_type $I32))))) (if-let true (has_zba)) (rv_adduw y x)) -(rule 4 (lower (has_type $I64 (iadd (uextend x @ (value_type $I32)) y))) +(rule 4 (lower (has_type $I64 (iadd _ (uextend _ x @ (value_type $I32)) y))) (if-let true (has_zba)) (rv_adduw x y)) @@ -70,12 +70,12 @@ (rule (match_shnadd (u64_from_imm64 2)) (AluOPRRR.Sh2add)) (rule (match_shnadd (u64_from_imm64 3)) (AluOPRRR.Sh3add)) -(rule 3 (lower (has_type $I64 (iadd x (ishl y (maybe_uextend (iconst n)))))) +(rule 3 (lower (has_type $I64 (iadd _ x (ishl _ y (maybe_uextend (iconst _ n)))))) (if-let true (has_zba)) (if-let shnadd (match_shnadd n)) (alu_rrr shnadd y x)) -(rule 4 (lower (has_type $I64 (iadd (ishl x (maybe_uextend (iconst n))) y))) +(rule 4 (lower (has_type $I64 (iadd _ (ishl _ x (maybe_uextend (iconst _ n))) y))) (if-let true (has_zba)) (if-let shnadd (match_shnadd n)) (alu_rrr shnadd x y)) @@ -92,18 +92,18 @@ (rule (match_shnadd_uw (u64_from_imm64 2)) (AluOPRRR.Sh2adduw)) (rule (match_shnadd_uw (u64_from_imm64 3)) (AluOPRRR.Sh3adduw)) -(rule 5 (lower (has_type $I64 (iadd x (ishl (uextend y @ (value_type $I32)) (maybe_uextend (iconst n)))))) +(rule 5 (lower (has_type $I64 (iadd _ x (ishl _ (uextend _ y @ (value_type $I32)) (maybe_uextend (iconst _ n)))))) (if-let true (has_zba)) (if-let shnadd_uw (match_shnadd_uw n)) (alu_rrr shnadd_uw y x)) -(rule 6 (lower (has_type $I64 (iadd (ishl (uextend x @ (value_type $I32)) (maybe_uextend (iconst n))) y))) +(rule 6 (lower (has_type $I64 (iadd _ (ishl _ (uextend _ x @ (value_type $I32)) (maybe_uextend (iconst _ n))) y))) (if-let true (has_zba)) (if-let shnadd_uw (match_shnadd_uw n)) (alu_rrr shnadd_uw x y)) ;; I128 cases -(rule 7 (lower (has_type $I128 (iadd x y))) +(rule 7 (lower (has_type $I128 (iadd _ x y))) (let ((low XReg (rv_add (value_regs_get x 0) (value_regs_get y 0))) ;; compute carry. (carry XReg (rv_sltu low (value_regs_get y 0))) @@ -114,152 +114,152 @@ (value_regs low high))) ;; SIMD Vectors -(rule 8 (lower (has_type (ty_supported_vec ty) (iadd x y))) +(rule 8 (lower (has_type (ty_supported_vec ty) (iadd _ x y))) (rv_vadd_vv x y (unmasked) ty)) -(rule 9 (lower (has_type (ty_supported_vec ty) (iadd x (splat y)))) +(rule 9 (lower (has_type (ty_supported_vec ty) (iadd _ x (splat _ y)))) (rv_vadd_vx x y (unmasked) ty)) -(rule 10 (lower (has_type (ty_supported_vec ty) (iadd x (splat (sextend y @ (value_type sext_ty)))))) +(rule 10 (lower (has_type (ty_supported_vec ty) (iadd _ x (splat _ (sextend _ y @ (value_type sext_ty)))))) (if-let half_ty (ty_half_width ty)) (if-let true (ty_equal (lane_type half_ty) sext_ty)) (rv_vwadd_wx x y (unmasked) (vstate_mf2 half_ty))) -(rule 10 (lower (has_type (ty_supported_vec ty) (iadd x (splat (uextend y @ (value_type uext_ty)))))) +(rule 10 (lower (has_type (ty_supported_vec ty) (iadd _ x (splat _ (uextend _ y @ (value_type uext_ty)))))) (if-let half_ty (ty_half_width ty)) (if-let true (ty_equal (lane_type half_ty) uext_ty)) (rv_vwaddu_wx x y (unmasked) (vstate_mf2 half_ty))) -(rule 20 (lower (has_type (ty_supported_vec ty) (iadd x y))) +(rule 20 (lower (has_type (ty_supported_vec ty) (iadd _ x y))) (if-let y_imm (replicated_imm5 y)) (rv_vadd_vi x y_imm (unmasked) ty)) -(rule 12 (lower (has_type (ty_supported_vec ty) (iadd (splat x) y))) +(rule 12 (lower (has_type (ty_supported_vec ty) (iadd _ (splat _ x) y))) (rv_vadd_vx y x (unmasked) ty)) -(rule 13 (lower (has_type (ty_supported_vec ty) (iadd (splat (sextend x @ (value_type sext_ty))) y))) +(rule 13 (lower (has_type (ty_supported_vec ty) (iadd _ (splat _ (sextend _ x @ (value_type sext_ty))) y))) (if-let half_ty (ty_half_width ty)) (if-let true (ty_equal (lane_type half_ty) sext_ty)) (rv_vwadd_wx y x (unmasked) (vstate_mf2 half_ty))) -(rule 13 (lower (has_type (ty_supported_vec ty) (iadd (splat (uextend x @ (value_type uext_ty))) y))) +(rule 13 (lower (has_type (ty_supported_vec ty) (iadd _ (splat _ (uextend _ x @ (value_type uext_ty))) y))) (if-let half_ty (ty_half_width ty)) (if-let true (ty_equal (lane_type half_ty) uext_ty)) (rv_vwaddu_wx y x (unmasked) (vstate_mf2 half_ty))) -(rule 21 (lower (has_type (ty_supported_vec ty) (iadd x y))) +(rule 21 (lower (has_type (ty_supported_vec ty) (iadd _ x y))) (if-let x_imm (replicated_imm5 x)) (rv_vadd_vi y x_imm (unmasked) ty)) ;; Signed Widening Low Additions -(rule 9 (lower (has_type (ty_supported_vec _) (iadd x (swiden_low y @ (value_type in_ty))))) +(rule 9 (lower (has_type (ty_supported_vec _) (iadd _ x (swiden_low _ y @ (value_type in_ty))))) (rv_vwadd_wv x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 12 (lower (has_type (ty_supported_vec _) (iadd (swiden_low x @ (value_type in_ty)) y))) +(rule 12 (lower (has_type (ty_supported_vec _) (iadd _ (swiden_low _ x @ (value_type in_ty)) y))) (rv_vwadd_wv y x (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (swiden_low x @ (value_type in_ty)) - (swiden_low y)))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (swiden_low _ x @ (value_type in_ty)) + (swiden_low _ y)))) (rv_vwadd_vv x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (swiden_low x @ (value_type in_ty)) - (splat (sextend y @ (value_type sext_ty)))))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (swiden_low _ x @ (value_type in_ty)) + (splat _ (sextend _ y @ (value_type sext_ty)))))) (if-let true (ty_equal (lane_type in_ty) sext_ty)) (rv_vwadd_vx x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 15 (lower (has_type (ty_supported_vec _) (iadd (splat (sextend x @ (value_type sext_ty))) - (swiden_low y @ (value_type in_ty))))) +(rule 15 (lower (has_type (ty_supported_vec _) (iadd _ (splat _ (sextend _ x @ (value_type sext_ty))) + (swiden_low _ y @ (value_type in_ty))))) (if-let true (ty_equal (lane_type in_ty) sext_ty)) (rv_vwadd_vx y x (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Signed Widening High Additions ;; These are the same as the low additions, but we first slide down the inputs. -(rule 9 (lower (has_type (ty_supported_vec _) (iadd x (swiden_high y @ (value_type in_ty))))) +(rule 9 (lower (has_type (ty_supported_vec _) (iadd _ x (swiden_high _ y @ (value_type in_ty))))) (rv_vwadd_wv x (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 12 (lower (has_type (ty_supported_vec _) (iadd (swiden_high x @ (value_type in_ty)) y))) +(rule 12 (lower (has_type (ty_supported_vec _) (iadd _ (swiden_high _ x @ (value_type in_ty)) y))) (rv_vwadd_wv y (gen_slidedown_half in_ty x) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (swiden_high x @ (value_type in_ty)) - (swiden_high y)))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (swiden_high _ x @ (value_type in_ty)) + (swiden_high _ y)))) (rv_vwadd_vv (gen_slidedown_half in_ty x) (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (swiden_high x @ (value_type in_ty)) - (splat (sextend y @ (value_type sext_ty)))))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (swiden_high _ x @ (value_type in_ty)) + (splat _ (sextend _ y @ (value_type sext_ty)))))) (if-let true (ty_equal (lane_type in_ty) sext_ty)) (rv_vwadd_vx (gen_slidedown_half in_ty x) y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 15 (lower (has_type (ty_supported_vec _) (iadd (splat (sextend x @ (value_type sext_ty))) - (swiden_high y @ (value_type in_ty))))) +(rule 15 (lower (has_type (ty_supported_vec _) (iadd _ (splat _ (sextend _ x @ (value_type sext_ty))) + (swiden_high _ y @ (value_type in_ty))))) (if-let true (ty_equal (lane_type in_ty) sext_ty)) (rv_vwadd_vx (gen_slidedown_half in_ty y) x (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Unsigned Widening Low Additions -(rule 9 (lower (has_type (ty_supported_vec _) (iadd x (uwiden_low y @ (value_type in_ty))))) +(rule 9 (lower (has_type (ty_supported_vec _) (iadd _ x (uwiden_low _ y @ (value_type in_ty))))) (rv_vwaddu_wv x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 12 (lower (has_type (ty_supported_vec _) (iadd (uwiden_low x @ (value_type in_ty)) y))) +(rule 12 (lower (has_type (ty_supported_vec _) (iadd _ (uwiden_low _ x @ (value_type in_ty)) y))) (rv_vwaddu_wv y x (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (uwiden_low x @ (value_type in_ty)) - (uwiden_low y)))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (uwiden_low _ x @ (value_type in_ty)) + (uwiden_low _ y)))) (rv_vwaddu_vv x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (uwiden_low x @ (value_type in_ty)) - (splat (uextend y @ (value_type uext_ty)))))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (uwiden_low _ x @ (value_type in_ty)) + (splat _ (uextend _ y @ (value_type uext_ty)))))) (if-let true (ty_equal (lane_type in_ty) uext_ty)) (rv_vwaddu_vx x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 15 (lower (has_type (ty_supported_vec _) (iadd (splat (uextend x @ (value_type uext_ty))) - (uwiden_low y @ (value_type in_ty))))) +(rule 15 (lower (has_type (ty_supported_vec _) (iadd _ (splat _ (uextend _ x @ (value_type uext_ty))) + (uwiden_low _ y @ (value_type in_ty))))) (if-let true (ty_equal (lane_type in_ty) uext_ty)) (rv_vwaddu_vx y x (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Unsigned Widening High Additions ;; These are the same as the low additions, but we first slide down the inputs. -(rule 9 (lower (has_type (ty_supported_vec _) (iadd x (uwiden_high y @ (value_type in_ty))))) +(rule 9 (lower (has_type (ty_supported_vec _) (iadd _ x (uwiden_high _ y @ (value_type in_ty))))) (rv_vwaddu_wv x (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 12 (lower (has_type (ty_supported_vec _) (iadd (uwiden_high x @ (value_type in_ty)) y))) +(rule 12 (lower (has_type (ty_supported_vec _) (iadd _ (uwiden_high _ x @ (value_type in_ty)) y))) (rv_vwaddu_wv y (gen_slidedown_half in_ty x) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (uwiden_high x @ (value_type in_ty)) - (uwiden_high y)))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (uwiden_high _ x @ (value_type in_ty)) + (uwiden_high _ y)))) (rv_vwaddu_vv (gen_slidedown_half in_ty x) (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (uwiden_high x @ (value_type in_ty)) - (splat (uextend y @ (value_type uext_ty)))))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (uwiden_high _ x @ (value_type in_ty)) + (splat _ (uextend _ y @ (value_type uext_ty)))))) (if-let true (ty_equal (lane_type in_ty) uext_ty)) (rv_vwaddu_vx (gen_slidedown_half in_ty x) y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 15 (lower (has_type (ty_supported_vec _) (iadd (splat (uextend y @ (value_type uext_ty))) - (uwiden_high x @ (value_type in_ty))))) +(rule 15 (lower (has_type (ty_supported_vec _) (iadd _ (splat _ (uextend _ y @ (value_type uext_ty))) + (uwiden_high _ x @ (value_type in_ty))))) (if-let true (ty_equal (lane_type in_ty) uext_ty)) (rv_vwaddu_vx (gen_slidedown_half in_ty x) y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Signed Widening Mixed High/Low Additions -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (swiden_low x @ (value_type in_ty)) - (swiden_high y)))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (swiden_low _ x @ (value_type in_ty)) + (swiden_high _ y)))) (rv_vwadd_vv x (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (swiden_high x @ (value_type in_ty)) - (swiden_low y)))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (swiden_high _ x @ (value_type in_ty)) + (swiden_low _ y)))) (rv_vwadd_vv (gen_slidedown_half in_ty x) y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Unsigned Widening Mixed High/Low Additions -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (uwiden_low x @ (value_type in_ty)) - (uwiden_high y)))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (uwiden_low _ x @ (value_type in_ty)) + (uwiden_high _ y)))) (rv_vwaddu_vv x (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 13 (lower (has_type (ty_supported_vec _) (iadd (uwiden_high x @ (value_type in_ty)) - (uwiden_low y)))) +(rule 13 (lower (has_type (ty_supported_vec _) (iadd _ (uwiden_high _ x @ (value_type in_ty)) + (uwiden_low _ y)))) (rv_vwaddu_vv (gen_slidedown_half in_ty x) y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Fused Multiply Accumulate Rules `vmacc` @@ -268,46 +268,46 @@ ;; register instead of the addition one. The actual pattern matched seems to be ;; exactly the same. -(rule 9 (lower (has_type (ty_supported_vec ty) (iadd x (imul y z)))) +(rule 9 (lower (has_type (ty_supported_vec ty) (iadd _ x (imul _ y z)))) (rv_vmacc_vv x y z (unmasked) ty)) -(rule 10 (lower (has_type (ty_supported_vec ty) (iadd x (imul y (splat z))))) +(rule 10 (lower (has_type (ty_supported_vec ty) (iadd _ x (imul _ y (splat _ z))))) (rv_vmacc_vx x y z (unmasked) ty)) -(rule 11 (lower (has_type (ty_supported_vec ty) (iadd x (imul (splat y) z)))) +(rule 11 (lower (has_type (ty_supported_vec ty) (iadd _ x (imul _ (splat _ y) z)))) (rv_vmacc_vx x z y (unmasked) ty)) -(rule 12 (lower (has_type (ty_supported_vec ty) (iadd (imul x y) z))) +(rule 12 (lower (has_type (ty_supported_vec ty) (iadd _ (imul _ x y) z))) (rv_vmacc_vv z x y (unmasked) ty)) -(rule 13 (lower (has_type (ty_supported_vec ty) (iadd (imul x (splat y)) z))) +(rule 13 (lower (has_type (ty_supported_vec ty) (iadd _ (imul _ x (splat _ y)) z))) (rv_vmacc_vx z x y (unmasked) ty)) -(rule 14 (lower (has_type (ty_supported_vec ty) (iadd (imul (splat x) y) z))) +(rule 14 (lower (has_type (ty_supported_vec ty) (iadd _ (imul _ (splat _ x) y) z))) (rv_vmacc_vx z y x (unmasked) ty)) ;; Fused Multiply Subtract Rules `vnmsac` -(rule 9 (lower (has_type (ty_supported_vec ty) (iadd x (ineg (imul y z))))) +(rule 9 (lower (has_type (ty_supported_vec ty) (iadd _ x (ineg _ (imul _ y z))))) (rv_vnmsac_vv x y z (unmasked) ty)) -(rule 10 (lower (has_type (ty_supported_vec ty) (iadd x (ineg (imul y (splat z)))))) +(rule 10 (lower (has_type (ty_supported_vec ty) (iadd _ x (ineg _ (imul _ y (splat _ z)))))) (rv_vnmsac_vx x y z (unmasked) ty)) -(rule 11 (lower (has_type (ty_supported_vec ty) (iadd x (ineg (imul (splat y) z))))) +(rule 11 (lower (has_type (ty_supported_vec ty) (iadd _ x (ineg _ (imul _ (splat _ y) z))))) (rv_vnmsac_vx x z y (unmasked) ty)) -(rule 12 (lower (has_type (ty_supported_vec ty) (iadd (ineg (imul x y)) z))) +(rule 12 (lower (has_type (ty_supported_vec ty) (iadd _ (ineg _ (imul _ x y)) z))) (rv_vnmsac_vv z x y (unmasked) ty)) -(rule 13 (lower (has_type (ty_supported_vec ty) (iadd (ineg (imul x (splat y))) z))) +(rule 13 (lower (has_type (ty_supported_vec ty) (iadd _ (ineg _ (imul _ x (splat _ y))) z))) (rv_vnmsac_vx z x y (unmasked) ty)) -(rule 14 (lower (has_type (ty_supported_vec ty) (iadd (ineg (imul (splat x) y)) z))) +(rule 14 (lower (has_type (ty_supported_vec ty) (iadd _ (ineg _ (imul _ (splat _ x) y)) z))) (rv_vnmsac_vx z y x (unmasked) ty)) ;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 ty) (uadd_overflow_trap x y tc))) +(rule 0 (lower (has_type (fits_in_32 ty) (uadd_overflow_trap _ x y tc))) (let ((tmp_x XReg (zext x)) (tmp_y XReg (zext y)) (sum XReg (rv_add tmp_x tmp_y)) @@ -315,7 +315,7 @@ (_ InstOutput (gen_trapnz test tc))) sum)) -(rule 1 (lower (has_type $I64 (uadd_overflow_trap x y tc))) +(rule 1 (lower (has_type $I64 (uadd_overflow_trap _ x y tc))) (let ((tmp XReg (rv_add x y)) (_ InstOutput (gen_trapif (IntCC.UnsignedLessThan) tmp x tc))) tmp)) @@ -323,20 +323,21 @@ ;;;; Rules for uadd_overflow ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; For i64, we can use the fact that if a + b < a, then overflow occurred -(rule 0 (lower (has_type $I64 (uadd_overflow x y))) +(rule 0 (lower (has_type $I64 (uadd_overflow _ x y))) (let ((sum XReg (rv_add x y)) (overflow XReg (rv_sltu sum x))) (output_pair sum overflow))) ;; i32 case (on RV64 use addw to detect 32-bit overflow correctly) -(rule 1 (lower (has_type $I32 (uadd_overflow x y))) - (let ((x64 XReg (zext x)) - (sum XReg (rv_addw x y)) - (overflow XReg (rv_sltu sum x64))) +(rule 1 (lower (has_type $I32 (uadd_overflow _ x y))) + (let ((x64 XReg (zext x)) + (y64 XReg (zext y)) + (sum XReg (rv_add x64 y64)) + (overflow XReg (rv_srli sum (imm12_const 32)))) (output_pair sum overflow))) ;; For i128, we need to handle the high and low parts separately -(rule 2 (lower (has_type $I128 (uadd_overflow x y))) +(rule 2 (lower (has_type $I128 (uadd_overflow _ x y))) (let ((x_regs ValueRegs x) (y_regs ValueRegs y) (x_lo XReg (value_regs_get x_regs 0)) @@ -347,154 +348,154 @@ (carry XReg (rv_sltu sum_lo x_lo)) (sum_hi XReg (rv_add x_hi y_hi)) (sum_hi_with_carry XReg (rv_add sum_hi carry)) - (overflow XReg (rv_or (rv_sltu sum_hi_with_carry x_hi) + (overflow XReg (rv_or (rv_sltu sum_hi_with_carry x_hi) (rv_and carry (rv_seqz (rv_xor sum_hi_with_carry x_hi)))))) (output_pair (value_regs sum_lo sum_hi_with_carry) overflow))) ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Base case, simply subtracting things in registers. -(rule 0 (lower (has_type (fits_in_32 (ty_int ty)) (isub x y))) +(rule 0 (lower (has_type (fits_in_32 (ty_int ty)) (isub _ x y))) (rv_subw x y)) -(rule 1 (lower (has_type $I64 (isub x y))) +(rule 1 (lower (has_type $I64 (isub _ x y))) (rv_sub x y)) -(rule 2 (lower (has_type $I128 (isub x y))) +(rule 2 (lower (has_type $I128 (isub _ x y))) (sub_i128 x y)) ;; Switch to an `addi` by a negative if we can fit the value in an `imm12`. -(rule 3 (lower (has_type (ty_int_ref_scalar_64 ty) (isub x y))) +(rule 3 (lower (has_type (ty_int_ref_scalar_64 ty) (isub _ x y))) (if-let imm12_neg (imm12_from_negated_value y)) (alu_rr_imm12 (select_addi ty) x imm12_neg)) ;; SIMD Vectors -(rule 4 (lower (has_type (ty_supported_vec ty) (isub x y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (isub _ x y))) (rv_vsub_vv x y (unmasked) ty)) -(rule 5 (lower (has_type (ty_supported_vec ty) (isub x (splat y)))) +(rule 5 (lower (has_type (ty_supported_vec ty) (isub _ x (splat _ y)))) (rv_vsub_vx x y (unmasked) ty)) -(rule 6 (lower (has_type (ty_supported_vec ty) (isub x (splat (sextend y @ (value_type sext_ty)))))) +(rule 6 (lower (has_type (ty_supported_vec ty) (isub _ x (splat _ (sextend _ y @ (value_type sext_ty)))))) (if-let half_ty (ty_half_width ty)) (if-let true (ty_equal (lane_type half_ty) sext_ty)) (rv_vwsub_wx x y (unmasked) (vstate_mf2 half_ty))) -(rule 6 (lower (has_type (ty_supported_vec ty) (isub x (splat (uextend y @ (value_type uext_ty)))))) +(rule 6 (lower (has_type (ty_supported_vec ty) (isub _ x (splat _ (uextend _ y @ (value_type uext_ty)))))) (if-let half_ty (ty_half_width ty)) (if-let true (ty_equal (lane_type half_ty) uext_ty)) (rv_vwsubu_wx x y (unmasked) (vstate_mf2 half_ty))) -(rule 7 (lower (has_type (ty_supported_vec ty) (isub (splat x) y))) +(rule 7 (lower (has_type (ty_supported_vec ty) (isub _ (splat _ x) y))) (rv_vrsub_vx y x (unmasked) ty)) -(rule 8 (lower (has_type (ty_supported_vec ty) (isub x y))) +(rule 8 (lower (has_type (ty_supported_vec ty) (isub _ x y))) (if-let imm5_neg (negated_replicated_imm5 y)) (rv_vadd_vi x imm5_neg (unmasked) ty)) -(rule 9 (lower (has_type (ty_supported_vec ty) (isub x y))) +(rule 9 (lower (has_type (ty_supported_vec ty) (isub _ x y))) (if-let x_imm (replicated_imm5 x)) (rv_vrsub_vi y x_imm (unmasked) ty)) ;; Signed Widening Low Subtractions -(rule 6 (lower (has_type (ty_supported_vec _) (isub x (swiden_low y @ (value_type in_ty))))) +(rule 6 (lower (has_type (ty_supported_vec _) (isub _ x (swiden_low _ y @ (value_type in_ty))))) (rv_vwsub_wv x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (swiden_low x @ (value_type in_ty)) - (swiden_low y)))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (swiden_low _ x @ (value_type in_ty)) + (swiden_low _ y)))) (rv_vwsub_vv x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (swiden_low x @ (value_type in_ty)) - (splat (sextend y @ (value_type sext_ty)))))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (swiden_low _ x @ (value_type in_ty)) + (splat _ (sextend _ y @ (value_type sext_ty)))))) (if-let true (ty_equal (lane_type in_ty) sext_ty)) (rv_vwsub_vx x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Signed Widening High Subtractions ;; These are the same as the low widenings, but we first slide down the inputs. -(rule 6 (lower (has_type (ty_supported_vec _) (isub x (swiden_high y @ (value_type in_ty))))) +(rule 6 (lower (has_type (ty_supported_vec _) (isub _ x (swiden_high _ y @ (value_type in_ty))))) (rv_vwsub_wv x (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (swiden_high x @ (value_type in_ty)) - (swiden_high y)))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (swiden_high _ x @ (value_type in_ty)) + (swiden_high _ y)))) (rv_vwsub_vv (gen_slidedown_half in_ty x) (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (swiden_high x @ (value_type in_ty)) - (splat (sextend y @ (value_type sext_ty)))))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (swiden_high _ x @ (value_type in_ty)) + (splat _ (sextend _ y @ (value_type sext_ty)))))) (if-let true (ty_equal (lane_type in_ty) sext_ty)) (rv_vwsub_vx (gen_slidedown_half in_ty x) y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Unsigned Widening Low Subtractions -(rule 6 (lower (has_type (ty_supported_vec _) (isub x (uwiden_low y @ (value_type in_ty))))) +(rule 6 (lower (has_type (ty_supported_vec _) (isub _ x (uwiden_low _ y @ (value_type in_ty))))) (rv_vwsubu_wv x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (uwiden_low x @ (value_type in_ty)) - (uwiden_low y)))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (uwiden_low _ x @ (value_type in_ty)) + (uwiden_low _ y)))) (rv_vwsubu_vv x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (uwiden_low x @ (value_type in_ty)) - (splat (uextend y @ (value_type uext_ty)))))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (uwiden_low _ x @ (value_type in_ty)) + (splat _ (uextend _ y @ (value_type uext_ty)))))) (if-let true (ty_equal (lane_type in_ty) uext_ty)) (rv_vwsubu_vx x y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Unsigned Widening High Subtractions ;; These are the same as the low widenings, but we first slide down the inputs. -(rule 6 (lower (has_type (ty_supported_vec _) (isub x (uwiden_high y @ (value_type in_ty))))) +(rule 6 (lower (has_type (ty_supported_vec _) (isub _ x (uwiden_high _ y @ (value_type in_ty))))) (rv_vwsubu_wv x (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (uwiden_high x @ (value_type in_ty)) - (uwiden_high y)))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (uwiden_high _ x @ (value_type in_ty)) + (uwiden_high _ y)))) (rv_vwsubu_vv (gen_slidedown_half in_ty x) (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (uwiden_high x @ (value_type in_ty)) - (splat (uextend y @ (value_type uext_ty)))))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (uwiden_high _ x @ (value_type in_ty)) + (splat _ (uextend _ y @ (value_type uext_ty)))))) (if-let true (ty_equal (lane_type in_ty) uext_ty)) (rv_vwsubu_vx (gen_slidedown_half in_ty x) y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Signed Widening Mixed High/Low Subtractions -(rule 10 (lower (has_type (ty_supported_vec _) (isub (swiden_low x @ (value_type in_ty)) - (swiden_high y)))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (swiden_low _ x @ (value_type in_ty)) + (swiden_high _ y)))) (rv_vwsub_vv x (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (swiden_high x @ (value_type in_ty)) - (swiden_low y)))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (swiden_high _ x @ (value_type in_ty)) + (swiden_low _ y)))) (rv_vwsub_vv (gen_slidedown_half in_ty x) y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;; Unsigned Widening Mixed High/Low Subtractions -(rule 10 (lower (has_type (ty_supported_vec _) (isub (uwiden_low x @ (value_type in_ty)) - (uwiden_high y)))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (uwiden_low _ x @ (value_type in_ty)) + (uwiden_high _ y)))) (rv_vwsubu_vv x (gen_slidedown_half in_ty y) (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) -(rule 10 (lower (has_type (ty_supported_vec _) (isub (uwiden_high x @ (value_type in_ty)) - (uwiden_low y)))) +(rule 10 (lower (has_type (ty_supported_vec _) (isub _ (uwiden_high _ x @ (value_type in_ty)) + (uwiden_low _ y)))) (rv_vwsubu_vv (gen_slidedown_half in_ty x) y (unmasked) (vstate_mf2 (ty_half_lanes in_ty)))) ;;;; Rules for `ineg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_int ty) (ineg val))) +(rule (lower (has_type (ty_int ty) (ineg _ val))) (neg ty val)) -(rule 1 (lower (has_type (ty_supported_vec ty) (ineg x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (ineg _ x))) (rv_vneg_v x (unmasked) ty)) ;;;; Rules for `imul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (imul x y))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (imul _ x y))) (rv_mul x y)) -(rule 1 (lower (has_type (fits_in_32 (ty_int ty)) (imul x y))) +(rule 1 (lower (has_type (fits_in_32 (ty_int ty)) (imul _ x y))) (rv_mulw x y)) ;; for I128 -(rule 2 (lower (has_type $I128 (imul x y))) +(rule 2 (lower (has_type $I128 (imul _ x y))) (let ((x_regs ValueRegs x) (x_lo XReg (value_regs_get x_regs 0)) @@ -522,82 +523,82 @@ ;; Special case 128-bit multiplication where the operands are extended since ;; that maps directly to the `mulhu` and `mulh` instructions. -(rule 6 (lower (has_type $I128 (imul (uextend x) (uextend y)))) +(rule 6 (lower (has_type $I128 (imul _ (uextend _ x) (uextend _ y)))) (let ((x XReg (zext x)) (y XReg (zext y))) (value_regs (rv_mul x y) (rv_mulhu x y)))) -(rule 6 (lower (has_type $I128 (imul (sextend x) (sextend y)))) +(rule 6 (lower (has_type $I128 (imul _ (sextend _ x) (sextend _ y)))) (let ((x XReg (sext x)) (y XReg (sext y))) (value_regs (rv_mul x y) (rv_mulh x y)))) ;; Vector multiplication -(rule 3 (lower (has_type (ty_supported_vec ty) (imul x y))) +(rule 3 (lower (has_type (ty_supported_vec ty) (imul _ x y))) (rv_vmul_vv x y (unmasked) ty)) -(rule 4 (lower (has_type (ty_supported_vec ty) (imul (splat x) y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (imul _ (splat _ x) y))) (rv_vmul_vx y x (unmasked) ty)) -(rule 5 (lower (has_type (ty_supported_vec ty) (imul x (splat y)))) +(rule 5 (lower (has_type (ty_supported_vec ty) (imul _ x (splat _ y)))) (rv_vmul_vx x y (unmasked) ty)) ;;;; Rules for `smulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (smulhi x y))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (smulhi _ x y))) (lower_smlhi ty (sext x) (sext y))) -(rule 1 (lower (has_type (ty_supported_vec ty) (smulhi x y))) +(rule 1 (lower (has_type (ty_supported_vec ty) (smulhi _ x y))) (rv_vmulh_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (smulhi (splat x) y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (smulhi _ (splat _ x) y))) (rv_vmulh_vx y x (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (smulhi x (splat y)))) +(rule 3 (lower (has_type (ty_supported_vec ty) (smulhi _ x (splat _ y)))) (rv_vmulh_vx x y (unmasked) ty)) ;;;; Rules for `umulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_32 ty) (umulhi x y))) +(rule 0 (lower (has_type (fits_in_32 ty) (umulhi _ x y))) (let ((tmp XReg (rv_mul (zext x) (zext y)))) (rv_srli tmp (imm12_const (ty_bits ty))))) -(rule 1 (lower (has_type $I64 (umulhi x y))) +(rule 1 (lower (has_type $I64 (umulhi _ x y))) (rv_mulhu x y)) -(rule 2 (lower (has_type (ty_supported_vec ty) (umulhi x y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (umulhi _ x y))) (rv_vmulhu_vv x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (umulhi (splat x) y))) +(rule 3 (lower (has_type (ty_supported_vec ty) (umulhi _ (splat _ x) y))) (rv_vmulhu_vx y x (unmasked) ty)) -(rule 4 (lower (has_type (ty_supported_vec ty) (umulhi x (splat y)))) +(rule 4 (lower (has_type (ty_supported_vec ty) (umulhi _ x (splat _ y)))) (rv_vmulhu_vx x y (unmasked) ty)) ;;;; Rules for `udiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_16 ty) (udiv x y))) +(rule 0 (lower (has_type (fits_in_16 ty) (udiv _ x y))) (if-let true (has_m)) (rv_divuw (zext x) (nonzero_divisor (zext y)))) -(rule 1 (lower (has_type (fits_in_16 ty) (udiv x y @ (iconst imm)))) +(rule 1 (lower (has_type (fits_in_16 ty) (udiv _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 ty imm)) (rv_divuw (zext x) (zext y))) -(rule 2 (lower (has_type $I32 (udiv x y))) +(rule 2 (lower (has_type $I32 (udiv _ x y))) (if-let true (has_m)) (rv_divuw x (nonzero_divisor (zext y)))) -(rule 3 (lower (has_type $I32 (udiv x y @ (iconst imm)))) +(rule 3 (lower (has_type $I32 (udiv _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 $I32 imm)) (rv_divuw x y)) -(rule 2 (lower (has_type $I64 (udiv x y))) +(rule 2 (lower (has_type $I64 (udiv _ x y))) (if-let true (has_m)) (rv_divu x (nonzero_divisor y))) -(rule 3 (lower (has_type $I64 (udiv x y @ (iconst imm)))) +(rule 3 (lower (has_type $I64 (udiv _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 $I64 imm)) (rv_divu x y)) @@ -610,31 +611,31 @@ ;;;; Rules for `sdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_16 ty) (sdiv x y))) +(rule 0 (lower (has_type (fits_in_16 ty) (sdiv _ x y))) (if-let true (has_m)) (let ((x XReg (sext x))) (rv_divw x (safe_sdiv_divisor ty x (sext y))))) -(rule 1 (lower (has_type (fits_in_16 ty) (sdiv x y @ (iconst imm)))) +(rule 1 (lower (has_type (fits_in_16 ty) (sdiv _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 ty imm)) (rv_divw (sext x) (sext y))) -(rule 2 (lower (has_type $I32 (sdiv x y))) +(rule 2 (lower (has_type $I32 (sdiv _ x y))) (if-let true (has_m)) (let ((x XReg (sext x))) (rv_divw x (safe_sdiv_divisor $I32 x (sext y))))) -(rule 3 (lower (has_type $I32 (sdiv x y @ (iconst imm)))) +(rule 3 (lower (has_type $I32 (sdiv _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 $I32 imm)) (rv_divw x y)) -(rule 2 (lower (has_type $I64 (sdiv x y))) +(rule 2 (lower (has_type $I64 (sdiv _ x y))) (if-let true (has_m)) (rv_div x (safe_sdiv_divisor $I64 x y))) -(rule 3 (lower (has_type $I64 (sdiv x y @ (iconst imm)))) +(rule 3 (lower (has_type $I64 (sdiv _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 $I64 imm)) (rv_div x y)) @@ -660,84 +661,84 @@ ;;;; Rules for `urem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_16 ty) (urem x y))) +(rule 0 (lower (has_type (fits_in_16 ty) (urem _ x y))) (if-let true (has_m)) (rv_remuw (zext x) (nonzero_divisor (zext y)))) -(rule 1 (lower (has_type (fits_in_16 ty) (urem x y @ (iconst imm)))) +(rule 1 (lower (has_type (fits_in_16 ty) (urem _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 ty imm)) (rv_remuw (zext x) (zext y))) -(rule 2 (lower (has_type $I32 (urem x y))) +(rule 2 (lower (has_type $I32 (urem _ x y))) (if-let true (has_m)) (rv_remuw x (nonzero_divisor (zext y)))) -(rule 3 (lower (has_type $I32 (urem x y @ (iconst imm)))) +(rule 3 (lower (has_type $I32 (urem _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 $I32 imm)) (rv_remuw x y)) -(rule 2 (lower (has_type $I64 (urem x y))) +(rule 2 (lower (has_type $I64 (urem _ x y))) (if-let true (has_m)) (rv_remu x (nonzero_divisor y))) -(rule 3 (lower (has_type $I64 (urem x y @ (iconst imm)))) +(rule 3 (lower (has_type $I64 (urem _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 $I64 imm)) (rv_remu x y)) ;;;; Rules for `srem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_16 ty) (srem x y))) +(rule 0 (lower (has_type (fits_in_16 ty) (srem _ x y))) (if-let true (has_m)) (rv_remw (sext x) (nonzero_divisor (sext y)))) -(rule 1 (lower (has_type (fits_in_16 ty) (srem x y @ (iconst imm)))) +(rule 1 (lower (has_type (fits_in_16 ty) (srem _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 ty imm)) (rv_remw (sext x) (sext y))) -(rule 2 (lower (has_type $I32 (srem x y))) +(rule 2 (lower (has_type $I32 (srem _ x y))) (if-let true (has_m)) (rv_remw x (nonzero_divisor (sext y)))) -(rule 3 (lower (has_type $I32 (srem x y @ (iconst imm)))) +(rule 3 (lower (has_type $I32 (srem _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 $I32 imm)) (rv_remw x y)) -(rule 2 (lower (has_type $I64 (srem x y))) +(rule 2 (lower (has_type $I64 (srem _ x y))) (if-let true (has_m)) (rv_rem x (nonzero_divisor y))) -(rule 3 (lower (has_type $I64 (srem x y @ (iconst imm)))) +(rule 3 (lower (has_type $I64 (srem _ x y @ (iconst _ imm)))) (if-let true (has_m)) (if (safe_divisor_from_imm64 $I64 imm)) (rv_rem x y)) ;;;; Rules for `and` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type (fits_in_64 ty) (band x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (band _ x y))) (rv_and x y)) -(rule 0 (lower (has_type (ty_reg_pair _) (band x y))) +(rule 0 (lower (has_type (ty_reg_pair _) (band _ x y))) (value_regs (rv_and (value_regs_get x 0) (value_regs_get y 0)) (rv_and (value_regs_get x 1) (value_regs_get y 1)))) ;; Special cases for when one operand is an immediate that fits in 12 bits. -(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (band x (imm12_from_value y)))) +(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (band _ x (imm12_from_value y)))) (rv_andi x y)) -(rule 2 (lower (has_type (fits_in_64 (ty_int ty)) (band (imm12_from_value x) y))) +(rule 2 (lower (has_type (fits_in_64 (ty_int ty)) (band _ (imm12_from_value x) y))) (rv_andi y x)) -(rule 3 (lower (has_type (ty_supported_float_size ty) (band x y))) +(rule 3 (lower (has_type (ty_supported_float_size ty) (band _ x y))) (lower_float_binary (AluOPRRR.And) x y ty)) ;; No need to NaN-box when moving back to the floating point register as the high ;; bits will already be set. -(rule 4 (lower (has_type (ty_supported_float_size $F16) (band x y))) +(rule 4 (lower (has_type (ty_supported_float_size $F16) (band _ x y))) (if-let false (has_zfhmin)) (lower_float_binary (AluOPRRR.And) x y $F32)) @@ -745,66 +746,66 @@ ;; by Cranelift's `band_not` instruction that is legalized into the simpler ;; forms early on. -(rule 5 (lower (has_type (fits_in_64 (ty_int ty)) (band x (bnot y)))) +(rule 5 (lower (has_type (fits_in_64 (ty_int ty)) (band _ x (bnot _ y)))) (if-let true (has_zbb)) (rv_andn x y)) -(rule 6 (lower (has_type (fits_in_64 (ty_int ty)) (band (bnot y) x))) +(rule 6 (lower (has_type (fits_in_64 (ty_int ty)) (band _ (bnot _ y) x))) (if-let true (has_zbb)) (rv_andn x y)) -(rule 7 (lower (has_type (ty_reg_pair _) (band x (bnot y)))) +(rule 7 (lower (has_type (ty_reg_pair _) (band _ x (bnot _ y)))) (if-let true (has_zbb)) (let ((low XReg (rv_andn (value_regs_get x 0) (value_regs_get y 0))) (high XReg (rv_andn (value_regs_get x 1) (value_regs_get y 1)))) (value_regs low high))) -(rule 8 (lower (has_type (ty_reg_pair _) (band (bnot y) x))) +(rule 8 (lower (has_type (ty_reg_pair _) (band _ (bnot _ y) x))) (if-let true (has_zbb)) (let ((low XReg (rv_andn (value_regs_get x 0) (value_regs_get y 0))) (high XReg (rv_andn (value_regs_get x 1) (value_regs_get y 1)))) (value_regs low high))) -(rule 9 (lower (has_type (ty_supported_vec ty) (band x y))) +(rule 9 (lower (has_type (ty_supported_vec ty) (band _ x y))) (rv_vand_vv x y (unmasked) ty)) -(rule 10 (lower (has_type (ty_supported_vec ty) (band x (splat y)))) +(rule 10 (lower (has_type (ty_supported_vec ty) (band _ x (splat _ y)))) (if (ty_vector_not_float ty)) (rv_vand_vx x y (unmasked) ty)) -(rule 11 (lower (has_type (ty_supported_vec ty) (band (splat x) y))) +(rule 11 (lower (has_type (ty_supported_vec ty) (band _ (splat _ x) y))) (if (ty_vector_not_float ty)) (rv_vand_vx y x (unmasked) ty)) -(rule 12 (lower (has_type (ty_supported_vec ty) (band x y))) +(rule 12 (lower (has_type (ty_supported_vec ty) (band _ x y))) (if-let y_imm (replicated_imm5 y)) (rv_vand_vi x y_imm (unmasked) ty)) -(rule 13 (lower (has_type (ty_supported_vec ty) (band x y))) +(rule 13 (lower (has_type (ty_supported_vec ty) (band _ x y))) (if-let x_imm (replicated_imm5 x)) (rv_vand_vi y x_imm (unmasked) ty)) ;; `bclr{,i}` specializations from `zbs` -(rule 14 (lower (has_type (fits_in_32 ty) (band x (bnot (ishl (i64_from_iconst 1) y))))) +(rule 14 (lower (has_type (fits_in_32 ty) (band _ x (bnot _ (ishl _ (i64_from_iconst 1) y))))) (if-let true (has_zbs)) (rv_bclr x (rv_andi y (imm12_const (u8_wrapping_sub (ty_bits ty) 1))))) -(rule 15 (lower (has_type (fits_in_32 ty) (band (bnot (ishl (i64_from_iconst 1) y)) x))) +(rule 15 (lower (has_type (fits_in_32 ty) (band _ (bnot _ (ishl _ (i64_from_iconst 1) y)) x))) (if-let true (has_zbs)) (rv_bclr x (rv_andi y (imm12_const (u8_wrapping_sub (ty_bits ty) 1))))) -(rule 16 (lower (has_type $I64 (band x (bnot (ishl (i64_from_iconst 1) y))))) +(rule 16 (lower (has_type $I64 (band _ x (bnot _ (ishl _ (i64_from_iconst 1) y))))) (if-let true (has_zbs)) (rv_bclr x y)) -(rule 17 (lower (has_type $I64 (band (bnot (ishl (i64_from_iconst 1) y)) x))) +(rule 17 (lower (has_type $I64 (band _ (bnot _ (ishl _ (i64_from_iconst 1) y)) x))) (if-let true (has_zbs)) (rv_bclr x y)) -(rule 18 (lower (has_type (fits_in_64 ty) (band x (u64_from_iconst n)))) +(rule 18 (lower (has_type (fits_in_64 ty) (band _ x (u64_from_iconst n)))) (if-let true (has_zbs)) (if-let imm (bclr_imm ty n)) (rv_bclri x imm)) -(rule 19 (lower (has_type (fits_in_64 ty) (band (u64_from_iconst n) x))) +(rule 19 (lower (has_type (fits_in_64 ty) (band _ (u64_from_iconst n) x))) (if-let true (has_zbs)) (if-let imm (bclr_imm ty n)) (rv_bclri x imm)) @@ -814,65 +815,65 @@ ;; `bext{,i}` specializations from `zbs` -(rule 20 (lower (has_type $I32 (band (ushr x y) (u64_from_iconst 1)))) +(rule 20 (lower (has_type $I32 (band _ (ushr _ x y) (u64_from_iconst 1)))) (if-let true (has_zbs)) (rv_bext x (rv_andi y (imm12_const 31)))) -(rule 20 (lower (has_type $I32 (band (sshr x y) (u64_from_iconst 1)))) +(rule 20 (lower (has_type $I32 (band _ (sshr _ x y) (u64_from_iconst 1)))) (if-let true (has_zbs)) (rv_bext x (rv_andi y (imm12_const 31)))) -(rule 20 (lower (has_type $I32 (band (u64_from_iconst 1) (ushr x y)))) +(rule 20 (lower (has_type $I32 (band _ (u64_from_iconst 1) (ushr _ x y)))) (if-let true (has_zbs)) (rv_bext x (rv_andi y (imm12_const 31)))) -(rule 20 (lower (has_type $I32 (band (u64_from_iconst 1) (sshr x y)))) +(rule 20 (lower (has_type $I32 (band _ (u64_from_iconst 1) (sshr _ x y)))) (if-let true (has_zbs)) (rv_bext x (rv_andi y (imm12_const 31)))) -(rule 20 (lower (has_type $I64 (band (ushr x y) (u64_from_iconst 1)))) +(rule 20 (lower (has_type $I64 (band _ (ushr _ x y) (u64_from_iconst 1)))) (if-let true (has_zbs)) (rv_bext x y)) -(rule 20 (lower (has_type $I64 (band (sshr x y) (u64_from_iconst 1)))) +(rule 20 (lower (has_type $I64 (band _ (sshr _ x y) (u64_from_iconst 1)))) (if-let true (has_zbs)) (rv_bext x y)) -(rule 20 (lower (has_type $I64 (band (u64_from_iconst 1) (ushr x y)))) +(rule 20 (lower (has_type $I64 (band _ (u64_from_iconst 1) (ushr _ x y)))) (if-let true (has_zbs)) (rv_bext x y)) -(rule 20 (lower (has_type $I64 (band (u64_from_iconst 1) (sshr x y)))) +(rule 20 (lower (has_type $I64 (band _ (u64_from_iconst 1) (sshr _ x y)))) (if-let true (has_zbs)) (rv_bext x y)) -(rule 21 (lower (has_type $I32 (band (ushr x (imm12_from_value y)) (u64_from_iconst 1)))) +(rule 21 (lower (has_type $I32 (band _ (ushr _ x (imm12_from_value y)) (u64_from_iconst 1)))) (if-let true (has_zbs)) (rv_bexti x (imm12_and y 31))) -(rule 21 (lower (has_type $I32 (band (sshr x (imm12_from_value y)) (u64_from_iconst 1)))) +(rule 21 (lower (has_type $I32 (band _ (sshr _ x (imm12_from_value y)) (u64_from_iconst 1)))) (if-let true (has_zbs)) (rv_bexti x (imm12_and y 31))) -(rule 21 (lower (has_type $I64 (band (ushr x (imm12_from_value y)) (u64_from_iconst 1)))) +(rule 21 (lower (has_type $I64 (band _ (ushr _ x (imm12_from_value y)) (u64_from_iconst 1)))) (if-let true (has_zbs)) (rv_bexti x (imm12_and y 63))) -(rule 21 (lower (has_type $I64 (band (sshr x (imm12_from_value y)) (u64_from_iconst 1)))) +(rule 21 (lower (has_type $I64 (band _ (sshr _ x (imm12_from_value y)) (u64_from_iconst 1)))) (if-let true (has_zbs)) (rv_bexti x (imm12_and y 63))) ;;;; Rules for `or` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -1 (lower (has_type (ty_int ty) (bor x y))) +(rule -1 (lower (has_type (ty_int ty) (bor _ x y))) (gen_or ty x y)) -(rule 0 (lower (has_type $F128 (bor x y))) +(rule 0 (lower (has_type $F128 (bor _ x y))) (gen_or $I128 x y)) ;; Special cases for when one operand is an immediate that fits in 12 bits. -(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (bor x (imm12_from_value y)))) +(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (bor _ x (imm12_from_value y)))) (rv_ori x y)) -(rule 2 (lower (has_type (fits_in_64 (ty_int ty)) (bor (imm12_from_value x) y))) +(rule 2 (lower (has_type (fits_in_64 (ty_int ty)) (bor _ (imm12_from_value x) y))) (rv_ori y x)) -(rule 3 (lower (has_type (ty_supported_float_size ty) (bor x y))) +(rule 3 (lower (has_type (ty_supported_float_size ty) (bor _ x y))) (lower_float_binary (AluOPRRR.Or) x y ty)) ;; No need to NaN-box when moving back to the floating point register as the high ;; bits will already be set. -(rule 4 (lower (has_type (ty_supported_float_size $F16) (bor x y))) +(rule 4 (lower (has_type (ty_supported_float_size $F16) (bor _ x y))) (if-let false (has_zfhmin)) (lower_float_binary (AluOPRRR.Or) x y $F32)) @@ -880,66 +881,66 @@ ;; by Cranelift's `bor_not` instruction that is legalized into the simpler ;; forms early on. -(rule 5 (lower (has_type (fits_in_64 (ty_int ty)) (bor x (bnot y)))) +(rule 5 (lower (has_type (fits_in_64 (ty_int ty)) (bor _ x (bnot _ y)))) (if-let true (has_zbb)) (rv_orn x y)) -(rule 6 (lower (has_type (fits_in_64 (ty_int ty)) (bor (bnot y) x))) +(rule 6 (lower (has_type (fits_in_64 (ty_int ty)) (bor _ (bnot _ y) x))) (if-let true (has_zbb)) (rv_orn x y)) -(rule 7 (lower (has_type (ty_reg_pair _) (bor x (bnot y)))) +(rule 7 (lower (has_type (ty_reg_pair _) (bor _ x (bnot _ y)))) (if-let true (has_zbb)) (let ((low XReg (rv_orn (value_regs_get x 0) (value_regs_get y 0))) (high XReg (rv_orn (value_regs_get x 1) (value_regs_get y 1)))) (value_regs low high))) -(rule 8 (lower (has_type (ty_reg_pair _) (bor (bnot y) x))) +(rule 8 (lower (has_type (ty_reg_pair _) (bor _ (bnot _ y) x))) (if-let true (has_zbb)) (let ((low XReg (rv_orn (value_regs_get x 0) (value_regs_get y 0))) (high XReg (rv_orn (value_regs_get x 1) (value_regs_get y 1)))) (value_regs low high))) -(rule 9 (lower (has_type (ty_supported_vec ty) (bor x y))) +(rule 9 (lower (has_type (ty_supported_vec ty) (bor _ x y))) (rv_vor_vv x y (unmasked) ty)) -(rule 10 (lower (has_type (ty_supported_vec ty) (bor x (splat y)))) +(rule 10 (lower (has_type (ty_supported_vec ty) (bor _ x (splat _ y)))) (if (ty_vector_not_float ty)) (rv_vor_vx x y (unmasked) ty)) -(rule 11 (lower (has_type (ty_supported_vec ty) (bor (splat x) y))) +(rule 11 (lower (has_type (ty_supported_vec ty) (bor _ (splat _ x) y))) (if (ty_vector_not_float ty)) (rv_vor_vx y x (unmasked) ty)) -(rule 12 (lower (has_type (ty_supported_vec ty) (bor x y))) +(rule 12 (lower (has_type (ty_supported_vec ty) (bor _ x y))) (if-let y_imm (replicated_imm5 y)) (rv_vor_vi x y_imm (unmasked) ty)) -(rule 13 (lower (has_type (ty_supported_vec ty) (bor x y))) +(rule 13 (lower (has_type (ty_supported_vec ty) (bor _ x y))) (if-let x_imm (replicated_imm5 x)) (rv_vor_vi y x_imm (unmasked) ty)) ;; `bset{,i}` specializations from `zbs` -(rule 14 (lower (has_type $I32 (bor x (ishl (i64_from_iconst 1) y)))) +(rule 14 (lower (has_type $I32 (bor _ x (ishl _ (i64_from_iconst 1) y)))) (if-let true (has_zbs)) (rv_bset x (rv_andi y (imm12_const 31)))) -(rule 15 (lower (has_type $I32 (bor (ishl (i64_from_iconst 1) y) x))) +(rule 15 (lower (has_type $I32 (bor _ (ishl _ (i64_from_iconst 1) y) x))) (if-let true (has_zbs)) (rv_bset x (rv_andi y (imm12_const 31)))) -(rule 14 (lower (has_type $I64 (bor x (ishl (i64_from_iconst 1) y)))) +(rule 14 (lower (has_type $I64 (bor _ x (ishl _ (i64_from_iconst 1) y)))) (if-let true (has_zbs)) (rv_bset x y)) -(rule 15 (lower (has_type $I64 (bor (ishl (i64_from_iconst 1) y) x))) +(rule 15 (lower (has_type $I64 (bor _ (ishl _ (i64_from_iconst 1) y) x))) (if-let true (has_zbs)) (rv_bset x y)) -(rule 16 (lower (has_type (fits_in_64 _) (bor x (u64_from_iconst n)))) +(rule 16 (lower (has_type (fits_in_64 _) (bor _ x (u64_from_iconst n)))) (if-let true (has_zbs)) (if-let imm (bseti_imm n)) (rv_bseti x imm)) -(rule 17 (lower (has_type (fits_in_64 _) (bor (u64_from_iconst n) x))) +(rule 17 (lower (has_type (fits_in_64 _) (bor _ (u64_from_iconst n) x))) (if-let true (has_zbs)) (if-let imm (bseti_imm n)) (rv_bseti x imm)) @@ -948,62 +949,62 @@ (extern constructor bseti_imm bseti_imm) ;;;; Rules for `xor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 (ty_int ty)) (bxor x y))) +(rule 0 (lower (has_type (fits_in_64 (ty_int ty)) (bxor _ x y))) (rv_xor x y)) ;; Special cases for when one operand is an immediate that fits in 12 bits. -(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (bxor x (imm12_from_value y)))) +(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (bxor _ x (imm12_from_value y)))) (rv_xori x y)) -(rule 2 (lower (has_type (fits_in_64 (ty_int ty)) (bxor (imm12_from_value x) y))) +(rule 2 (lower (has_type (fits_in_64 (ty_int ty)) (bxor _ (imm12_from_value x) y))) (rv_xori y x)) -(rule 3 (lower (has_type (ty_reg_pair _) (bxor x y))) +(rule 3 (lower (has_type (ty_reg_pair _) (bxor _ x y))) (lower_b128_binary (AluOPRRR.Xor) x y)) -(rule 4 (lower (has_type (ty_supported_float_size ty) (bxor x y))) +(rule 4 (lower (has_type (ty_supported_float_size ty) (bxor _ x y))) (lower_float_binary (AluOPRRR.Xor) x y ty)) -(rule 5 (lower (has_type (ty_supported_vec ty) (bxor x y))) +(rule 5 (lower (has_type (ty_supported_vec ty) (bxor _ x y))) (rv_vxor_vv x y (unmasked) ty)) -(rule 6 (lower (has_type (ty_supported_vec ty) (bxor x (splat y)))) +(rule 6 (lower (has_type (ty_supported_vec ty) (bxor _ x (splat _ y)))) (if (ty_vector_not_float ty)) (rv_vxor_vx x y (unmasked) ty)) -(rule 7 (lower (has_type (ty_supported_vec ty) (bxor (splat x) y))) +(rule 7 (lower (has_type (ty_supported_vec ty) (bxor _ (splat _ x) y))) (if (ty_vector_not_float ty)) (rv_vxor_vx y x (unmasked) ty)) -(rule 8 (lower (has_type (ty_supported_vec ty) (bxor x y))) +(rule 8 (lower (has_type (ty_supported_vec ty) (bxor _ x y))) (if-let y_imm (replicated_imm5 y)) (rv_vxor_vi x y_imm (unmasked) ty)) -(rule 9 (lower (has_type (ty_supported_vec ty) (bxor x y))) +(rule 9 (lower (has_type (ty_supported_vec ty) (bxor _ x y))) (if-let x_imm (replicated_imm5 x)) (rv_vxor_vi y x_imm (unmasked) ty)) ;; `binv{,i}` specializations from `zbs` -(rule 13 (lower (has_type $I32 (bxor x (ishl (i64_from_iconst 1) y)))) +(rule 13 (lower (has_type $I32 (bxor _ x (ishl _ (i64_from_iconst 1) y)))) (if-let true (has_zbs)) (rv_binv x (rv_andi y (imm12_const 31)))) -(rule 14 (lower (has_type $I32 (bxor (ishl (i64_from_iconst 1) y) x))) +(rule 14 (lower (has_type $I32 (bxor _ (ishl _ (i64_from_iconst 1) y) x))) (if-let true (has_zbs)) (rv_binv x (rv_andi y (imm12_const 31)))) -(rule 13 (lower (has_type $I64 (bxor x (ishl (i64_from_iconst 1) y)))) +(rule 13 (lower (has_type $I64 (bxor _ x (ishl _ (i64_from_iconst 1) y)))) (if-let true (has_zbs)) (rv_binv x y)) -(rule 14 (lower (has_type $I64 (bxor (ishl (i64_from_iconst 1) y) x))) +(rule 14 (lower (has_type $I64 (bxor _ (ishl _ (i64_from_iconst 1) y) x))) (if-let true (has_zbs)) (rv_binv x y)) -(rule 15 (lower (has_type (fits_in_64 _) (bxor x (u64_from_iconst n)))) +(rule 15 (lower (has_type (fits_in_64 _) (bxor _ x (u64_from_iconst n)))) (if-let true (has_zbs)) (if-let imm (binvi_imm n)) (rv_binvi x imm)) -(rule 16 (lower (has_type (fits_in_64 _) (bxor (u64_from_iconst n) x))) +(rule 16 (lower (has_type (fits_in_64 _) (bxor _ (u64_from_iconst n) x))) (if-let true (has_zbs)) (if-let imm (binvi_imm n)) (rv_binvi x imm)) @@ -1013,30 +1014,30 @@ ;;;; Rules for `bnot` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 _) (bnot x))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64 _) (bnot _ x))) (rv_not x)) -(rule 1 (lower (has_type (ty_supported_float_size ty) (bnot x))) +(rule 1 (lower (has_type (ty_supported_float_size ty) (bnot _ x))) (move_x_to_f (rv_not (move_f_to_x x ty)) ty)) -(rule 2 (lower (has_type (ty_reg_pair _) (bnot x))) +(rule 2 (lower (has_type (ty_reg_pair _) (bnot _ x))) (value_regs (rv_not (value_regs_get x 0)) (rv_not (value_regs_get x 1)))) -(rule 3 (lower (has_type (ty_supported_vec ty) (bnot x))) +(rule 3 (lower (has_type (ty_supported_vec ty) (bnot _ x))) (rv_vnot_v x (unmasked) ty)) -(rule 4 (lower (has_type (ty_int_ref_scalar_64 _) (bnot (bxor x y)))) +(rule 4 (lower (has_type (ty_int_ref_scalar_64 _) (bnot _ (bxor _ x y)))) (if-let true (has_zbb)) (rv_xnor x y)) ;;;; Rules for `bit_reverse` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (bitrev x))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (bitrev _ x))) (gen_bitrev ty x)) -(rule 1 (lower (has_type $I128 (bitrev x))) +(rule 1 (lower (has_type $I128 (bitrev _ x))) (value_regs (gen_bitrev $I64 (value_regs_get x 1)) (gen_bitrev $I64 (value_regs_get x 0)))) @@ -1061,10 +1062,10 @@ ;;;; Rules for `bswap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (bswap x))) +(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (bswap _ x))) (gen_bswap ty x)) -(rule 2 (lower (has_type $I128 (bswap x))) +(rule 2 (lower (has_type $I128 (bswap _ x))) (value_regs (gen_bswap $I64 (value_regs_get x 1)) (gen_bswap $I64 (value_regs_get x 0)))) @@ -1107,10 +1108,10 @@ (rv_rev8 x)) ;;;; Rules for `ctz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_64 ty) (ctz x))) +(rule (lower (has_type (fits_in_64 ty) (ctz _ x))) (lower_ctz ty x)) -(rule 1 (lower (has_type $I128 (ctz x))) +(rule 1 (lower (has_type $I128 (ctz _ x))) (let ((x_lo XReg (value_regs_get x 0)) (x_hi XReg (value_regs_get x 1)) ;; Count both halves @@ -1122,10 +1123,10 @@ (value_regs result (imm $I64 0)))) ;;;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 ty) (clz x))) +(rule 0 (lower (has_type (fits_in_64 ty) (clz _ x))) (gen_cltz true x ty)) -(rule 1 (lower (has_type $I128 (clz x))) +(rule 1 (lower (has_type $I128 (clz _ x))) (let ((x_lo XReg (value_regs_get x 0)) (x_hi XReg (value_regs_get x 1)) ;; Count both halves @@ -1135,18 +1136,18 @@ (low XReg (gen_select_xreg (cmp_eqz x_hi) low (zero_reg)))) (value_regs (rv_add high low) (imm $I64 0)))) -(rule 2 (lower (has_type (fits_in_16 ty) (clz x))) +(rule 2 (lower (has_type (fits_in_16 ty) (clz _ x))) (if-let true (has_zbb)) (let ((tmp XReg (zext x)) (count XReg (rv_clz tmp))) ;; We always do the operation on the full 64-bit register, so subtract 64 from the result. (rv_addi count (imm12_const_add (ty_bits ty) -64)))) -(rule 3 (lower (has_type $I32 (clz x))) +(rule 3 (lower (has_type $I32 (clz _ x))) (if-let true (has_zbb)) (rv_clzw x)) -(rule 3 (lower (has_type $I64 (clz x))) +(rule 3 (lower (has_type $I64 (clz _ x))) (if-let true (has_zbb)) (rv_clz x)) @@ -1159,7 +1160,7 @@ ;;;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_64 ty) (cls x))) +(rule (lower (has_type (fits_in_64 ty) (cls _ x))) (let ((tmp XReg (sext x)) (tmp2 XReg (gen_select_xreg (cmp_ltz tmp) (rv_not tmp) tmp)) (tmp3 XReg (gen_clz tmp2))) @@ -1170,7 +1171,7 @@ ;; If the sign bit is set, we count the leading zeros of the inverted value. ;; Otherwise we can just count the leading zeros of the original value. ;; Subtract 1 since the sign bit does not count. -(rule 1 (lower (has_type $I128 (cls x))) +(rule 1 (lower (has_type $I128 (cls _ x))) (let ((low XReg (value_regs_get x 0)) (high XReg (value_regs_get x 1)) (low XReg (gen_select_xreg (cmp_ltz high) (rv_not low) low)) @@ -1187,14 +1188,14 @@ ;;;; Rules for `uextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 _) (uextend val))) +(rule 0 (lower (has_type (fits_in_64 _) (uextend _ val))) (zext val)) -(rule 1 (lower (has_type $I128 (uextend val))) +(rule 1 (lower (has_type $I128 (uextend _ val))) (value_regs (zext val) (imm $I64 0))) ;; When the source of an `uextend` is a load, we can merge both ops -(rule 2 (lower (has_type (fits_in_64 _) (uextend (sinkable_load inst ty flags addr offset)))) +(rule 2 (lower (has_type (fits_in_64 _) (uextend _ (sinkable_load inst ty flags addr offset)))) (gen_sunk_load inst (amode addr offset) (uextend_load_op ty) flags)) (decl pure uextend_load_op (Type) LoadOP) @@ -1203,15 +1204,15 @@ (rule (uextend_load_op $I32) (LoadOP.Lwu)) ;;;; Rules for `sextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 _) (sextend val @ (value_type in_ty)))) +(rule 0 (lower (has_type (fits_in_64 _) (sextend _ val @ (value_type in_ty)))) (sext val)) -(rule 1 (lower (has_type $I128 (sextend val @ (value_type in_ty)))) +(rule 1 (lower (has_type $I128 (sextend _ val @ (value_type in_ty)))) (let ((lo XReg (sext val))) (value_regs lo (rv_srai lo (imm12_const 63))))) ;; When the source of an `sextend` is a load, we can merge both ops -(rule 2 (lower (has_type (fits_in_64 _) (sextend (sinkable_load inst ty flags addr offset)))) +(rule 2 (lower (has_type (fits_in_64 _) (sextend _ (sinkable_load inst ty flags addr offset)))) (gen_sunk_load inst (amode addr offset) (sextend_load_op ty) flags)) (decl pure sextend_load_op (Type) LoadOP) @@ -1221,10 +1222,10 @@ ;;;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 _) (popcnt x))) +(rule 0 (lower (has_type (fits_in_64 _) (popcnt _ x))) (gen_popcnt (zext x))) -(rule 1 (lower (has_type $I128 (popcnt x))) +(rule 1 (lower (has_type $I128 (popcnt _ x))) (let ((x ValueRegs x) (low XReg (gen_popcnt (value_regs_get x 0))) @@ -1232,15 +1233,15 @@ (result XReg (rv_add low high))) (value_regs result (imm $I64 0)))) -(rule 2 (lower (has_type (fits_in_64 _) (popcnt x))) +(rule 2 (lower (has_type (fits_in_64 _) (popcnt _ x))) (if-let true (has_zbb)) (rv_cpop (zext x))) -(rule 3 (lower (has_type $I32 (popcnt x))) +(rule 3 (lower (has_type $I32 (popcnt _ x))) (if-let true (has_zbb)) (rv_cpopw x)) -(rule 3 (lower (has_type $I128 (popcnt x))) +(rule 3 (lower (has_type $I128 (popcnt _ x))) (if-let true (has_zbb)) (let ((x ValueRegs x) @@ -1261,7 +1262,7 @@ ;; ;; TODO: LLVM generates a much better implementation for I8X16. See: https://godbolt.org/z/qr6vf9Gr3 ;; For the other types it seems to be largely the same. -(rule 4 (lower (has_type (ty_supported_vec ty) (popcnt x))) +(rule 4 (lower (has_type (ty_supported_vec ty) (popcnt _ x))) (if-let one (u64_to_uimm5 1)) (if-let two (u64_to_uimm5 2)) (if-let four (u64_to_uimm5 4)) @@ -1296,34 +1297,34 @@ ;;;; Rules for `ishl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 8/16 bit types need a mask on the shift amount -(rule 0 (lower (has_type (ty_int (ty_8_or_16 ty)) (ishl x y))) +(rule 0 (lower (has_type (ty_int (ty_8_or_16 ty)) (ishl _ x y))) (if-let mask (u64_to_imm12 (ty_shift_mask ty))) (rv_sllw x (rv_andi (value_regs_get y 0) mask))) ;; Using the 32bit version of `sll` automatically masks the shift amount. -(rule 1 (lower (has_type $I32 (ishl x y))) +(rule 1 (lower (has_type $I32 (ishl _ x y))) (rv_sllw x (value_regs_get y 0))) ;; Similarly, the 64bit version does the right thing. -(rule 1 (lower (has_type $I64 (ishl x y))) +(rule 1 (lower (has_type $I64 (ishl _ x y))) (rv_sll x (value_regs_get y 0))) ;; If the shift amount is known. We can mask it and encode it in the instruction. -(rule 2 (lower (has_type (int_fits_in_32 ty) (ishl x (maybe_uextend (imm12_from_value y))))) +(rule 2 (lower (has_type (int_fits_in_32 ty) (ishl _ x (maybe_uextend (imm12_from_value y))))) (rv_slliw x (imm12_and y (ty_shift_mask ty)))) ;; We technically don't need to mask the shift amount here. The instruction ;; does the right thing. But it's neater when pretty printing it. -(rule 3 (lower (has_type ty @ $I64 (ishl x (maybe_uextend (imm12_from_value y))))) +(rule 3 (lower (has_type ty @ $I64 (ishl _ x (maybe_uextend (imm12_from_value y))))) (rv_slli x (imm12_and y (ty_shift_mask ty)))) ;; With `Zba` we have a shift that zero extends the LHS argument. -(rule 4 (lower (has_type $I64 (ishl (uextend x @ (value_type $I32)) (maybe_uextend (imm12_from_value y))))) +(rule 4 (lower (has_type $I64 (ishl _ (uextend _ x @ (value_type $I32)) (maybe_uextend (imm12_from_value y))))) (if-let true (has_zba)) (rv_slliuw x y)) ;; I128 cases -(rule 4 (lower (has_type $I128 (ishl x y))) +(rule 4 (lower (has_type $I128 (ishl _ x y))) (let ((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0))) (shamt XReg (value_regs_get tmp 0)) (len_sub_shamt XReg (value_regs_get tmp 1)) @@ -1346,39 +1347,39 @@ ;; SIMD Cases ;; We don't need to mask anything since it is done by the instruction according to SEW. -(rule 5 (lower (has_type (ty_supported_vec ty) (ishl x y))) +(rule 5 (lower (has_type (ty_supported_vec ty) (ishl _ x y))) (rv_vsll_vx x (value_regs_get y 0) (unmasked) ty)) -(rule 6 (lower (has_type (ty_supported_vec ty) (ishl x (maybe_uextend (uimm5_from_value y))))) +(rule 6 (lower (has_type (ty_supported_vec ty) (ishl _ x (maybe_uextend (uimm5_from_value y))))) (rv_vsll_vi x y (unmasked) ty)) ;;;; Rules for `ushr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 8/16 bit types need a mask on the shift amount, and the LHS needs to be ;; zero extended. -(rule 0 (lower (has_type (ty_int (fits_in_16 ty)) (ushr x y))) +(rule 0 (lower (has_type (ty_int (fits_in_16 ty)) (ushr _ x y))) (if-let mask (u64_to_imm12 (ty_shift_mask ty))) (rv_srlw (zext x) (rv_andi (value_regs_get y 0) mask))) ;; Using the 32bit version of `srl` automatically masks the shift amount. -(rule 1 (lower (has_type $I32 (ushr x y))) +(rule 1 (lower (has_type $I32 (ushr _ x y))) (rv_srlw x (value_regs_get y 0))) ;; Similarly, the 64bit version does the right thing. -(rule 1 (lower (has_type $I64 (ushr x y))) +(rule 1 (lower (has_type $I64 (ushr _ x y))) (rv_srl x (value_regs_get y 0))) ;; When the RHS is known we can just encode it in the instruction. -(rule 2 (lower (has_type (ty_int (fits_in_16 ty)) (ushr x (maybe_uextend (imm12_from_value y))))) +(rule 2 (lower (has_type (ty_int (fits_in_16 ty)) (ushr _ x (maybe_uextend (imm12_from_value y))))) (rv_srliw (zext x) (imm12_and y (ty_shift_mask ty)))) -(rule 3 (lower (has_type $I32 (ushr x (maybe_uextend (imm12_from_value y))))) +(rule 3 (lower (has_type $I32 (ushr _ x (maybe_uextend (imm12_from_value y))))) (rv_srliw x y)) -(rule 3 (lower (has_type $I64 (ushr x (maybe_uextend (imm12_from_value y))))) +(rule 3 (lower (has_type $I64 (ushr _ x (maybe_uextend (imm12_from_value y))))) (rv_srli x y)) -(rule 3 (lower (has_type $I128 (ushr x y))) +(rule 3 (lower (has_type $I128 (ushr _ x y))) (let ((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0))) (shamt XReg (value_regs_get tmp 0)) (len_sub_shamt XReg (value_regs_get tmp 1)) @@ -1401,39 +1402,39 @@ ;; SIMD Cases ;; We don't need to mask or extend anything since it is done by the instruction according to SEW. -(rule 4 (lower (has_type (ty_supported_vec ty) (ushr x y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (ushr _ x y))) (rv_vsrl_vx x (value_regs_get y 0) (unmasked) ty)) -(rule 5 (lower (has_type (ty_supported_vec ty) (ushr x (maybe_uextend (uimm5_from_value y))))) +(rule 5 (lower (has_type (ty_supported_vec ty) (ushr _ x (maybe_uextend (uimm5_from_value y))))) (rv_vsrl_vi x y (unmasked) ty)) ;;;; Rules for `sshr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 8/16 bit types need a mask on the shift amount, and the LHS needs to be ;; zero extended. -(rule 0 (lower (has_type (ty_int (fits_in_16 ty)) (sshr x y))) +(rule 0 (lower (has_type (ty_int (fits_in_16 ty)) (sshr _ x y))) (if-let mask (u64_to_imm12 (ty_shift_mask ty))) (rv_sraw (sext x) (rv_andi (value_regs_get y 0) mask))) ;; Using the 32bit version of `sra` automatically masks the shift amount. -(rule 1 (lower (has_type $I32 (sshr x y))) +(rule 1 (lower (has_type $I32 (sshr _ x y))) (rv_sraw x (value_regs_get y 0))) ;; Similarly, the 64bit version does the right thing. -(rule 1 (lower (has_type $I64 (sshr x y))) +(rule 1 (lower (has_type $I64 (sshr _ x y))) (rv_sra x (value_regs_get y 0))) ;; When the RHS is known we can just encode it in the instruction. -(rule 2 (lower (has_type (ty_int (fits_in_16 ty)) (sshr x (maybe_uextend (imm12_from_value y))))) +(rule 2 (lower (has_type (ty_int (fits_in_16 ty)) (sshr _ x (maybe_uextend (imm12_from_value y))))) (rv_sraiw (sext x) (imm12_and y (ty_shift_mask ty)))) -(rule 3 (lower (has_type $I32 (sshr x (maybe_uextend (imm12_from_value y))))) +(rule 3 (lower (has_type $I32 (sshr _ x (maybe_uextend (imm12_from_value y))))) (rv_sraiw x y)) -(rule 3 (lower (has_type $I64 (sshr x (maybe_uextend (imm12_from_value y))))) +(rule 3 (lower (has_type $I64 (sshr _ x (maybe_uextend (imm12_from_value y))))) (rv_srai x y)) -(rule 3 (lower (has_type $I128 (sshr x y))) +(rule 3 (lower (has_type $I128 (sshr _ x y))) (let ((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0))) (shamt XReg (value_regs_get tmp 0)) (len_sub_shamt XReg (value_regs_get tmp 1)) @@ -1461,16 +1462,16 @@ ;; SIMD Cases ;; We don't need to mask or extend anything since it is done by the instruction according to SEW. -(rule 4 (lower (has_type (ty_supported_vec ty) (sshr x y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (sshr _ x y))) (rv_vsra_vx x (value_regs_get y 0) (unmasked) ty)) -(rule 5 (lower (has_type (ty_supported_vec ty) (sshr x (maybe_uextend (uimm5_from_value y))))) +(rule 5 (lower (has_type (ty_supported_vec ty) (sshr _ x (maybe_uextend (uimm5_from_value y))))) (rv_vsra_vi x y (unmasked) ty)) ;;;; Rules for `rotl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 ty) (rotl rs amount))) +(rule 0 (lower (has_type (fits_in_64 ty) (rotl _ rs amount))) (let ((rs XReg (zext rs)) (amount XReg (value_regs_get amount 0)) @@ -1482,25 +1483,25 @@ (part3 Reg (gen_select_xreg (cmp_eqz shamt) (zero_reg) part2))) (rv_or part1 part3))) -(rule 1 (lower (has_type $I32 (rotl rs amount))) +(rule 1 (lower (has_type $I32 (rotl _ rs amount))) (if-let true (has_zbb)) (rv_rolw rs (value_regs_get amount 0))) -(rule 2 (lower (has_type $I32 (rotl rs (u64_from_iconst n)))) +(rule 2 (lower (has_type $I32 (rotl _ rs (u64_from_iconst n)))) (if-let true (has_zbb)) (if-let (imm12_from_u64 imm) (u64_wrapping_sub 32 (u64_and n 31))) (rv_roriw rs imm)) -(rule 1 (lower (has_type $I64 (rotl rs amount))) +(rule 1 (lower (has_type $I64 (rotl _ rs amount))) (if-let true (has_zbb)) (rv_rol rs (value_regs_get amount 0))) -(rule 2 (lower (has_type $I64 (rotl rs (u64_from_iconst n)))) +(rule 2 (lower (has_type $I64 (rotl _ rs (u64_from_iconst n)))) (if-let true (has_zbb)) (if-let (imm12_from_u64 imm) (u64_wrapping_sub 64 (u64_and n 63))) (rv_rori rs imm)) -(rule 1 (lower (has_type $I128 (rotl x y))) +(rule 1 (lower (has_type $I128 (rotl _ x y))) (let ((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0))) (shamt XReg (value_regs_get tmp 0)) @@ -1526,7 +1527,7 @@ ;;;; Rules for `rotr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_64 ty) (rotr rs amount))) +(rule (lower (has_type (fits_in_64 ty) (rotr _ rs amount))) (let ((rs XReg (zext rs)) (amount XReg (value_regs_get amount 0)) @@ -1538,23 +1539,23 @@ (part3 XReg (gen_select_xreg (cmp_eqz shamt) (zero_reg) part2))) (rv_or part1 part3))) -(rule 1 (lower (has_type $I32 (rotr rs amount))) +(rule 1 (lower (has_type $I32 (rotr _ rs amount))) (if-let true (has_zbb)) (rv_rorw rs (value_regs_get amount 0))) -(rule 2 (lower (has_type $I32 (rotr rs (imm12_from_value n)))) +(rule 2 (lower (has_type $I32 (rotr _ rs (imm12_from_value n)))) (if-let true (has_zbb)) (rv_roriw rs n)) -(rule 1 (lower (has_type $I64 (rotr rs amount))) +(rule 1 (lower (has_type $I64 (rotr _ rs amount))) (if-let true (has_zbb)) (rv_ror rs (value_regs_get amount 0))) -(rule 2 (lower (has_type $I64 (rotr rs (imm12_from_value n)))) +(rule 2 (lower (has_type $I64 (rotr _ rs (imm12_from_value n)))) (if-let true (has_zbb)) (rv_rori rs n)) -(rule 1 (lower (has_type $I128 (rotr x y))) +(rule 1 (lower (has_type $I128 (rotr _ x y))) (let ((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0))) (shamt XReg (value_regs_get tmp 0)) @@ -1579,27 +1580,27 @@ ))) ;;;; Rules for `fabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (fabs x))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (fabs _ x))) (rv_fabs ty x)) -(rule 1 (lower (has_type (ty_supported_vec ty) (fabs x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (fabs _ x))) (rv_vfabs_v x (unmasked) ty)) ;;;; Rules for `fneg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (fneg x))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (fneg _ x))) (rv_fneg ty x)) -(rule 1 (lower (has_type (ty_supported_vec ty) (fneg x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (fneg _ x))) (rv_vfneg_v x (unmasked) ty)) ;;;; Rules for `fcopysign` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (fcopysign x y))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (fcopysign _ x y))) (rv_fsgnj ty x y)) -(rule 1 (lower (has_type (ty_supported_vec ty) (fcopysign x y))) +(rule 1 (lower (has_type (ty_supported_vec ty) (fcopysign _ x y))) (rv_vfsgnj_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (fcopysign x (splat y)))) +(rule 2 (lower (has_type (ty_supported_vec ty) (fcopysign _ x (splat _ y)))) (rv_vfsgnj_vf x y (unmasked) ty)) ;;;; Rules for `fma` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1626,7 +1627,7 @@ (type IsFneg (enum (Result (negate u64) (value Value)))) (decl pure is_fneg (Value) IsFneg) -(rule 1 (is_fneg (fneg x)) (IsFneg.Result 1 x)) +(rule 1 (is_fneg (fneg _ x)) (IsFneg.Result 1 x)) (rule 0 (is_fneg x) (IsFneg.Result 0 x)) (decl pure is_fneg_neg (IsFneg) u64) @@ -1635,7 +1636,7 @@ (decl pure get_fneg_value (IsFneg) Value) (rule (get_fneg_value (IsFneg.Result _ v)) v) -(rule (lower (has_type ty (fma x_src y_src z_src))) +(rule (lower (has_type ty (fma _ x_src y_src z_src))) (let ((x_res IsFneg (is_fneg x_src)) (y_res IsFneg (is_fneg y_src)) @@ -1655,54 +1656,54 @@ (rule 1 (rv_fma (ty_supported_vec ty) 0 1 x y z) (rv_vfmsac_vv z y x (unmasked) ty)) (rule 1 (rv_fma (ty_supported_vec ty) 1 0 x y z) (rv_vfnmsac_vv z y x (unmasked) ty)) (rule 1 (rv_fma (ty_supported_vec ty) 1 1 x y z) (rv_vfnmacc_vv z y x (unmasked) ty)) -(rule 2 (rv_fma (ty_supported_vec ty) 0 0 (splat x) y z) (rv_vfmacc_vf z y x (unmasked) ty)) -(rule 2 (rv_fma (ty_supported_vec ty) 0 1 (splat x) y z) (rv_vfmsac_vf z y x (unmasked) ty)) -(rule 2 (rv_fma (ty_supported_vec ty) 1 0 (splat x) y z) (rv_vfnmsac_vf z y x (unmasked) ty)) -(rule 2 (rv_fma (ty_supported_vec ty) 1 1 (splat x) y z) (rv_vfnmacc_vf z y x (unmasked) ty)) -(rule 3 (rv_fma (ty_supported_vec ty) 0 0 x (splat y) z) (rv_vfmacc_vf z x y (unmasked) ty)) -(rule 3 (rv_fma (ty_supported_vec ty) 0 1 x (splat y) z) (rv_vfmsac_vf z x y (unmasked) ty)) -(rule 3 (rv_fma (ty_supported_vec ty) 1 0 x (splat y) z) (rv_vfnmsac_vf z x y (unmasked) ty)) -(rule 3 (rv_fma (ty_supported_vec ty) 1 1 x (splat y) z) (rv_vfnmacc_vf z x y (unmasked) ty)) +(rule 2 (rv_fma (ty_supported_vec ty) 0 0 (splat _ x) y z) (rv_vfmacc_vf z y x (unmasked) ty)) +(rule 2 (rv_fma (ty_supported_vec ty) 0 1 (splat _ x) y z) (rv_vfmsac_vf z y x (unmasked) ty)) +(rule 2 (rv_fma (ty_supported_vec ty) 1 0 (splat _ x) y z) (rv_vfnmsac_vf z y x (unmasked) ty)) +(rule 2 (rv_fma (ty_supported_vec ty) 1 1 (splat _ x) y z) (rv_vfnmacc_vf z y x (unmasked) ty)) +(rule 3 (rv_fma (ty_supported_vec ty) 0 0 x (splat _ y) z) (rv_vfmacc_vf z x y (unmasked) ty)) +(rule 3 (rv_fma (ty_supported_vec ty) 0 1 x (splat _ y) z) (rv_vfmsac_vf z x y (unmasked) ty)) +(rule 3 (rv_fma (ty_supported_vec ty) 1 0 x (splat _ y) z) (rv_vfnmsac_vf z x y (unmasked) ty)) +(rule 3 (rv_fma (ty_supported_vec ty) 1 1 x (splat _ y) z) (rv_vfnmacc_vf z x y (unmasked) ty)) ;;;; Rules for `sqrt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (sqrt x))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (sqrt _ x))) (rv_fsqrt ty (FRM.RNE) x)) -(rule 1 (lower (has_type (ty_supported_vec ty) (sqrt x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (sqrt _ x))) (rv_vfsqrt_v x (unmasked) ty)) ;;;; Rules for `AtomicRMW` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule -1 ;; (lower - (has_type (valid_atomic_transaction ty) (atomic_rmw (little_or_native_endian flags) op addr x))) + (has_type (valid_atomic_transaction ty) (atomic_rmw _ (little_or_native_endian flags) op addr x))) (gen_atomic (get_atomic_rmw_op ty op) addr x (atomic_amo))) ;;; for I8 and I16 (rule 1 (lower - (has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw (little_or_native_endian flags) op addr x))) + (has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw _ (little_or_native_endian flags) op addr x))) (gen_atomic_rmw_loop op ty addr x)) ;;;special for I8 and I16 max min etc. ;;;because I need uextend or sextend the value. (rule 2 (lower - (has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw (little_or_native_endian flags) (is_atomic_rmw_max_etc op true) addr x))) + (has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw _ (little_or_native_endian flags) (is_atomic_rmw_max_etc op true) addr x))) (gen_atomic_rmw_loop op ty addr (sext x))) (rule 2 ;; (lower - (has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw (little_or_native_endian flags) (is_atomic_rmw_max_etc op false) addr x))) + (has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw _ (little_or_native_endian flags) (is_atomic_rmw_max_etc op false) addr x))) ;; (gen_atomic_rmw_loop op ty addr (zext x))) ;;;;; Rules for `AtomicRmwOp.Sub` (rule (lower - (has_type (valid_atomic_transaction ty) (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Sub) addr x))) + (has_type (valid_atomic_transaction ty) (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Sub) addr x))) (let ((tmp WritableReg (temp_writable_reg ty)) (x2 Reg (rv_neg x))) @@ -1720,7 +1721,7 @@ ;;;;; Rules for `AtomicRmwOp.Nand` (rule (lower - (has_type (valid_atomic_transaction ty) (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Nand) addr x))) + (has_type (valid_atomic_transaction ty) (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Nand) addr x))) (gen_atomic_rmw_loop (AtomicRmwOp.Nand) ty addr x)) (decl is_atomic_rmw_max_etc (AtomicRmwOp bool) AtomicRmwOp) @@ -1728,7 +1729,7 @@ ;;;;; Rules for `atomic load`;;;;;;;;;;;;;;;;; (rule - (lower (has_type (valid_atomic_transaction ty) (atomic_load (little_or_native_endian flags) p))) + (lower (has_type (valid_atomic_transaction ty) (atomic_load _ (little_or_native_endian flags) p))) (gen_atomic_load p ty)) @@ -1754,7 +1755,7 @@ ;;;;; Rules for `atomic cas`;;;;;;;;;;;;;;;;; (rule - (lower (has_type (valid_atomic_transaction ty) (atomic_cas (little_or_native_endian flags) p e x))) + (lower (has_type (valid_atomic_transaction ty) (atomic_cas _ (little_or_native_endian flags) p e x))) (let ((t0 WritableReg (temp_writable_reg ty)) (dst WritableReg (temp_writable_reg ty)) @@ -1763,28 +1764,28 @@ ;;;;; Rules for `ireduce`;;;;;;;;;;;;;;;;; (rule - (lower (has_type ty (ireduce x))) + (lower (has_type ty (ireduce _ x))) (value_regs_get x 0)) ;;;;; Rules for `fpromote`;;;;;;;;;;;;;;;;; -(rule (lower (fpromote x)) +(rule (lower (fpromote _ x)) (rv_fcvtds x)) ;;;;; Rules for `fvpromote_low`;;;;;;;;;;;; -(rule (lower (has_type (ty_supported_vec ty) (fvpromote_low x))) +(rule (lower (has_type (ty_supported_vec ty) (fvpromote_low _ x))) (if-let half_ty (ty_half_width ty)) (rv_vfwcvt_f_f_v x (unmasked) (vstate_mf2 half_ty))) ;;;;; Rules for `fdemote`;;;;;;;;;;;;;;;;;; -(rule (lower (fdemote x)) +(rule (lower (fdemote _ x)) (rv_fcvtsd (FRM.RNE) x)) ;;;;; Rules for `fvdemote`;;;;;;;;;;;;;;;;; ;; `vfncvt...` leaves the upper bits of the register undefined so ;; we need to zero them out. -(rule (lower (has_type (ty_supported_vec ty @ $F32X4) (fvdemote x))) +(rule (lower (has_type (ty_supported_vec ty @ $F32X4) (fvdemote _ x))) (if-let zero (i8_to_imm5 0)) (let ((narrow VReg (rv_vfncvt_f_f_w x (unmasked) (vstate_mf2 ty))) (mask VReg (gen_vec_mask 0xC))) @@ -1796,57 +1797,57 @@ ;;;; Rules for `fadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (fadd x y))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (fadd _ x y))) (rv_fadd ty (FRM.RNE) x y)) -(rule 1 (lower (has_type (ty_supported_vec ty) (fadd x y))) +(rule 1 (lower (has_type (ty_supported_vec ty) (fadd _ x y))) (rv_vfadd_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (fadd x (splat y)))) +(rule 2 (lower (has_type (ty_supported_vec ty) (fadd _ x (splat _ y)))) (rv_vfadd_vf x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (fadd (splat x) y))) +(rule 3 (lower (has_type (ty_supported_vec ty) (fadd _ (splat _ x) y))) (rv_vfadd_vf y x (unmasked) ty)) ;;;; Rules for `fsub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (fsub x y))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (fsub _ x y))) (rv_fsub ty (FRM.RNE) x y)) -(rule 1 (lower (has_type (ty_supported_vec ty) (fsub x y))) +(rule 1 (lower (has_type (ty_supported_vec ty) (fsub _ x y))) (rv_vfsub_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (fsub x (splat y)))) +(rule 2 (lower (has_type (ty_supported_vec ty) (fsub _ x (splat _ y)))) (rv_vfsub_vf x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (fsub (splat x) y))) +(rule 3 (lower (has_type (ty_supported_vec ty) (fsub _ (splat _ x) y))) (rv_vfrsub_vf y x (unmasked) ty)) ;;;; Rules for `fmul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (fmul x y))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (fmul _ x y))) (rv_fmul ty (FRM.RNE) x y)) -(rule 1 (lower (has_type (ty_supported_vec ty) (fmul x y))) +(rule 1 (lower (has_type (ty_supported_vec ty) (fmul _ x y))) (rv_vfmul_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (fmul x (splat y)))) +(rule 2 (lower (has_type (ty_supported_vec ty) (fmul _ x (splat _ y)))) (rv_vfmul_vf x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (fmul (splat x) y))) +(rule 3 (lower (has_type (ty_supported_vec ty) (fmul _ (splat _ x) y))) (rv_vfmul_vf y x (unmasked) ty)) ;;;; Rules for `fdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (fdiv x y))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (fdiv _ x y))) (rv_fdiv ty (FRM.RNE) x y)) -(rule 1 (lower (has_type (ty_supported_vec ty) (fdiv x y))) +(rule 1 (lower (has_type (ty_supported_vec ty) (fdiv _ x y))) (rv_vfdiv_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (fdiv x (splat y)))) +(rule 2 (lower (has_type (ty_supported_vec ty) (fdiv _ x (splat _ y)))) (rv_vfdiv_vf x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (fdiv (splat x) y))) +(rule 3 (lower (has_type (ty_supported_vec ty) (fdiv _ (splat _ x) y))) (rv_vfrdiv_vf y x (unmasked) ty)) ;;;; Rules for `fmin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1854,7 +1855,7 @@ ;; RISC-V's `fmin` instruction returns the number input if one of inputs is a ;; NaN. We handle this by manually checking if one of the inputs is a NaN ;; and selecting based on that result. -(rule 0 (lower (has_type (ty_supported_float_full ty) (fmin x y))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (fmin _ x y))) (let ( ;; Check if both inputs are not nan. (is_ordered FloatCompare (fcmp_to_float_compare (FloatCC.Ordered) ty x y)) @@ -1865,7 +1866,7 @@ ;; With Zfa we can use the special `fminm` that precisely matches the expected ;; NaN behavior. -(rule 1 (lower (has_type (ty_supported_float_full ty) (fmin x y))) +(rule 1 (lower (has_type (ty_supported_float_full ty) (fmin _ x y))) (if-let true (has_zfa)) (rv_fminm ty x y)) @@ -1875,7 +1876,7 @@ ;; ;; TODO: We can improve this by using a masked `fmin` instruction that modifies ;; the canonical nan register. That way we could avoid the `vmerge.vv` instruction. -(rule 2 (lower (has_type (ty_supported_vec ty) (fmin x y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (fmin _ x y))) (let ((is_not_nan VReg (gen_fcmp_mask ty (FloatCC.Ordered) x y)) (nan XReg (imm $I64 (canonical_nan_u64 (lane_type ty)))) (vec_nan VReg (rv_vmv_vx nan ty)) @@ -1887,7 +1888,7 @@ ;; RISC-V's `fmax` instruction returns the number input if one of inputs is a ;; NaN. We handle this by manually checking if one of the inputs is a NaN ;; and selecting based on that result. -(rule 0 (lower (has_type (ty_supported_float_full ty) (fmax x y))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (fmax _ x y))) (let ( ;; Check if both inputs are not nan. (is_ordered FloatCompare (fcmp_to_float_compare (FloatCC.Ordered) ty x y)) @@ -1898,7 +1899,7 @@ ;; With Zfa we can use the special `fmaxm` that precisely matches the expected ;; NaN behavior. -(rule 1 (lower (has_type (ty_supported_float_full ty) (fmax x y))) +(rule 1 (lower (has_type (ty_supported_float_full ty) (fmax _ x y))) (if-let true (has_zfa)) (rv_fmaxm ty x y)) @@ -1908,7 +1909,7 @@ ;; ;; TODO: We can improve this by using a masked `fmax` instruction that modifies ;; the canonical nan register. That way we could avoid the `vmerge.vv` instruction. -(rule 2 (lower (has_type (ty_supported_vec ty) (fmax x y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (fmax _ x y))) (let ((is_not_nan VReg (gen_fcmp_mask ty (FloatCC.Ordered) x y)) (nan XReg (imm $I64 (canonical_nan_u64 (lane_type ty)))) (vec_nan VReg (rv_vmv_vx nan ty)) @@ -1917,7 +1918,7 @@ ;;;;; Rules for `stack_addr`;;;;;;;;; (rule - (lower (stack_addr ss offset)) + (lower (stack_addr _ ss offset)) (gen_stack_addr ss offset)) ;;;;; Rules for `select`;;;;;;;;; @@ -1926,28 +1927,28 @@ ;; of the iconst rule because that runs into regalloc issues. gen_select_xreg ;; has some optimizations based on the use of the zero register so we have to ;; manually match it here. -(rule 5 (lower (has_type (ty_int_ref_scalar_64 _) (select c (i64_from_iconst 0) y))) +(rule 5 (lower (has_type (ty_int_ref_scalar_64 _) (select _ c (i64_from_iconst 0) y))) (gen_select_xreg (is_nonzero_cmp c) (zero_reg) y)) -(rule 4 (lower (has_type (ty_int_ref_scalar_64 _) (select c x (i64_from_iconst 0)))) +(rule 4 (lower (has_type (ty_int_ref_scalar_64 _) (select _ c x (i64_from_iconst 0)))) (gen_select_xreg (is_nonzero_cmp c) x (zero_reg))) -(rule 3 (lower (has_type (ty_int_ref_scalar_64 _) (select c x y))) +(rule 3 (lower (has_type (ty_int_ref_scalar_64 _) (select _ c x y))) (gen_select_xreg (is_nonzero_cmp c) x y)) -(rule 2 (lower (has_type (ty_reg_pair _) (select c x y))) +(rule 2 (lower (has_type (ty_reg_pair _) (select _ c x y))) (gen_select_regs (is_nonzero_cmp c) x y)) -(rule 1 (lower (has_type (ty_supported_vec _) (select c x y))) +(rule 1 (lower (has_type (ty_supported_vec _) (select _ c x y))) (gen_select_vreg (is_nonzero_cmp c) x y)) -(rule 0 (lower (has_type (ty_supported_float_size _) (select c x y))) +(rule 0 (lower (has_type (ty_supported_float_size _) (select _ c x y))) (gen_select_freg (is_nonzero_cmp c) x y)) ;;;;; Rules for `bitselect`;;;;;;;;; ;; Do a (c & x) | (~c & y) operation. -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (bitselect c x y))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (bitselect _ c x y))) (let ((tmp_x XReg (rv_and c x)) (c_inverse XReg (rv_not c)) (tmp_y XReg (rv_and c_inverse y))) @@ -1958,7 +1959,7 @@ ;; using the type of the inputs so that we avoid emitting unnecessary ;; `vsetvl` instructions. it's likely that the vector unit is already ;; configured for that type. -(rule 1 (lower (has_type (ty_supported_vec ty) (bitselect c x y))) +(rule 1 (lower (has_type (ty_supported_vec ty) (bitselect _ c x y))) (let ((tmp_x VReg (rv_vand_vv c x (unmasked) ty)) (c_inverse VReg (rv_vnot_v c (unmasked) ty)) (tmp_y VReg (rv_vand_vv c_inverse y (unmasked) ty))) @@ -1976,26 +1977,26 @@ ;; ;; See: https://github.com/bytecodealliance/wasmtime/issues/8131 -(rule 2 (lower (has_type (ty_supported_vec _ty) (bitselect (icmp cc a @ (value_type (ty_supported_vec cmp_ty)) b) x y))) +(rule 2 (lower (has_type (ty_supported_vec _ty) (bitselect _ (icmp _ cc a @ (value_type (ty_supported_vec cmp_ty)) b) x y))) (let ((mask VReg (gen_icmp_mask cmp_ty cc a b))) (rv_vmerge_vvm y x mask cmp_ty))) -(rule 2 (lower (has_type (ty_supported_vec _ty) (bitselect (fcmp cc a @ (value_type (ty_supported_vec cmp_ty)) b) x y))) +(rule 2 (lower (has_type (ty_supported_vec _ty) (bitselect _ (fcmp _ cc a @ (value_type (ty_supported_vec cmp_ty)) b) x y))) (let ((mask VReg (gen_fcmp_mask cmp_ty cc a b))) (rv_vmerge_vvm y x mask cmp_ty))) -(rule 2 (lower (has_type (ty_supported_vec _ty) (bitselect (bitcast _ (fcmp cc a @ (value_type (ty_supported_vec cmp_ty)) b)) x y))) +(rule 2 (lower (has_type (ty_supported_vec _ty) (bitselect _ (bitcast _ _ (fcmp _ cc a @ (value_type (ty_supported_vec cmp_ty)) b)) x y))) (let ((mask VReg (gen_fcmp_mask cmp_ty cc a b))) (rv_vmerge_vvm y x mask cmp_ty))) -(rule 2 (lower (has_type (ty_supported_vec _ty) (bitselect (bitcast _ (icmp cc a @ (value_type (ty_supported_vec cmp_ty)) b)) x y))) +(rule 2 (lower (has_type (ty_supported_vec _ty) (bitselect _ (bitcast _ _ (icmp _ cc a @ (value_type (ty_supported_vec cmp_ty)) b)) x y))) (let ((mask VReg (gen_icmp_mask cmp_ty cc a b))) (rv_vmerge_vvm y x mask cmp_ty))) ;;;;; Rules for `isplit`;;;;;;;;; (rule - (lower (isplit x)) + (lower (isplit _ x)) (let ((t1 XReg (value_regs_get x 0)) (t2 XReg (value_regs_get x 1))) @@ -2003,7 +2004,7 @@ ;;;;; Rules for `iconcat`;;;;;;;;; (rule - (lower (has_type $I128 (iconcat x y))) + (lower (has_type $I128 (iconcat _ x y))) (let ((t1 XReg x) (t2 XReg y)) @@ -2012,90 +2013,90 @@ ;; Special-case the lowering of an `isplit` of a 128-bit multiply where the ;; lower bits of the result are discarded and the operands are sign or zero ;; extended. This maps directly to `umulh` and `smulh`. -(rule 1 (lower i @ (isplit (has_type $I128 (imul (uextend x) (uextend y))))) +(rule 1 (lower i @ (isplit _ (has_type $I128 (imul _ (uextend _ x) (uextend _ y))))) (if-let (first_result lo) i) (if-let true (value_is_unused lo)) (output_pair (invalid_reg) (rv_mulhu (zext x) (zext y)))) -(rule 1 (lower i @ (isplit (has_type $I128 (imul (sextend x) (sextend y))))) +(rule 1 (lower i @ (isplit _ (has_type $I128 (imul _ (sextend _ x) (sextend _ y))))) (if-let (first_result lo) i) (if-let true (value_is_unused lo)) (output_pair (invalid_reg) (rv_mulh (sext x) (sext y)))) ;;;;; Rules for `smax`;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 ty) (smax x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (smax _ x y))) (let ((x XReg (sext x)) (y XReg (sext y))) (gen_select_xreg (cmp_gt x y) x y))) -(rule 1 (lower (has_type $I128 (smax x y))) +(rule 1 (lower (has_type $I128 (smax _ x y))) (gen_select_regs (icmp_to_int_compare (IntCC.SignedGreaterThan) x y) x y)) -(rule 2 (lower (has_type (ty_supported_vec ty) (smax x y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (smax _ x y))) (rv_vmax_vv x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (smax x (splat y)))) +(rule 3 (lower (has_type (ty_supported_vec ty) (smax _ x (splat _ y)))) (rv_vmax_vx x y (unmasked) ty)) -(rule 4 (lower (has_type (ty_supported_vec ty) (smax (splat x) y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (smax _ (splat _ x) y))) (rv_vmax_vx y x (unmasked) ty)) ;;;;; Rules for `smin`;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 ty) (smin x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (smin _ x y))) (let ((x XReg (sext x)) (y XReg (sext y))) (gen_select_xreg (cmp_lt x y) x y))) -(rule 1 (lower (has_type $I128 (smin x y))) +(rule 1 (lower (has_type $I128 (smin _ x y))) (gen_select_regs (icmp_to_int_compare (IntCC.SignedLessThan) x y) x y)) -(rule 2 (lower (has_type (ty_supported_vec ty) (smin x y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (smin _ x y))) (rv_vmin_vv x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (smin x (splat y)))) +(rule 3 (lower (has_type (ty_supported_vec ty) (smin _ x (splat _ y)))) (rv_vmin_vx x y (unmasked) ty)) -(rule 4 (lower (has_type (ty_supported_vec ty) (smin (splat x) y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (smin _ (splat _ x) y))) (rv_vmin_vx y x (unmasked) ty)) ;;;;; Rules for `umax`;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 ty) (umax x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (umax _ x y))) (let ((x XReg (zext x)) (y XReg (zext y))) (gen_select_xreg (cmp_gtu x y) x y))) -(rule 1 (lower (has_type $I128 (umax x y))) +(rule 1 (lower (has_type $I128 (umax _ x y))) (gen_select_regs (icmp_to_int_compare (IntCC.UnsignedGreaterThan) x y) x y)) -(rule 2 (lower (has_type (ty_supported_vec ty) (umax x y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (umax _ x y))) (rv_vmaxu_vv x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (umax x (splat y)))) +(rule 3 (lower (has_type (ty_supported_vec ty) (umax _ x (splat _ y)))) (rv_vmaxu_vx x y (unmasked) ty)) -(rule 4 (lower (has_type (ty_supported_vec ty) (umax (splat x) y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (umax _ (splat _ x) y))) (rv_vmaxu_vx y x (unmasked) ty)) ;;;;; Rules for `umin`;;;;;;;;; -(rule 0 (lower (has_type (fits_in_64 ty) (umin x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (umin _ x y))) (let ((x XReg (zext x)) (y XReg (zext y))) (gen_select_xreg (cmp_ltu x y) x y))) -(rule 1 (lower (has_type $I128 (umin x y))) +(rule 1 (lower (has_type $I128 (umin _ x y))) (gen_select_regs (icmp_to_int_compare (IntCC.UnsignedLessThan) x y) x y)) -(rule 2 (lower (has_type (ty_supported_vec ty) (umin x y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (umin _ x y))) (rv_vminu_vv x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (umin x (splat y)))) +(rule 3 (lower (has_type (ty_supported_vec ty) (umin _ x (splat _ y)))) (rv_vminu_vx x y (unmasked) ty)) -(rule 4 (lower (has_type (ty_supported_vec ty) (umin (splat x) y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (umin _ (splat _ x) y))) (rv_vminu_vx y x (unmasked) ty)) @@ -2124,8 +2125,11 @@ (gen_trapif_val_i128 (ZeroCond.Zero) value code)) ; fold icmp + trapz -(rule 2 (lower (trapz (icmp cc x @ (value_type (fits_in_64 _)) y) code)) - (gen_trapif (intcc_complement cc) x y code)) +(rule 2 (lower (trapz (icmp _ cc x @ (value_type (fits_in_64 _)) y) code)) + (gen_trapif (intcc_complement cc) + (put_value_in_reg_for_icmp cc x) + (put_value_in_reg_for_icmp cc y) + code)) ;;;;; Rules for `trapnz`;;;;;;;;; (rule @@ -2137,44 +2141,47 @@ (gen_trapif_val_i128 (ZeroCond.NonZero) value code)) ; fold icmp + trapnz -(rule 2 (lower (trapnz (icmp cc x @ (value_type (fits_in_64 _)) y) code)) - (gen_trapif cc x y code)) +(rule 2 (lower (trapnz (icmp _ cc x @ (value_type (fits_in_64 _)) y) code)) + (gen_trapif cc + (put_value_in_reg_for_icmp cc x) + (put_value_in_reg_for_icmp cc y) + code)) ;;;;; Rules for `uload8`;;;;;;;;; -(rule (lower (uload8 (little_or_native_endian flags) addr offset)) +(rule (lower (uload8 _ (little_or_native_endian flags) addr offset)) (gen_load (amode addr offset) (LoadOP.Lbu) flags)) ;;;;; Rules for `sload8`;;;;;;;;; -(rule (lower (sload8 (little_or_native_endian flags) addr offset)) +(rule (lower (sload8 _ (little_or_native_endian flags) addr offset)) (gen_load (amode addr offset) (LoadOP.Lb) flags)) ;;;;; Rules for `uload16`;;;;;;;;; -(rule (lower (uload16 (little_or_native_endian flags) addr offset)) +(rule (lower (uload16 _ (little_or_native_endian flags) addr offset)) (gen_load (amode addr offset) (LoadOP.Lhu) flags)) ;;;;; Rules for `iload16`;;;;;;;;; -(rule (lower (sload16 (little_or_native_endian flags) addr offset)) +(rule (lower (sload16 _ (little_or_native_endian flags) addr offset)) (gen_load (amode addr offset) (LoadOP.Lh) flags)) ;;;;; Rules for `uload32`;;;;;;;;; -(rule (lower (uload32 (little_or_native_endian flags) addr offset)) +(rule (lower (uload32 _ (little_or_native_endian flags) addr offset)) (gen_load (amode addr offset) (LoadOP.Lwu) flags)) ;;;;; Rules for `sload32`;;;;;;;;; -(rule (lower (sload32 (little_or_native_endian flags) addr offset)) +(rule (lower (sload32 _ (little_or_native_endian flags) addr offset)) (gen_load (amode addr offset) (LoadOP.Lw) flags)) ;;;;; Rules for `load`;;;;;;;;; -(rule (lower (has_type ty (load (little_or_native_endian flags) addr offset))) +(rule (lower (has_type ty (load _ (little_or_native_endian flags) addr offset))) (gen_load (amode addr offset) (load_op ty) flags)) -(rule 1 (lower (has_type (ty_reg_pair _) (load (little_or_native_endian flags) addr offset))) +(rule 1 (lower (has_type (ty_reg_pair _) (load _ (little_or_native_endian flags) addr offset))) (if-let offset_plus_8 (i32_checked_add offset 8)) (let ((lo XReg (gen_load (amode addr offset) (LoadOP.Ld) flags)) (hi XReg (gen_load (amode addr offset_plus_8) (LoadOP.Ld) flags))) (value_regs lo hi))) -(rule 2 (lower (has_type (ty_supported_vec ty) (load (little_or_native_endian flags) addr offset))) +(rule 2 (lower (has_type (ty_supported_vec ty) (load _ (little_or_native_endian flags) addr offset))) (let ((eew VecElementWidth (element_width_from_type ty)) (amode AMode (amode addr offset))) (vec_load eew (VecAMode.UnitStride amode) flags (unmasked) ty))) @@ -2201,27 +2208,27 @@ (rv_vzext_vf2 loaded (unmasked) ty))) ;;;;; Rules for `uload8x8`;;;;;;;;;; -(rule (lower (has_type (ty_supported_vec ty @ $I16X8) (uload8x8 (little_or_native_endian flags) addr offset))) +(rule (lower (has_type (ty_supported_vec ty @ $I16X8) (uload8x8 _ (little_or_native_endian flags) addr offset))) (gen_load64_extend ty (ExtendOp.Zero) flags (amode addr offset))) ;;;;; Rules for `uload16x4`;;;;;;;;; -(rule (lower (has_type (ty_supported_vec ty @ $I32X4) (uload16x4 (little_or_native_endian flags) addr offset))) +(rule (lower (has_type (ty_supported_vec ty @ $I32X4) (uload16x4 _ (little_or_native_endian flags) addr offset))) (gen_load64_extend ty (ExtendOp.Zero) flags (amode addr offset))) ;;;;; Rules for `uload32x2`;;;;;;;;; -(rule (lower (has_type (ty_supported_vec ty @ $I64X2) (uload32x2 (little_or_native_endian flags) addr offset))) +(rule (lower (has_type (ty_supported_vec ty @ $I64X2) (uload32x2 _ (little_or_native_endian flags) addr offset))) (gen_load64_extend ty (ExtendOp.Zero) flags (amode addr offset))) ;;;;; Rules for `sload8x8`;;;;;;;;;; -(rule (lower (has_type (ty_supported_vec ty @ $I16X8) (sload8x8 (little_or_native_endian flags) addr offset))) +(rule (lower (has_type (ty_supported_vec ty @ $I16X8) (sload8x8 _ (little_or_native_endian flags) addr offset))) (gen_load64_extend ty (ExtendOp.Signed) flags (amode addr offset))) ;;;;; Rules for `sload16x4`;;;;;;;;; -(rule (lower (has_type (ty_supported_vec ty @ $I32X4) (sload16x4 (little_or_native_endian flags) addr offset))) +(rule (lower (has_type (ty_supported_vec ty @ $I32X4) (sload16x4 _ (little_or_native_endian flags) addr offset))) (gen_load64_extend ty (ExtendOp.Signed) flags (amode addr offset))) ;;;;; Rules for `sload32x2`;;;;;;;;; -(rule (lower (has_type (ty_supported_vec ty @ $I64X2) (sload32x2 (little_or_native_endian flags) addr offset))) +(rule (lower (has_type (ty_supported_vec ty @ $I64X2) (sload32x2 _ (little_or_native_endian flags) addr offset))) (gen_load64_extend ty (ExtendOp.Signed) flags (amode addr offset))) ;;;;; Rules for `istore8`;;;;;;;;; @@ -2264,7 +2271,7 @@ ;; materializing that, but before that happens try to match some ;; constant-related patterns -(rule 0 (lower (icmp cc x @ (value_type (fits_in_64 ty)) y)) +(rule 0 (lower (icmp _ cc x @ (value_type (fits_in_64 ty)) y)) (lower_icmp cc x y)) ; Recursion: at most once to implement >= in terms of <. @@ -2354,7 +2361,7 @@ ;; Currently only `==`, `!=`, and `<` are implemented, and everything else ;; delegates to one of those. -(rule 20 (lower (icmp cc x @ (value_type $I128) y)) +(rule 20 (lower (icmp _ cc x @ (value_type $I128) y)) (lower_icmp_i128 cc x y)) ; Recursion: at most once to implement some conditions in terms of a smaller primitive set. @@ -2400,23 +2407,23 @@ ;; vector icmp comparisons -(rule 30 (lower (icmp cc x @ (value_type (ty_supported_vec ty)) y)) +(rule 30 (lower (icmp _ cc x @ (value_type (ty_supported_vec ty)) y)) (gen_expand_mask ty (gen_icmp_mask ty cc x y))) ;;;;; Rules for `fcmp`;;;;;;;;; -(rule 0 (lower (fcmp cc x @ (value_type (ty_supported_float_full ty)) y)) +(rule 0 (lower (fcmp _ cc x @ (value_type (ty_supported_float_full ty)) y)) (lower_float_compare (fcmp_to_float_compare cc ty x y))) (decl lower_float_compare (FloatCompare) XReg) (rule (lower_float_compare (FloatCompare.One r)) r) (rule (lower_float_compare (FloatCompare.Zero r)) (rv_seqz r)) -(rule 1 (lower (fcmp cc x @ (value_type (ty_supported_vec ty)) y)) +(rule 1 (lower (fcmp _ cc x @ (value_type (ty_supported_vec ty)) y)) (gen_expand_mask ty (gen_fcmp_mask ty cc x y))) ;;;;; Rules for `func_addr`;;;;;;;;; (rule - (lower (func_addr (func_ref_data _ name dist _))) + (lower (func_addr _ (func_ref_data _ name dist _))) (load_ext_name name 0 dist)) ;;;;; Rules for `fcvt_to_uint`;;;;;;;;; @@ -2428,7 +2435,7 @@ ;; TODO: could this perhaps be more optimal through inspection of the `fcsr`? ;; Unsure whether that needs to be preserved across function calls and/or would ;; cause other problems. Also unsure whether it's actually more performant. -(rule (lower (has_type ity (fcvt_to_uint v @ (value_type fty)))) +(rule (lower (has_type ity (fcvt_to_uint _ v @ (value_type fty)))) (let ((_ InstOutput (gen_trapz (rv_feq fty v v) (TrapCode.BAD_CONVERSION_TO_INTEGER))) (min FReg (imm fty (fcvt_umin_bound fty false))) (_ InstOutput (gen_trapnz (rv_fle fty v min) (TrapCode.INTEGER_OVERFLOW))) @@ -2445,7 +2452,7 @@ ;;;;; Rules for `fcvt_to_sint`;;;;;;;;; ;; NB: see above with `fcvt_to_uint` as this is similar -(rule (lower (has_type ity (fcvt_to_sint v @ (value_type fty)))) +(rule (lower (has_type ity (fcvt_to_sint _ v @ (value_type fty)))) (let ((_ InstOutput (gen_trapz (rv_feq fty v v) (TrapCode.BAD_CONVERSION_TO_INTEGER))) (min FReg (imm fty (fcvt_smin_bound fty ity false))) (_ InstOutput (gen_trapnz (rv_fle fty v min) (TrapCode.INTEGER_OVERFLOW))) @@ -2461,7 +2468,7 @@ ;;;;; Rules for `fcvt_to_sint_sat`;;;;;;;;; -(rule 0 (lower (has_type to (fcvt_to_sint_sat v @ (value_type (ty_supported_float_full from))))) +(rule 0 (lower (has_type to (fcvt_to_sint_sat _ v @ (value_type (ty_supported_float_full from))))) (handle_fcvt_to_int_nan from v (lower_fcvt_to_sint_sat from to v))) ;; Lowers to a `rv_fcvt*` instruction but handles 8/16-bit cases where the @@ -2496,7 +2503,7 @@ (not_nan_mask XReg (rv_neg is_not_nan))) (rv_and xreg not_nan_mask))) -(rule 1 (lower (has_type (ty_supported_vec _) (fcvt_to_sint_sat v @ (value_type from_ty)))) +(rule 1 (lower (has_type (ty_supported_vec _) (fcvt_to_sint_sat _ v @ (value_type from_ty)))) (if-let zero (i8_to_imm5 0)) (let ((is_nan VReg (rv_vmfne_vv v v (unmasked) from_ty)) (cvt VReg (rv_vfcvt_rtz_x_f_v v (unmasked) from_ty))) @@ -2504,7 +2511,7 @@ ;;;;; Rules for `fcvt_to_uint_sat`;;;;;;;;; -(rule 0 (lower (has_type to (fcvt_to_uint_sat v @ (value_type (ty_supported_float_full from))))) +(rule 0 (lower (has_type to (fcvt_to_uint_sat _ v @ (value_type (ty_supported_float_full from))))) (handle_fcvt_to_int_nan from v (lower_fcvt_to_uint_sat from to v))) ;; Lowers to a `rv_fcvt*` instruction but handles 8/16-bit cases where the @@ -2523,78 +2530,78 @@ (decl fcvt_umin_bound (Type bool) u64) (extern constructor fcvt_umin_bound fcvt_umin_bound) -(rule 1 (lower (has_type (ty_supported_vec _) (fcvt_to_uint_sat v @ (value_type from_ty)))) +(rule 1 (lower (has_type (ty_supported_vec _) (fcvt_to_uint_sat _ v @ (value_type from_ty)))) (if-let zero (i8_to_imm5 0)) (let ((is_nan VReg (rv_vmfne_vv v v (unmasked) from_ty)) (cvt VReg (rv_vfcvt_rtz_xu_f_v v (unmasked) from_ty))) (rv_vmerge_vim cvt zero is_nan from_ty))) ;;;;; Rules for `fcvt_from_sint`;;;;;;;;; -(rule 0 (lower (has_type $F32 (fcvt_from_sint v @ (value_type (fits_in_16 ty))))) +(rule 0 (lower (has_type $F32 (fcvt_from_sint _ v @ (value_type (fits_in_16 ty))))) (rv_fcvtsl (FRM.RNE) (sext v))) -(rule 1 (lower (has_type $F32 (fcvt_from_sint v @ (value_type $I32)))) +(rule 1 (lower (has_type $F32 (fcvt_from_sint _ v @ (value_type $I32)))) (rv_fcvtsw (FRM.RNE) v)) -(rule 1 (lower (has_type $F32 (fcvt_from_sint v @ (value_type $I64)))) +(rule 1 (lower (has_type $F32 (fcvt_from_sint _ v @ (value_type $I64)))) (rv_fcvtsl (FRM.RNE) v)) -(rule 0 (lower (has_type $F64 (fcvt_from_sint v @ (value_type (fits_in_16 ty))))) +(rule 0 (lower (has_type $F64 (fcvt_from_sint _ v @ (value_type (fits_in_16 ty))))) (rv_fcvtdl (FRM.RNE) (sext v))) -(rule 1 (lower (has_type $F64 (fcvt_from_sint v @ (value_type $I32)))) +(rule 1 (lower (has_type $F64 (fcvt_from_sint _ v @ (value_type $I32)))) (rv_fcvtdw v)) -(rule 1 (lower (has_type $F64 (fcvt_from_sint v @ (value_type $I64)))) +(rule 1 (lower (has_type $F64 (fcvt_from_sint _ v @ (value_type $I64)))) (rv_fcvtdl (FRM.RNE) v)) -(rule 2 (lower (has_type (ty_supported_vec _) (fcvt_from_sint v @ (value_type from_ty)))) +(rule 2 (lower (has_type (ty_supported_vec _) (fcvt_from_sint _ v @ (value_type from_ty)))) (rv_vfcvt_f_x_v v (unmasked) from_ty)) ;;;;; Rules for `fcvt_from_uint`;;;;;;;;; -(rule 0 (lower (has_type $F32 (fcvt_from_uint v @ (value_type (fits_in_16 ty))))) +(rule 0 (lower (has_type $F32 (fcvt_from_uint _ v @ (value_type (fits_in_16 ty))))) (rv_fcvtslu (FRM.RNE) (zext v))) -(rule 1 (lower (has_type $F32 (fcvt_from_uint v @ (value_type $I32)))) +(rule 1 (lower (has_type $F32 (fcvt_from_uint _ v @ (value_type $I32)))) (rv_fcvtswu (FRM.RNE) v)) -(rule 1 (lower (has_type $F32 (fcvt_from_uint v @ (value_type $I64)))) +(rule 1 (lower (has_type $F32 (fcvt_from_uint _ v @ (value_type $I64)))) (rv_fcvtslu (FRM.RNE) v)) -(rule 0 (lower (has_type $F64 (fcvt_from_uint v @ (value_type (fits_in_16 ty))))) +(rule 0 (lower (has_type $F64 (fcvt_from_uint _ v @ (value_type (fits_in_16 ty))))) (rv_fcvtdlu (FRM.RNE) (zext v))) -(rule 1 (lower (has_type $F64 (fcvt_from_uint v @ (value_type $I32)))) +(rule 1 (lower (has_type $F64 (fcvt_from_uint _ v @ (value_type $I32)))) (rv_fcvtdwu v)) -(rule 1 (lower (has_type $F64 (fcvt_from_uint v @ (value_type $I64)))) +(rule 1 (lower (has_type $F64 (fcvt_from_uint _ v @ (value_type $I64)))) (rv_fcvtdlu (FRM.RNE) v)) -(rule 2 (lower (has_type (ty_supported_vec _) (fcvt_from_uint v @ (value_type from_ty)))) +(rule 2 (lower (has_type (ty_supported_vec _) (fcvt_from_uint _ v @ (value_type from_ty)))) (rv_vfcvt_f_xu_v v (unmasked) from_ty)) ;;;;; Rules for `symbol_value`;;;;;;;;; (rule - (lower (symbol_value (symbol_value_data name dist offset))) + (lower (symbol_value _ (symbol_value_data name dist offset))) (load_ext_name name offset dist)) ;;;;; Rules for `tls_value` ;;;;;;;;;;;;;; -(rule (lower (has_type (tls_model (TlsModel.ElfGd)) (tls_value (symbol_value_data name _ _)))) +(rule (lower (has_type (tls_model (TlsModel.ElfGd)) (tls_value _ (symbol_value_data name _ _)))) (elf_tls_get_addr name)) ;;;;; Rules for `bitcast`;;;;;;;;; ;; These rules should probably be handled in `gen_bitcast`, but it's convenient to have that return ;; a single register, instead of a `ValueRegs` -(rule 3 (lower (has_type (ty_reg_pair _) (bitcast _ v @ (value_type (ty_supported_vec _))))) +(rule 3 (lower (has_type (ty_reg_pair _) (bitcast _ _ v @ (value_type (ty_supported_vec _))))) (value_regs (gen_extractlane $I64X2 v 0) (gen_extractlane $I64X2 v 1))) ;; Move the high half into a vector register, and then use vslide1up to move it up and ;; insert the lower half in one instruction. -(rule 2 (lower (has_type (ty_supported_vec _) (bitcast _ v @ (value_type (ty_reg_pair _))))) +(rule 2 (lower (has_type (ty_supported_vec _) (bitcast _ _ v @ (value_type (ty_reg_pair _))))) (let ((lo XReg (value_regs_get v 0)) (hi XReg (value_regs_get v 1)) (vstate VState (vstate_from_type $I64X2)) @@ -2603,38 +2610,38 @@ ;; `gen_bitcast` below only works with single register values, so handle I128 ;; and F128 specially here. -(rule 1 (lower (has_type (ty_reg_pair _) (bitcast _ v @ (value_type (ty_reg_pair _))))) +(rule 1 (lower (has_type (ty_reg_pair _) (bitcast _ _ v @ (value_type (ty_reg_pair _))))) v) -(rule 0 (lower (has_type out_ty (bitcast _ v @ (value_type in_ty)))) +(rule 0 (lower (has_type out_ty (bitcast _ _ v @ (value_type in_ty)))) (gen_bitcast v in_ty out_ty)) ;;;;; Rules for `ceil`;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (ceil x))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (ceil _ x))) (gen_float_round (FRM.RUP) x ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (ceil x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (ceil _ x))) (gen_vec_round x (FRM.RUP) ty)) ;;;;; Rules for `floor`;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (floor x))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (floor _ x))) (gen_float_round (FRM.RDN) x ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (floor x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (floor _ x))) (gen_vec_round x (FRM.RDN) ty)) ;;;;; Rules for `trunc`;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (trunc x))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (trunc _ x))) (gen_float_round (FRM.RTZ) x ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (trunc x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (trunc _ x))) (gen_vec_round x (FRM.RTZ) ty)) ;;;;; Rules for `nearest`;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_float_full ty) (nearest x))) +(rule 0 (lower (has_type (ty_supported_float_full ty) (nearest _ x))) (gen_float_round (FRM.RNE) x ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (nearest x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (nearest _ x))) (gen_vec_round x (FRM.RNE) ty)) @@ -2648,10 +2655,10 @@ ;; Base case: use `gen_bmask` to generate a 0 mask or -1 mask from the value of ;; `cmp`. This is then used with some bit twiddling to produce the final result. -(rule 0 (lower (has_type (fits_in_64 _) (select_spectre_guard cmp x y))) +(rule 0 (lower (has_type (fits_in_64 _) (select_spectre_guard _ cmp x y))) (let ((mask XReg (gen_bmask cmp))) (rv_or (rv_and mask x) (rv_andn y mask)))) -(rule 1 (lower (has_type $I128 (select_spectre_guard cmp x y))) +(rule 1 (lower (has_type $I128 (select_spectre_guard _ cmp x y))) (let ((mask XReg (gen_bmask cmp))) (value_regs (rv_or (rv_and mask (value_regs_get x 0)) (rv_andn (value_regs_get y 0) mask)) @@ -2659,14 +2666,14 @@ ;; Special case when an argument is the constant zero as some ands and ors ;; can be folded away. -(rule 2 (lower (has_type (fits_in_64 _) (select_spectre_guard cmp (i64_from_iconst 0) y))) +(rule 2 (lower (has_type (fits_in_64 _) (select_spectre_guard _ cmp (i64_from_iconst 0) y))) (rv_andn y (gen_bmask cmp))) -(rule 3 (lower (has_type (fits_in_64 _) (select_spectre_guard cmp x (i64_from_iconst 0)))) +(rule 3 (lower (has_type (fits_in_64 _) (select_spectre_guard _ cmp x (i64_from_iconst 0)))) (rv_and x (gen_bmask cmp))) ;;;;; Rules for `bmask`;;;;;;;;; (rule - (lower (has_type oty (bmask x))) + (lower (has_type oty (bmask _ x))) (lower_bmask x oty)) ;; N.B.: the Ret itself is generated by the ABI. @@ -2675,13 +2682,13 @@ ;;; Rules for `get_{frame,stack}_pointer` and `get_return_address` ;;;;;;;;;;;;; -(rule (lower (get_frame_pointer)) +(rule (lower (get_frame_pointer _)) (gen_mov_from_preg (fp_reg))) -(rule (lower (get_stack_pointer)) +(rule (lower (get_stack_pointer _)) (gen_mov_from_preg (sp_reg))) -(rule (lower (get_return_address)) +(rule (lower (get_return_address _)) (load_ra)) ;;; Rules for `iabs` ;;;;;;;;;;;;; @@ -2691,7 +2698,7 @@ ;; sext.{b,h,w} a0, a0 ;; neg a1, a0 ;; max a0, a0, a1 -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (iabs x))) +(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (iabs _ x))) (let ((extended XReg (sext x)) (negated XReg (rv_neg extended))) (gen_select_xreg (cmp_gt extended negated) extended negated))) @@ -2699,7 +2706,7 @@ ;; For vectors we generate the same code, but with vector instructions ;; we can skip the sign extension, since the vector unit will only process ;; Element Sized chunks. -(rule 1 (lower (has_type (ty_supported_vec ty) (iabs x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (iabs _ x))) (let ((negated VReg (rv_vneg_v x (unmasked) ty))) (rv_vmax_vv x negated (unmasked) ty))) @@ -2796,7 +2803,7 @@ ;;;; Rules for `extractlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (extractlane x @ (value_type ty) (u8_from_uimm8 idx))) +(rule (lower (extractlane _ x @ (value_type ty) (u8_from_uimm8 idx))) (gen_extractlane ty x idx)) ;;;; Rules for `insertlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2804,14 +2811,14 @@ ;; We can insert a lane by using a masked splat from an X register. ;; Build a mask that is only enabled in the lane we want to insert. ;; Then use a masked splat (vmerge) to insert the value. -(rule 0 (lower (insertlane vec @ (value_type (ty_supported_vec ty)) +(rule 0 (lower (insertlane _ vec @ (value_type (ty_supported_vec ty)) val @ (value_type (ty_int _)) (u8_from_uimm8 lane))) (let ((mask VReg (gen_vec_mask (u64_wrapping_shl 1 lane)))) (rv_vmerge_vxm vec val mask ty))) ;; Similar to above, but using the float variants of the instructions. -(rule 1 (lower (insertlane vec @ (value_type (ty_supported_vec ty)) +(rule 1 (lower (insertlane _ vec @ (value_type (ty_supported_vec ty)) val @ (value_type (ty_supported_float_full _)) (u8_from_uimm8 lane))) (let ((mask VReg (gen_vec_mask (u64_wrapping_shl 1 lane)))) @@ -2819,7 +2826,7 @@ ;; If we are inserting from an Imm5 const we can use the immediate ;; variant of vmerge. -(rule 2 (lower (insertlane vec @ (value_type (ty_supported_vec ty)) +(rule 2 (lower (insertlane _ vec @ (value_type (ty_supported_vec ty)) (i64_from_iconst (imm5_from_i64 imm)) (u8_from_uimm8 lane))) (let ((mask VReg (gen_vec_mask (u64_wrapping_shl 1 lane)))) @@ -2827,13 +2834,13 @@ ;;;; Rules for `splat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type ty (splat n @ (value_type (ty_supported_float_full _))))) +(rule 0 (lower (has_type ty (splat _ n @ (value_type (ty_supported_float_full _))))) (rv_vfmv_vf n ty)) -(rule 1 (lower (has_type ty (splat n @ (value_type (ty_int_ref_scalar_64 _))))) +(rule 1 (lower (has_type ty (splat _ n @ (value_type (ty_int_ref_scalar_64 _))))) (rv_vmv_vx n ty)) -(rule 2 (lower (has_type ty (splat (iconst (u64_from_imm64 (imm5_from_u64 imm)))))) +(rule 2 (lower (has_type ty (splat _ (iconst _ (u64_from_imm64 (imm5_from_u64 imm)))))) (rv_vmv_vi imm ty)) ;; TODO: We can splat out more patterns by using for example a vmv.v.i i8x16 for @@ -2844,56 +2851,56 @@ ;;;; Rules for `uadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_vec ty) (uadd_sat x y))) +(rule 0 (lower (has_type (ty_supported_vec ty) (uadd_sat _ x y))) (rv_vsaddu_vv x y (unmasked) ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (uadd_sat x (splat y)))) +(rule 1 (lower (has_type (ty_supported_vec ty) (uadd_sat _ x (splat _ y)))) (rv_vsaddu_vx x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (uadd_sat (splat x) y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (uadd_sat _ (splat _ x) y))) (rv_vsaddu_vx y x (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (uadd_sat x y))) +(rule 3 (lower (has_type (ty_supported_vec ty) (uadd_sat _ x y))) (if-let y_imm (replicated_imm5 y)) (rv_vsaddu_vi x y_imm (unmasked) ty)) -(rule 4 (lower (has_type (ty_supported_vec ty) (uadd_sat x y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (uadd_sat _ x y))) (if-let x_imm (replicated_imm5 x)) (rv_vsaddu_vi y x_imm (unmasked) ty)) ;;;; Rules for `sadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_vec ty) (sadd_sat x y))) +(rule 0 (lower (has_type (ty_supported_vec ty) (sadd_sat _ x y))) (rv_vsadd_vv x y (unmasked) ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (sadd_sat x (splat y)))) +(rule 1 (lower (has_type (ty_supported_vec ty) (sadd_sat _ x (splat _ y)))) (rv_vsadd_vx x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (sadd_sat (splat x) y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (sadd_sat _ (splat _ x) y))) (rv_vsadd_vx y x (unmasked) ty)) -(rule 3 (lower (has_type (ty_supported_vec ty) (sadd_sat x y))) +(rule 3 (lower (has_type (ty_supported_vec ty) (sadd_sat _ x y))) (if-let y_imm (replicated_imm5 y)) (rv_vsadd_vi x y_imm (unmasked) ty)) -(rule 4 (lower (has_type (ty_supported_vec ty) (sadd_sat x y))) +(rule 4 (lower (has_type (ty_supported_vec ty) (sadd_sat _ x y))) (if-let x_imm (replicated_imm5 x)) (rv_vsadd_vi y x_imm (unmasked) ty)) ;;;; Rules for `usub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_vec ty) (usub_sat x y))) +(rule 0 (lower (has_type (ty_supported_vec ty) (usub_sat _ x y))) (rv_vssubu_vv x y (unmasked) ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (usub_sat x (splat y)))) +(rule 1 (lower (has_type (ty_supported_vec ty) (usub_sat _ x (splat _ y)))) (rv_vssubu_vx x y (unmasked) ty)) ;;;; Rules for `ssub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_vec ty) (ssub_sat x y))) +(rule 0 (lower (has_type (ty_supported_vec ty) (ssub_sat _ x y))) (rv_vssub_vv x y (unmasked) ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (ssub_sat x (splat y)))) +(rule 1 (lower (has_type (ty_supported_vec ty) (ssub_sat _ x (splat _ y)))) (rv_vssub_vx x y (unmasked) ty)) ;;;; Rules for `vall_true` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2904,7 +2911,7 @@ ;; be a 1. ;; The reduce operation leaves the result in the lowest lane, we then move it ;; into the destination X register. -(rule (lower (vall_true x @ (value_type (ty_supported_vec ty)))) +(rule (lower (vall_true _ x @ (value_type (ty_supported_vec ty)))) (if-let one (i8_to_imm5 1)) ;; We don't need to broadcast the immediate into all lanes, only into lane 0. ;; I did it this way since it uses one less instruction than with a vmv.s.x. @@ -2918,7 +2925,7 @@ ;; Here we do a Vector Reduce operation. Get the unsigned maximum value of the ;; input vector register. Move the max to an X register, and do a `snez` on it ;; to ensure its either 1 or 0. -(rule (lower (vany_true x @ (value_type (ty_supported_vec ty)))) +(rule (lower (vany_true _ x @ (value_type (ty_supported_vec ty)))) (let ((max VReg (rv_vredmaxu_vs x x (unmasked) ty)) (x_max XReg (rv_vmv_xs max ty))) (rv_snez x_max))) @@ -2934,7 +2941,7 @@ ;; to hold the full mask. Additionally, in some cases (e.g. i64x2) we are going ;; to read some tail bits. These are undefined, so we need to further mask them ;; off. -(rule (lower (vhigh_bits x @ (value_type (ty_supported_vec ty)))) +(rule (lower (vhigh_bits _ x @ (value_type (ty_supported_vec ty)))) (let ((mask VReg (rv_vmslt_vx x (zero_reg) (unmasked) ty)) ;; Here we only need I64X1, but emit an AVL of 2 since it ;; saves one vector state change in the case of I64X2. @@ -2946,13 +2953,13 @@ ;;;; Rules for `swizzle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_vec ty) (swizzle x y))) +(rule 0 (lower (has_type (ty_supported_vec ty) (swizzle _ x y))) (rv_vrgather_vv x y (unmasked) ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (swizzle x (splat y)))) +(rule 1 (lower (has_type (ty_supported_vec ty) (swizzle _ x (splat _ y)))) (rv_vrgather_vx x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (swizzle x y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (swizzle _ x y))) (if-let y_imm (replicated_uimm5 y)) (rv_vrgather_vi x y_imm (unmasked) ty)) @@ -2963,7 +2970,7 @@ ;; ;; vrgather will insert a 0 for lanes that are out of bounds, so we can let it load ;; negative and out of bounds indexes. -(rule (lower (has_type (ty_supported_vec ty @ $I8X16) (shuffle x y (vconst_from_immediate mask)))) +(rule (lower (has_type (ty_supported_vec ty @ $I8X16) (shuffle _ x y (vconst_from_immediate mask)))) (if-let neg16 (i8_to_imm5 -16)) (let ((x_mask VReg (gen_constant ty mask)) (x_lanes VReg (rv_vrgather_vv x x_mask (unmasked) ty)) @@ -2974,51 +2981,51 @@ ;;;; Rules for `swiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Slide down half the vector, and do a signed extension. -(rule 0 (lower (has_type (ty_supported_vec out_ty) (swiden_high x @ (value_type in_ty)))) +(rule 0 (lower (has_type (ty_supported_vec out_ty) (swiden_high _ x @ (value_type in_ty)))) (rv_vsext_vf2 (gen_slidedown_half in_ty x) (unmasked) out_ty)) -(rule 1 (lower (has_type (ty_supported_vec out_ty) (swiden_high (swiden_high x @ (value_type in_ty))))) +(rule 1 (lower (has_type (ty_supported_vec out_ty) (swiden_high _ (swiden_high _ x @ (value_type in_ty))))) (if-let (uimm5_from_u64 amt) (u64_wrapping_sub (ty_lane_count in_ty) (ty_lane_count out_ty))) (rv_vsext_vf4 (rv_vslidedown_vi x amt (unmasked) in_ty) (unmasked) out_ty)) -(rule 2 (lower (has_type (ty_supported_vec out_ty) (swiden_high (swiden_high (swiden_high x @ (value_type in_ty)))))) +(rule 2 (lower (has_type (ty_supported_vec out_ty) (swiden_high _ (swiden_high _ (swiden_high _ x @ (value_type in_ty)))))) (if-let (uimm5_from_u64 amt) (u64_wrapping_sub (ty_lane_count in_ty) (ty_lane_count out_ty))) (rv_vsext_vf8 (rv_vslidedown_vi x amt (unmasked) in_ty) (unmasked) out_ty)) ;;;; Rules for `uwiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Slide down half the vector, and do a zero extension. -(rule 0 (lower (has_type (ty_supported_vec out_ty) (uwiden_high x @ (value_type in_ty)))) +(rule 0 (lower (has_type (ty_supported_vec out_ty) (uwiden_high _ x @ (value_type in_ty)))) (rv_vzext_vf2 (gen_slidedown_half in_ty x) (unmasked) out_ty)) -(rule 1 (lower (has_type (ty_supported_vec out_ty) (uwiden_high (uwiden_high x @ (value_type in_ty))))) +(rule 1 (lower (has_type (ty_supported_vec out_ty) (uwiden_high _ (uwiden_high _ x @ (value_type in_ty))))) (if-let (uimm5_from_u64 amt) (u64_wrapping_sub (ty_lane_count in_ty) (ty_lane_count out_ty))) (rv_vzext_vf4 (rv_vslidedown_vi x amt (unmasked) in_ty) (unmasked) out_ty)) -(rule 2 (lower (has_type (ty_supported_vec out_ty) (uwiden_high (uwiden_high (uwiden_high x @ (value_type in_ty)))))) +(rule 2 (lower (has_type (ty_supported_vec out_ty) (uwiden_high _ (uwiden_high _ (uwiden_high _ x @ (value_type in_ty)))))) (if-let (uimm5_from_u64 amt) (u64_wrapping_sub (ty_lane_count in_ty) (ty_lane_count out_ty))) (rv_vzext_vf8 (rv_vslidedown_vi x amt (unmasked) in_ty) (unmasked) out_ty)) ;;;; Rules for `swiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_vec out_ty) (swiden_low x))) +(rule 0 (lower (has_type (ty_supported_vec out_ty) (swiden_low _ x))) (rv_vsext_vf2 x (unmasked) out_ty)) -(rule 1 (lower (has_type (ty_supported_vec out_ty) (swiden_low (swiden_low x)))) +(rule 1 (lower (has_type (ty_supported_vec out_ty) (swiden_low _ (swiden_low _ x)))) (rv_vsext_vf4 x (unmasked) out_ty)) -(rule 2 (lower (has_type (ty_supported_vec out_ty) (swiden_low (swiden_low (swiden_low x))))) +(rule 2 (lower (has_type (ty_supported_vec out_ty) (swiden_low _ (swiden_low _ (swiden_low _ x))))) (rv_vsext_vf8 x (unmasked) out_ty)) ;;;; Rules for `uwiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_vec out_ty) (uwiden_low x))) +(rule 0 (lower (has_type (ty_supported_vec out_ty) (uwiden_low _ x))) (rv_vzext_vf2 x (unmasked) out_ty)) -(rule 1 (lower (has_type (ty_supported_vec out_ty) (uwiden_low (uwiden_low x)))) +(rule 1 (lower (has_type (ty_supported_vec out_ty) (uwiden_low _ (uwiden_low _ x)))) (rv_vzext_vf4 x (unmasked) out_ty)) -(rule 2 (lower (has_type (ty_supported_vec out_ty) (uwiden_low (uwiden_low (uwiden_low x))))) +(rule 2 (lower (has_type (ty_supported_vec out_ty) (uwiden_low _ (uwiden_low _ (uwiden_low _ x))))) (rv_vzext_vf8 x (unmasked) out_ty)) ;;;; Rules for `iadd_pairwise` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3036,7 +3043,7 @@ ;; However V8 does something better. They use 2 vcompresses using LMUL2, that means ;; that they can do the whole thing in 3 instructions (2 vcompress + vadd). We don't ;; support LMUL > 1, so we can't do that. -(rule (lower (has_type (ty_supported_vec ty) (iadd_pairwise x y))) +(rule (lower (has_type (ty_supported_vec ty) (iadd_pairwise _ x y))) (if-let half_size (u64_to_uimm5 (u64_checked_div (ty_lane_count ty) 2))) (let ((odd_mask VReg (gen_vec_mask 0x5555555555555555)) (lhs_lo VReg (rv_vcompress_vm x odd_mask ty)) @@ -3067,7 +3074,7 @@ ;; Right Logical instruction using the `vxrm` fixed-point rounding mode. The ;; default rounding mode is `rnu` (round-to-nearest-up (add +0.5 LSB)). ;; Which is coincidentally the rounding mode we want for `avg_round`. -(rule (lower (has_type (ty_supported_vec ty) (avg_round x y))) +(rule (lower (has_type (ty_supported_vec ty) (avg_round _ x y))) (if-let one (u64_to_uimm5 1)) (let ((lhs VReg (rv_vand_vv x y (unmasked) ty)) (xor VReg (rv_vxor_vv x y (unmasked) ty)) @@ -3076,38 +3083,38 @@ ;;;; Rules for `scalar_to_vector` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_vec ty) (scalar_to_vector x))) +(rule 0 (lower (has_type (ty_supported_vec ty) (scalar_to_vector _ x))) (if (ty_vector_float ty)) (let ((zero VReg (rv_vmv_vx (zero_reg) ty)) (elem VReg (rv_vfmv_sf x ty)) (mask VReg (gen_vec_mask 1))) (rv_vmerge_vvm zero elem mask ty))) -(rule 1 (lower (has_type (ty_supported_vec ty) (scalar_to_vector x))) +(rule 1 (lower (has_type (ty_supported_vec ty) (scalar_to_vector _ x))) (if (ty_vector_not_float ty)) (let ((zero VReg (rv_vmv_vx (zero_reg) ty)) (mask VReg (gen_vec_mask 1))) (rv_vmerge_vxm zero x mask ty))) -(rule 2 (lower (has_type (ty_supported_vec ty) (scalar_to_vector (imm5_from_value x)))) +(rule 2 (lower (has_type (ty_supported_vec ty) (scalar_to_vector _ (imm5_from_value x)))) (let ((zero VReg (rv_vmv_vx (zero_reg) ty)) (mask VReg (gen_vec_mask 1))) (rv_vmerge_vim zero x mask ty))) ;;;; Rules for `sqmul_round_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_supported_vec ty) (sqmul_round_sat x y))) +(rule 0 (lower (has_type (ty_supported_vec ty) (sqmul_round_sat _ x y))) (rv_vsmul_vv x y (unmasked) ty)) -(rule 1 (lower (has_type (ty_supported_vec ty) (sqmul_round_sat x (splat y)))) +(rule 1 (lower (has_type (ty_supported_vec ty) (sqmul_round_sat _ x (splat _ y)))) (rv_vsmul_vx x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_supported_vec ty) (sqmul_round_sat (splat x) y))) +(rule 2 (lower (has_type (ty_supported_vec ty) (sqmul_round_sat _ (splat _ x) y))) (rv_vsmul_vx y x (unmasked) ty)) ;;;; Rules for `snarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_supported_vec out_ty) (snarrow x @ (value_type in_ty) y))) +(rule (lower (has_type (ty_supported_vec out_ty) (snarrow _ x @ (value_type in_ty) y))) (if-let lane_diff (u64_to_uimm5 (u64_checked_div (ty_lane_count out_ty) 2))) (if-let zero (u64_to_uimm5 0)) (let ((x_clip VReg (rv_vnclip_wi x zero (unmasked) (vstate_mf2 (ty_half_lanes out_ty)))) @@ -3116,7 +3123,7 @@ ;;;; Rules for `uunarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (ty_supported_vec out_ty) (uunarrow x @ (value_type in_ty) y))) +(rule (lower (has_type (ty_supported_vec out_ty) (uunarrow _ x @ (value_type in_ty) y))) (if-let lane_diff (u64_to_uimm5 (u64_checked_div (ty_lane_count out_ty) 2))) (if-let zero (u64_to_uimm5 0)) (let ((x_clip VReg (rv_vnclipu_wi x zero (unmasked) (vstate_mf2 (ty_half_lanes out_ty)))) @@ -3129,7 +3136,7 @@ ;; To correct for this we just remove negative values using `vmax` and then use the normal ;; unsigned to unsigned narrowing instruction. -(rule (lower (has_type (ty_supported_vec out_ty) (unarrow x @ (value_type in_ty) y))) +(rule (lower (has_type (ty_supported_vec out_ty) (unarrow _ x @ (value_type in_ty) y))) (if-let lane_diff (u64_to_uimm5 (u64_checked_div (ty_lane_count out_ty) 2))) (if-let zero (u64_to_uimm5 0)) (let ((x_pos VReg (rv_vmax_vx x (zero_reg) (unmasked) in_ty)) @@ -3141,7 +3148,7 @@ ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (rv64_label_address succ_label))) diff --git a/cranelift/codegen/src/isa/riscv64/lower.rs b/cranelift/codegen/src/isa/riscv64/lower.rs index 117778f820b3..ae56a1de4908 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.rs +++ b/cranelift/codegen/src/isa/riscv64/lower.rs @@ -30,6 +30,4 @@ impl LowerBackend for Riscv64Backend { // right now riscv64 not support this feature. None } - - type FactFlowState = (); } diff --git a/cranelift/codegen/src/isa/s390x/inst.isle b/cranelift/codegen/src/isa/s390x/inst.isle index 41830d57bd38..04537641d267 100644 --- a/cranelift/codegen/src/isa/s390x/inst.isle +++ b/cranelift/codegen/src/isa/s390x/inst.isle @@ -1401,14 +1401,14 @@ (UCmpHi128) )) -;; An integer vector element comparion operation. +;; An integer vector element comparison operation. (type VecIntEltCmpOp (enum (SCmp128) (UCmp128) )); -;; A floatint-point vector comparison operation. +;; A floating-point vector comparison operation. (type VecFloatCmpOp (enum (CmpEq32x4) @@ -1918,11 +1918,11 @@ (rule (lower_address flags addr @ (value_type (ty_addr64 _)) (i64_from_offset offset)) (memarg_reg_plus_off addr offset 0 flags)) -(rule 1 (lower_address flags (has_type (ty_addr64 _) (iadd x y)) (memarg_imm_from_offset z)) +(rule 1 (lower_address flags (has_type (ty_addr64 _) (iadd _ x y)) (memarg_imm_from_offset z)) (memarg_reg_plus_reg_plus_off x y z flags)) (rule 1 (lower_address flags - (symbol_value (symbol_value_data name (RelocDistance.Near) sym_offset)) + (symbol_value _ (symbol_value_data name (RelocDistance.Near) sym_offset)) (i64_from_offset offset)) (if-let final_offset (memarg_symbol_offset_sum offset sym_offset)) (memarg_symbol name final_offset flags)) @@ -1935,7 +1935,7 @@ (rule (lower_address_bias flags addr @ (value_type $I64) (i64_from_offset offset) bias) (memarg_reg_plus_off addr offset bias flags)) -(rule 1 (lower_address_bias flags (has_type $I64 (iadd x y)) z bias) +(rule 1 (lower_address_bias flags (has_type $I64 (iadd _ x y)) z bias) (if-let final_offset (memarg_imm_from_offset_plus_bias z bias)) (memarg_reg_plus_reg_plus_off x y final_offset flags)) @@ -1944,7 +1944,7 @@ (decl pure partial load_sym (Inst) Inst) (rule (load_sym inst) - (if-let (load _ (symbol_value (symbol_value_data _ (RelocDistance.Near) sym_offset)) + (if-let (load _ _ (symbol_value _ (symbol_value_data _ (RelocDistance.Near) sym_offset)) (i64_from_offset load_offset)) inst) (if (memarg_symbol_offset_sum sym_offset load_offset)) @@ -1952,7 +1952,7 @@ (decl pure partial uload16_sym (Inst) Inst) (rule (uload16_sym inst) - (if-let (uload16 _ (symbol_value (symbol_value_data _ (RelocDistance.Near) sym_offset)) + (if-let (uload16 _ _ (symbol_value _ (symbol_value_data _ (RelocDistance.Near) sym_offset)) (i64_from_offset load_offset)) inst) (if (memarg_symbol_offset_sum sym_offset load_offset)) @@ -1972,11 +1972,11 @@ ;; A value that is the result of a sign-extend from a 32-bit value. (decl sext32_value (Value) Value) -(extractor (sext32_value x) (sextend (and x (value_type $I32)))) +(extractor (sext32_value x) (sextend _ (and x (value_type $I32)))) ;; A value that is the result of a zero-extend from a 32-bit value. (decl zext32_value (Value) Value) -(extractor (zext32_value x) (uextend (and x (value_type $I32)))) +(extractor (zext32_value x) (uextend _ (and x (value_type $I32)))) ;; Helpers for sinkable loads ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1988,7 +1988,7 @@ ;; Sinkable big-endian load instruction. (decl sinkable_load (Inst) Value) (extractor (sinkable_load inst) - (sinkable_inst (and inst (load (bigendian) _addr _offset)))) + (sinkable_inst (and inst (load _ (bigendian) _addr _offset)))) ;; Sinkable big-endian load instruction (32/64-bit types only). (decl sinkable_load_32_64 (Inst) Value) @@ -2003,60 +2003,60 @@ ;; Sinkable little-endian load instruction. (decl sinkable_load_little (Inst) Value) (extractor (sinkable_load_little inst) - (sinkable_inst (and inst (load (littleendian) _addr _offset)))) + (sinkable_inst (and inst (load _ (littleendian) _addr _offset)))) ;; Sinkable big-endian sload16 instruction. (decl sinkable_sload16 (Inst) Value) (extractor (sinkable_sload16 inst) - (sinkable_inst (and inst (sload16 (bigendian) _addr _offset)))) + (sinkable_inst (and inst (sload16 _ (bigendian) _addr _offset)))) ;; Sinkable big-endian sload32 instruction. (decl sinkable_sload32 (Inst) Value) (extractor (sinkable_sload32 inst) - (sinkable_inst (and inst (sload32 (bigendian) _addr _offset)))) + (sinkable_inst (and inst (sload32 _ (bigendian) _addr _offset)))) ;; Sinkable big-endian uload16 instruction. (decl sinkable_uload16 (Inst) Value) (extractor (sinkable_uload16 inst) - (sinkable_inst (and inst (uload16 (bigendian) _addr _offset)))) + (sinkable_inst (and inst (uload16 _ (bigendian) _addr _offset)))) ;; Sinkable big-endian uload32 instruction. (decl sinkable_uload32 (Inst) Value) (extractor (sinkable_uload32 inst) - (sinkable_inst (and inst (uload32 (bigendian) _addr _offset)))) + (sinkable_inst (and inst (uload32 _ (bigendian) _addr _offset)))) ;; Sink a load instruction, returning its address as `MemArg`. ;; This is a side-effectful operation, see `sink_inst`. (decl sink_load (Inst) MemArg) -(rule (sink_load inst @ (load flags addr offset)) +(rule (sink_load inst @ (load _ flags addr offset)) (let ((_ Unit (sink_inst inst))) (lower_address flags addr offset))) ;; Sink a sload16 instruction, returning its address as `MemArg`. ;; This is a side-effectful operation, see `sink_inst`. (decl sink_sload16 (Inst) MemArg) -(rule (sink_sload16 inst @ (sload16 flags addr offset)) +(rule (sink_sload16 inst @ (sload16 _ flags addr offset)) (let ((_ Unit (sink_inst inst))) (lower_address flags addr offset))) ;; Sink a sload32 instruction, returning its address as `MemArg`. ;; This is a side-effectful operation, see `sink_inst`. (decl sink_sload32 (Inst) MemArg) -(rule (sink_sload32 inst @ (sload32 flags addr offset)) +(rule (sink_sload32 inst @ (sload32 _ flags addr offset)) (let ((_ Unit (sink_inst inst))) (lower_address flags addr offset))) ;; Sink a uload16 instruction, returning its address as `MemArg`. ;; This is a side-effectful operation, see `sink_inst`. (decl sink_uload16 (Inst) MemArg) -(rule (sink_uload16 inst @ (uload16 flags addr offset)) +(rule (sink_uload16 inst @ (uload16 _ flags addr offset)) (let ((_ Unit (sink_inst inst))) (lower_address flags addr offset))) ;; Sink a uload32 instruction, returning its address as `MemArg`. ;; This is a side-effectful operation, see `sink_inst`. (decl sink_uload32 (Inst) MemArg) -(rule (sink_uload32 inst @ (uload32 flags addr offset)) +(rule (sink_uload32 inst @ (uload32 _ flags addr offset)) (let ((_ Unit (sink_inst inst))) (lower_address flags addr offset))) diff --git a/cranelift/codegen/src/isa/s390x/lower.isle b/cranelift/codegen/src/isa/s390x/lower.isle index 670ca6b2ccc0..a88e75166aca 100644 --- a/cranelift/codegen/src/isa/s390x/lower.isle +++ b/cranelift/codegen/src/isa/s390x/lower.isle @@ -12,37 +12,37 @@ ;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (iconst (u64_from_imm64 n)))) +(rule (lower (has_type ty (iconst _ (u64_from_imm64 n)))) (imm ty n)) ;;;; Rules for `f16const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f16const (u16_from_ieee16 x))) +(rule (lower (f16const _ (u16_from_ieee16 x))) (imm $F16 x)) ;;;; Rules for `f32const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f32const (u32_from_ieee32 x))) +(rule (lower (f32const _ (u32_from_ieee32 x))) (imm $F32 x)) ;;;; Rules for `f64const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f64const (u64_from_ieee64 x))) +(rule (lower (f64const _ (u64_from_ieee64 x))) (imm $F64 x)) ;;;; Rules for `f128const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f128const (u128_from_constant x))) +(rule (lower (f128const _ (u128_from_constant x))) (vec_imm $F128 (be_vec_const $F128 x))) ;;;; Rules for `vconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (vconst (u128_from_constant x)))) +(rule (lower (has_type ty (vconst _ (u128_from_constant x)))) (vec_imm ty (be_vec_const ty x))) @@ -54,13 +54,13 @@ ;;;; Rules for `iconcat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (vr128_ty ty) (iconcat x y))) +(rule (lower (has_type (vr128_ty ty) (iconcat _ x y))) (mov_to_vec128 ty y x)) ;;;; Rules for `isplit` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (isplit x @ (value_type $I128))) +(rule (lower (isplit _ x @ (value_type $I128))) (let ((x_reg Reg x) (x_hi Reg (vec_extract_lane $I64X2 x_reg 0 (zero_reg))) (x_lo Reg (vec_extract_lane $I64X2 x_reg 1 (zero_reg)))) @@ -70,56 +70,56 @@ ;;;; Rules for `iadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Add two registers. -(rule 0 (lower (has_type (fits_in_64 ty) (iadd x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (iadd _ x y))) (add_reg ty x y)) ;; Add a register and a sign-extended register. -(rule 8 (lower (has_type (fits_in_64 ty) (iadd x (sext32_value y)))) +(rule 8 (lower (has_type (fits_in_64 ty) (iadd _ x (sext32_value y)))) (add_reg_sext32 ty x y)) -(rule 15 (lower (has_type (fits_in_64 ty) (iadd (sext32_value x) y))) +(rule 15 (lower (has_type (fits_in_64 ty) (iadd _ (sext32_value x) y))) (add_reg_sext32 ty y x)) ;; Add a register and an immediate. -(rule 7 (lower (has_type (fits_in_64 ty) (iadd x (i16_from_value y)))) +(rule 7 (lower (has_type (fits_in_64 ty) (iadd _ x (i16_from_value y)))) (add_simm16 ty x y)) -(rule 14 (lower (has_type (fits_in_64 ty) (iadd (i16_from_value x) y))) +(rule 14 (lower (has_type (fits_in_64 ty) (iadd _ (i16_from_value x) y))) (add_simm16 ty y x)) -(rule 6 (lower (has_type (fits_in_64 ty) (iadd x (i32_from_value y)))) +(rule 6 (lower (has_type (fits_in_64 ty) (iadd _ x (i32_from_value y)))) (add_simm32 ty x y)) -(rule 13 (lower (has_type (fits_in_64 ty) (iadd (i32_from_value x) y))) +(rule 13 (lower (has_type (fits_in_64 ty) (iadd _ (i32_from_value x) y))) (add_simm32 ty y x)) ;; Add a register and memory (32/64-bit types). -(rule 5 (lower (has_type (fits_in_64 ty) (iadd x (sinkable_load_32_64 y)))) +(rule 5 (lower (has_type (fits_in_64 ty) (iadd _ x (sinkable_load_32_64 y)))) (add_mem ty x (sink_load y))) -(rule 12 (lower (has_type (fits_in_64 ty) (iadd (sinkable_load_32_64 x) y))) +(rule 12 (lower (has_type (fits_in_64 ty) (iadd _ (sinkable_load_32_64 x) y))) (add_mem ty y (sink_load x))) ;; Add a register and memory (16-bit types). -(rule 4 (lower (has_type (fits_in_64 ty) (iadd x (sinkable_load_16 y)))) +(rule 4 (lower (has_type (fits_in_64 ty) (iadd _ x (sinkable_load_16 y)))) (add_mem_sext16 ty x (sink_load y))) -(rule 11 (lower (has_type (fits_in_64 ty) (iadd (sinkable_load_16 x) y))) +(rule 11 (lower (has_type (fits_in_64 ty) (iadd _ (sinkable_load_16 x) y))) (add_mem_sext16 ty y (sink_load x))) ;; Add a register and sign-extended memory. -(rule 3 (lower (has_type (fits_in_64 ty) (iadd x (sinkable_sload16 y)))) +(rule 3 (lower (has_type (fits_in_64 ty) (iadd _ x (sinkable_sload16 y)))) (add_mem_sext16 ty x (sink_sload16 y))) -(rule 10 (lower (has_type (fits_in_64 ty) (iadd (sinkable_sload16 x) y))) +(rule 10 (lower (has_type (fits_in_64 ty) (iadd _ (sinkable_sload16 x) y))) (add_mem_sext16 ty y (sink_sload16 x))) -(rule 2 (lower (has_type (fits_in_64 ty) (iadd x (sinkable_sload32 y)))) +(rule 2 (lower (has_type (fits_in_64 ty) (iadd _ x (sinkable_sload32 y)))) (add_mem_sext32 ty x (sink_sload32 y))) -(rule 9 (lower (has_type (fits_in_64 ty) (iadd (sinkable_sload32 x) y))) +(rule 9 (lower (has_type (fits_in_64 ty) (iadd _ (sinkable_sload32 x) y))) (add_mem_sext32 ty y (sink_sload32 x))) ;; Add two vector registers. -(rule 1 (lower (has_type (vr128_ty ty) (iadd x y))) +(rule 1 (lower (has_type (vr128_ty ty) (iadd _ x y))) (vec_add ty x y)) ;;;; Rules for `uadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Add (saturate unsigned) two vector registers. -(rule (lower (has_type (ty_vec128 ty) (uadd_sat x y))) +(rule (lower (has_type (ty_vec128 ty) (uadd_sat _ x y))) (let ((sum Reg (vec_add ty x y))) (vec_or ty sum (vec_cmphl ty x sum)))) @@ -127,7 +127,7 @@ ;;;; Rules for `sadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Add (saturate signed) two vector registers. $I64X2 not supported. -(rule (lower (has_type (ty_vec128 ty) (sadd_sat x y))) +(rule (lower (has_type (ty_vec128 ty) (sadd_sat _ x y))) (vec_pack_ssat (vec_widen_type ty) (vec_add (vec_widen_type ty) (vec_unpacks_high ty x) (vec_unpacks_high ty y)) @@ -138,7 +138,7 @@ ;;;; Rules for `iadd_pairwise` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Lane-wise integer pairwise addition for 8-/16/32-bit vector registers. -(rule (lower (has_type ty @ (multi_lane bits _) (iadd_pairwise x y))) +(rule (lower (has_type ty @ (multi_lane bits _) (iadd_pairwise _ x y))) (let ((size Reg (vec_imm_splat $I8X16 bits))) (vec_pack_lane_order (vec_widen_type ty) (vec_add ty x (vec_lshr_by_byte x size)) @@ -146,9 +146,9 @@ ;; special case for the `i32x4.dot_i16x8_s` wasm instruction (rule 1 (lower - (has_type dst_ty (iadd_pairwise - (imul (swiden_low x @ (value_type src_ty)) (swiden_low y)) - (imul (swiden_high x) (swiden_high y))))) + (has_type dst_ty (iadd_pairwise _ + (imul _ (swiden_low _ x @ (value_type src_ty)) (swiden_low _ y)) + (imul _ (swiden_high _ x) (swiden_high _ y))))) (vec_add dst_ty (vec_smul_even src_ty x y) (vec_smul_odd src_ty x y))) @@ -156,49 +156,49 @@ ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Sub two registers. -(rule 0 (lower (has_type (fits_in_64 ty) (isub x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (isub _ x y))) (sub_reg ty x y)) ;; Sub a register and a sign-extended register. -(rule 8 (lower (has_type (fits_in_64 ty) (isub x (sext32_value y)))) +(rule 8 (lower (has_type (fits_in_64 ty) (isub _ x (sext32_value y)))) (sub_reg_sext32 ty x y)) ;; Sub a register and an immediate (using add of the negated value). -(rule 7 (lower (has_type (fits_in_64 ty) (isub x (i16_from_negated_value y)))) +(rule 7 (lower (has_type (fits_in_64 ty) (isub _ x (i16_from_negated_value y)))) (add_simm16 ty x y)) -(rule 6 (lower (has_type (fits_in_64 ty) (isub x (i32_from_negated_value y)))) +(rule 6 (lower (has_type (fits_in_64 ty) (isub _ x (i32_from_negated_value y)))) (add_simm32 ty x y)) ;; Sub a register and memory (32/64-bit types). -(rule 5 (lower (has_type (fits_in_64 ty) (isub x (sinkable_load_32_64 y)))) +(rule 5 (lower (has_type (fits_in_64 ty) (isub _ x (sinkable_load_32_64 y)))) (sub_mem ty x (sink_load y))) ;; Sub a register and memory (16-bit types). -(rule 4 (lower (has_type (fits_in_64 ty) (isub x (sinkable_load_16 y)))) +(rule 4 (lower (has_type (fits_in_64 ty) (isub _ x (sinkable_load_16 y)))) (sub_mem_sext16 ty x (sink_load y))) ;; Sub a register and sign-extended memory. -(rule 3 (lower (has_type (fits_in_64 ty) (isub x (sinkable_sload16 y)))) +(rule 3 (lower (has_type (fits_in_64 ty) (isub _ x (sinkable_sload16 y)))) (sub_mem_sext16 ty x (sink_sload16 y))) -(rule 2 (lower (has_type (fits_in_64 ty) (isub x (sinkable_sload32 y)))) +(rule 2 (lower (has_type (fits_in_64 ty) (isub _ x (sinkable_sload32 y)))) (sub_mem_sext32 ty x (sink_sload32 y))) ;; Sub two vector registers. -(rule 1 (lower (has_type (vr128_ty ty) (isub x y))) +(rule 1 (lower (has_type (vr128_ty ty) (isub _ x y))) (vec_sub ty x y)) ;;;; Rules for `usub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Add (saturate unsigned) two vector registers. -(rule (lower (has_type (ty_vec128 ty) (usub_sat x y))) +(rule (lower (has_type (ty_vec128 ty) (usub_sat _ x y))) (vec_and ty (vec_sub ty x y) (vec_cmphl ty x y))) ;;;; Rules for `ssub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Add (saturate signed) two vector registers. $I64X2 not supported. -(rule (lower (has_type (ty_vec128 ty) (ssub_sat x y))) +(rule (lower (has_type (ty_vec128 ty) (ssub_sat _ x y))) (vec_pack_ssat (vec_widen_type ty) (vec_sub (vec_widen_type ty) (vec_unpacks_high ty x) (vec_unpacks_high ty y)) @@ -210,23 +210,23 @@ ;; Absolute value of a register. ;; For types smaller than 32-bit, the input value must be sign-extended. -(rule 2 (lower (has_type (fits_in_64 ty) (iabs x))) +(rule 2 (lower (has_type (fits_in_64 ty) (iabs _ x))) (abs_reg (ty_ext32 ty) (put_in_reg_sext32 x))) ;; Absolute value of a sign-extended register. -(rule 3 (lower (has_type (fits_in_64 ty) (iabs (sext32_value x)))) +(rule 3 (lower (has_type (fits_in_64 ty) (iabs _ (sext32_value x)))) (abs_reg_sext32 ty x)) ;; Absolute value of a vector register. -(rule 1 (lower (has_type (ty_vec128 ty) (iabs x))) +(rule 1 (lower (has_type (ty_vec128 ty) (iabs _ x))) (vec_abs ty x)) ;; Absolute value of a 128-bit integer on z17. -(rule 4 (lower (has_type (and (vxrs_ext3_enabled) $I128) (iabs x))) +(rule 4 (lower (has_type (and (vxrs_ext3_enabled) $I128) (iabs _ x))) (vec_abs $I128 x)) ;; Absolute value of a 128-bit integer pre-z17. -(rule 0 (lower (has_type (and (vxrs_ext3_disabled) $I128) (iabs x))) +(rule 0 (lower (has_type (and (vxrs_ext3_disabled) $I128) (iabs _ x))) (let ((zero Reg (vec_imm $I128 0)) (pos Reg x) (neg Reg (vec_sub $I128 zero pos)) @@ -238,30 +238,30 @@ ;;;; Rules for `ineg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Negate a register. -(rule 2 (lower (has_type (fits_in_64 ty) (ineg x))) +(rule 2 (lower (has_type (fits_in_64 ty) (ineg _ x))) (neg_reg ty x)) ;; Negate a sign-extended register. -(rule 3 (lower (has_type (fits_in_64 ty) (ineg (sext32_value x)))) +(rule 3 (lower (has_type (fits_in_64 ty) (ineg _ (sext32_value x)))) (neg_reg_sext32 ty x)) ;; Negate a vector register. -(rule 1 (lower (has_type (ty_vec128 ty) (ineg x))) +(rule 1 (lower (has_type (ty_vec128 ty) (ineg _ x))) (vec_neg ty x)) ;; Negate a 128-bit integer on z17. -(rule 4 (lower (has_type (and (vxrs_ext3_enabled) $I128) (ineg x))) +(rule 4 (lower (has_type (and (vxrs_ext3_enabled) $I128) (ineg _ x))) (vec_neg $I128 x)) ;; Negate a 128-bit integer pre-z17. -(rule 0 (lower (has_type (and (vxrs_ext3_disabled) $I128) (ineg x))) +(rule 0 (lower (has_type (and (vxrs_ext3_disabled) $I128) (ineg _ x))) (vec_sub $I128 (vec_imm $I128 0) x)) ;;;; Rules for `umax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Unsigned maximum of two scalar integers - expand to icmp + select. -(rule 2 (lower (has_type (fits_in_64 ty) (umax x y))) +(rule 2 (lower (has_type (fits_in_64 ty) (umax _ x y))) (let ((x_ext Reg (put_in_reg_zext32 x)) (y_ext Reg (put_in_reg_zext32 y)) (cond ProducesBool (bool (icmpu_reg (ty_ext32 ty) x_ext y_ext) @@ -269,25 +269,25 @@ (select_bool_reg ty cond y_ext x_ext))) ;; Unsigned maximum of two 128-bit integers pre-z17 - expand to icmp + select. -(rule 1 (lower (has_type (and (vxrs_ext3_disabled) $I128) (umax x y))) +(rule 1 (lower (has_type (and (vxrs_ext3_disabled) $I128) (umax _ x y))) (let ((x_reg Reg (put_in_reg x)) (y_reg Reg (put_in_reg y)) (cond ProducesBool (vec_int128_ucmphi y_reg x_reg))) (select_bool_reg $I128 cond y_reg x_reg))) ;; Unsigned maximum of two 128-bit integers on z17. -(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (umax x y))) +(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (umax _ x y))) (vec_umax $I128 x y)) ;; Unsigned maximum of two vector registers. -(rule 0 (lower (has_type (ty_vec128 ty) (umax x y))) +(rule 0 (lower (has_type (ty_vec128 ty) (umax _ x y))) (vec_umax ty x y)) ;;;; Rules for `umin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Unsigned minimum of two scalar integers - expand to icmp + select. -(rule 2 (lower (has_type (fits_in_64 ty) (umin x y))) +(rule 2 (lower (has_type (fits_in_64 ty) (umin _ x y))) (let ((x_ext Reg (put_in_reg_zext32 x)) (y_ext Reg (put_in_reg_zext32 y)) (cond ProducesBool (bool (icmpu_reg (ty_ext32 ty) x_ext y_ext) @@ -295,25 +295,25 @@ (select_bool_reg ty cond y_ext x_ext))) ;; Unsigned maximum of two 128-bit integers pre-z17 - expand to icmp + select. -(rule 1 (lower (has_type (and (vxrs_ext3_disabled) $I128) (umin x y))) +(rule 1 (lower (has_type (and (vxrs_ext3_disabled) $I128) (umin _ x y))) (let ((x_reg Reg (put_in_reg x)) (y_reg Reg (put_in_reg y)) (cond ProducesBool (vec_int128_ucmphi x_reg y_reg))) (select_bool_reg $I128 cond y_reg x_reg))) ;; Unsigned minimum of two 128-bit integers on z17. -(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (umin x y))) +(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (umin _ x y))) (vec_umin $I128 x y)) ;; Unsigned minimum of two vector registers. -(rule 0 (lower (has_type (ty_vec128 ty) (umin x y))) +(rule 0 (lower (has_type (ty_vec128 ty) (umin _ x y))) (vec_umin ty x y)) ;;;; Rules for `smax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Signed maximum of two scalar integers - expand to icmp + select. -(rule 2 (lower (has_type (fits_in_64 ty) (smax x y))) +(rule 2 (lower (has_type (fits_in_64 ty) (smax _ x y))) (let ((x_ext Reg (put_in_reg_sext32 x)) (y_ext Reg (put_in_reg_sext32 y)) (cond ProducesBool (bool (icmps_reg (ty_ext32 ty) x_ext y_ext) @@ -321,25 +321,25 @@ (select_bool_reg ty cond y_ext x_ext))) ;; Signed maximum of two 128-bit integers pre-z17 - expand to icmp + select. -(rule 1 (lower (has_type (and (vxrs_ext3_disabled) $I128) (smax x y))) +(rule 1 (lower (has_type (and (vxrs_ext3_disabled) $I128) (smax _ x y))) (let ((x_reg Reg (put_in_reg x)) (y_reg Reg (put_in_reg y)) (cond ProducesBool (vec_int128_scmphi y_reg x_reg))) (select_bool_reg $I128 cond y_reg x_reg))) ;; Signed maximum of two 128-bit integers on z17. -(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (smax x y))) +(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (smax _ x y))) (vec_smax $I128 x y)) ;; Signed maximum of two vector registers. -(rule (lower (has_type (ty_vec128 ty) (smax x y))) +(rule (lower (has_type (ty_vec128 ty) (smax _ x y))) (vec_smax ty x y)) ;;;; Rules for `smin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Signed minimum of two scalar integers - expand to icmp + select. -(rule 2 (lower (has_type (fits_in_64 ty) (smin x y))) +(rule 2 (lower (has_type (fits_in_64 ty) (smin _ x y))) (let ((x_ext Reg (put_in_reg_sext32 x)) (y_ext Reg (put_in_reg_sext32 y)) (cond ProducesBool (bool (icmps_reg (ty_ext32 ty) x_ext y_ext) @@ -347,75 +347,75 @@ (select_bool_reg ty cond y_ext x_ext))) ;; Signed maximum of two 128-bit integers pre-z17 - expand to icmp + select. -(rule 1 (lower (has_type (and (vxrs_ext3_disabled) $I128) (smin x y))) +(rule 1 (lower (has_type (and (vxrs_ext3_disabled) $I128) (smin _ x y))) (let ((x_reg Reg (put_in_reg x)) (y_reg Reg (put_in_reg y)) (cond ProducesBool (vec_int128_scmphi x_reg y_reg))) (select_bool_reg $I128 cond y_reg x_reg))) ;; Signed minimum of two 128-bit integers on z17. -(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (smin x y))) +(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (smin _ x y))) (vec_smin $I128 x y)) ;; Signed minimum of two vector registers. -(rule (lower (has_type (ty_vec128 ty) (smin x y))) +(rule (lower (has_type (ty_vec128 ty) (smin _ x y))) (vec_smin ty x y)) ;;;; Rules for `avg_round` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Unsigned average of two vector registers. -(rule (lower (has_type (ty_vec128 ty) (avg_round x y))) +(rule (lower (has_type (ty_vec128 ty) (avg_round _ x y))) (vec_uavg ty x y)) ;;;; Rules for `imul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Multiply two registers. -(rule 0 (lower (has_type (fits_in_64 ty) (imul x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (imul _ x y))) (mul_reg ty x y)) ;; Multiply a register and a sign-extended register. -(rule 8 (lower (has_type (fits_in_64 ty) (imul x (sext32_value y)))) +(rule 8 (lower (has_type (fits_in_64 ty) (imul _ x (sext32_value y)))) (mul_reg_sext32 ty x y)) -(rule 15 (lower (has_type (fits_in_64 ty) (imul (sext32_value x) y))) +(rule 15 (lower (has_type (fits_in_64 ty) (imul _ (sext32_value x) y))) (mul_reg_sext32 ty y x)) ;; Multiply a register and an immediate. -(rule 7 (lower (has_type (fits_in_64 ty) (imul x (i16_from_value y)))) +(rule 7 (lower (has_type (fits_in_64 ty) (imul _ x (i16_from_value y)))) (mul_simm16 ty x y)) -(rule 14 (lower (has_type (fits_in_64 ty) (imul (i16_from_value x) y))) +(rule 14 (lower (has_type (fits_in_64 ty) (imul _ (i16_from_value x) y))) (mul_simm16 ty y x)) -(rule 6 (lower (has_type (fits_in_64 ty) (imul x (i32_from_value y)))) +(rule 6 (lower (has_type (fits_in_64 ty) (imul _ x (i32_from_value y)))) (mul_simm32 ty x y)) -(rule 13 (lower (has_type (fits_in_64 ty) (imul (i32_from_value x) y))) +(rule 13 (lower (has_type (fits_in_64 ty) (imul _ (i32_from_value x) y))) (mul_simm32 ty y x)) ;; Multiply a register and memory (32/64-bit types). -(rule 5 (lower (has_type (fits_in_64 ty) (imul x (sinkable_load_32_64 y)))) +(rule 5 (lower (has_type (fits_in_64 ty) (imul _ x (sinkable_load_32_64 y)))) (mul_mem ty x (sink_load y))) -(rule 12 (lower (has_type (fits_in_64 ty) (imul (sinkable_load_32_64 x) y))) +(rule 12 (lower (has_type (fits_in_64 ty) (imul _ (sinkable_load_32_64 x) y))) (mul_mem ty y (sink_load x))) ;; Multiply a register and memory (16-bit types). -(rule 4 (lower (has_type (fits_in_64 ty) (imul x (sinkable_load_16 y)))) +(rule 4 (lower (has_type (fits_in_64 ty) (imul _ x (sinkable_load_16 y)))) (mul_mem_sext16 ty x (sink_load y))) -(rule 11 (lower (has_type (fits_in_64 ty) (imul (sinkable_load_16 x) y))) +(rule 11 (lower (has_type (fits_in_64 ty) (imul _ (sinkable_load_16 x) y))) (mul_mem_sext16 ty y (sink_load x))) ;; Multiply a register and sign-extended memory. -(rule 3 (lower (has_type (fits_in_64 ty) (imul x (sinkable_sload16 y)))) +(rule 3 (lower (has_type (fits_in_64 ty) (imul _ x (sinkable_sload16 y)))) (mul_mem_sext16 ty x (sink_sload16 y))) -(rule 10 (lower (has_type (fits_in_64 ty) (imul (sinkable_sload16 x) y))) +(rule 10 (lower (has_type (fits_in_64 ty) (imul _ (sinkable_sload16 x) y))) (mul_mem_sext16 ty y (sink_sload16 x))) -(rule 2 (lower (has_type (fits_in_64 ty) (imul x (sinkable_sload32 y)))) +(rule 2 (lower (has_type (fits_in_64 ty) (imul _ x (sinkable_sload32 y)))) (mul_mem_sext32 ty x (sink_sload32 y))) -(rule 9 (lower (has_type (fits_in_64 ty) (imul (sinkable_sload32 x) y))) +(rule 9 (lower (has_type (fits_in_64 ty) (imul _ (sinkable_sload32 x) y))) (mul_mem_sext32 ty y (sink_sload32 x))) ;; Multiply two vector registers, using a helper. (decl vec_mul_impl (Type Reg Reg) Reg) -(rule 1 (lower (has_type (vr128_ty ty) (imul x y))) +(rule 1 (lower (has_type (vr128_ty ty) (imul _ x y))) (vec_mul_impl ty x y)) ;; Multiply two vector registers - byte, halfword, and word. @@ -453,46 +453,46 @@ ;; Special-case the lowering of a 128-bit multiply where the operands are sign ;; or zero extended. This maps directly to `umul_wide` and `smul_wide`. -(rule 16 (lower (has_type $I128 (imul (uextend x) (uextend y)))) +(rule 16 (lower (has_type $I128 (imul _ (uextend _ x) (uextend _ y)))) (let ((pair RegPair (umul_wide (put_in_reg_zext64 x) (put_in_reg_zext64 y)))) (mov_to_vec128 $I64X2 (regpair_hi pair) (regpair_lo pair)))) -(rule 16 (lower (has_type $I128 (imul (sextend x) (sextend y)))) +(rule 16 (lower (has_type $I128 (imul _ (sextend _ x) (sextend _ y)))) (let ((pair RegPair (smul_wide (put_in_reg_sext64 x) (put_in_reg_sext64 y)))) (mov_to_vec128 $I64X2 (regpair_hi pair) (regpair_lo pair)))) ;;;; Rules for `umulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Multiply high part unsigned, 8-bit or 16-bit types. (Uses 32-bit multiply.) -(rule -1 (lower (has_type (ty_8_or_16 ty) (umulhi x y))) +(rule -1 (lower (has_type (ty_8_or_16 ty) (umulhi _ x y))) (let ((ext_reg_x Reg (put_in_reg_zext32 x)) (ext_reg_y Reg (put_in_reg_zext32 y)) (ext_mul Reg (mul_reg $I32 ext_reg_x ext_reg_y))) (lshr_imm $I32 ext_mul (ty_bits ty)))) ;; Multiply high part unsigned, 32-bit types. (Uses 64-bit multiply.) -(rule (lower (has_type $I32 (umulhi x y))) +(rule (lower (has_type $I32 (umulhi _ x y))) (let ((ext_reg_x Reg (put_in_reg_zext64 x)) (ext_reg_y Reg (put_in_reg_zext64 y)) (ext_mul Reg (mul_reg $I64 ext_reg_x ext_reg_y))) (lshr_imm $I64 ext_mul 32))) ;; Multiply high part unsigned, 64-bit types. (Uses umul_wide.) -(rule (lower (has_type $I64 (umulhi x y))) +(rule (lower (has_type $I64 (umulhi _ x y))) (let ((pair RegPair (umul_wide x y))) (regpair_hi pair))) ;; Multiply high part unsigned, vector types with 8-, 16-, or 32-bit elements. -(rule (lower (has_type $I8X16 (umulhi x y))) (vec_umulhi $I8X16 x y)) -(rule (lower (has_type $I16X8 (umulhi x y))) (vec_umulhi $I16X8 x y)) -(rule (lower (has_type $I32X4 (umulhi x y))) (vec_umulhi $I32X4 x y)) +(rule (lower (has_type $I8X16 (umulhi _ x y))) (vec_umulhi $I8X16 x y)) +(rule (lower (has_type $I16X8 (umulhi _ x y))) (vec_umulhi $I16X8 x y)) +(rule (lower (has_type $I32X4 (umulhi _ x y))) (vec_umulhi $I32X4 x y)) ;; Multiply high part unsigned, vector types with 64-bit elements on z17. -(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I64X2) (umulhi x y))) (vec_umulhi $I64X2 x y)) +(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I64X2) (umulhi _ x y))) (vec_umulhi $I64X2 x y)) ;; Multiply high part unsigned, vector types with 64-bit elements pre-z17. ;; Has to be scalarized. -(rule (lower (has_type (and (vxrs_ext3_disabled) $I64X2) (umulhi x y))) +(rule (lower (has_type (and (vxrs_ext3_disabled) $I64X2) (umulhi _ x y))) (let ((pair_0 RegPair (umul_wide (vec_extract_lane $I64X2 x 0 (zero_reg)) (vec_extract_lane $I64X2 y 0 (zero_reg)))) (res_0 Reg (regpair_hi pair_0)) @@ -505,35 +505,35 @@ ;;;; Rules for `smulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Multiply high part signed, 8-bit or 16-bit types. (Uses 32-bit multiply.) -(rule -1 (lower (has_type (ty_8_or_16 ty) (smulhi x y))) +(rule -1 (lower (has_type (ty_8_or_16 ty) (smulhi _ x y))) (let ((ext_reg_x Reg (put_in_reg_sext32 x)) (ext_reg_y Reg (put_in_reg_sext32 y)) (ext_mul Reg (mul_reg $I32 ext_reg_x ext_reg_y))) (ashr_imm $I32 ext_mul (ty_bits ty)))) ;; Multiply high part signed, 32-bit types. (Uses 64-bit multiply.) -(rule (lower (has_type $I32 (smulhi x y))) +(rule (lower (has_type $I32 (smulhi _ x y))) (let ((ext_reg_x Reg (put_in_reg_sext64 x)) (ext_reg_y Reg (put_in_reg_sext64 y)) (ext_mul Reg (mul_reg $I64 ext_reg_x ext_reg_y))) (ashr_imm $I64 ext_mul 32))) ;; Multiply high part signed, 64-bit types. (Uses smul_wide.) -(rule (lower (has_type $I64 (smulhi x y))) +(rule (lower (has_type $I64 (smulhi _ x y))) (let ((pair RegPair (smul_wide x y))) (regpair_hi pair))) ;; Multiply high part signed, vector types with 8-, 16-, or 32-bit elements. -(rule (lower (has_type $I8X16 (smulhi x y))) (vec_smulhi $I8X16 x y)) -(rule (lower (has_type $I16X8 (smulhi x y))) (vec_smulhi $I16X8 x y)) -(rule (lower (has_type $I32X4 (smulhi x y))) (vec_smulhi $I32X4 x y)) +(rule (lower (has_type $I8X16 (smulhi _ x y))) (vec_smulhi $I8X16 x y)) +(rule (lower (has_type $I16X8 (smulhi _ x y))) (vec_smulhi $I16X8 x y)) +(rule (lower (has_type $I32X4 (smulhi _ x y))) (vec_smulhi $I32X4 x y)) ;; Multiply high part signed, vector types with 64-bit elements on z17. -(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I64X2) (smulhi x y))) (vec_smulhi $I64X2 x y)) +(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I64X2) (smulhi _ x y))) (vec_smulhi $I64X2 x y)) ;; Multiply high part signed, vector types with 64-bit elements pre-z17. ;; Has to be scalarized. -(rule (lower (has_type (and (vxrs_ext3_disabled) $I64X2) (smulhi x y))) +(rule (lower (has_type (and (vxrs_ext3_disabled) $I64X2) (smulhi _ x y))) (let ((pair_0 RegPair (smul_wide (vec_extract_lane $I64X2 x 0 (zero_reg)) (vec_extract_lane $I64X2 y 0 (zero_reg)))) (res_0 Reg (copy_reg $I64 (regpair_hi pair_0))) @@ -546,7 +546,7 @@ ;;;; Rules for `sqmul_round_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Fixed-point multiplication of two vector registers. -(rule (lower (has_type (ty_vec128 ty) (sqmul_round_sat x y))) +(rule (lower (has_type (ty_vec128 ty) (sqmul_round_sat _ x y))) (vec_pack_ssat (vec_widen_type ty) (sqmul_impl (vec_widen_type ty) (vec_unpacks_high ty x) @@ -584,7 +584,7 @@ ;; and the second variant for 64-bit input types. ;; Implement `udiv`. -(rule (lower (has_type (fits_in_64 ty) (udiv x y))) +(rule (lower (has_type (fits_in_64 ty) (udiv _ x y))) (let ( ;; Look at the divisor to determine whether we need to generate ;; an explicit division-by zero check. @@ -603,7 +603,7 @@ ;; Implement `urem`. Same as `udiv`, but finds the remainder in ;; the high half of the result register pair instead. -(rule (lower (has_type (fits_in_64 ty) (urem x y))) +(rule (lower (has_type (fits_in_64 ty) (urem _ x y))) (let ((ext_x RegPair (regpair (imm (ty_ext32 ty) 0) (put_in_reg_zext32 x))) (ext_y Reg (put_in_reg_zext32 y)) @@ -612,11 +612,11 @@ (regpair_hi pair))) ;; Implement `udiv` for 128-bit integers on z17 (only). -(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I128) (udiv x y))) +(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I128) (udiv _ x y))) (vec_udiv $I128 x y)) ;; Implement `urem` for 128-bit integers on z17 (only). -(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I128) (urem x y))) +(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I128) (urem _ x y))) (vec_urem $I128 x y)) @@ -637,7 +637,7 @@ ;; and the second variant for 64-bit input types. ;; Implement `sdiv`. -(rule (lower (has_type (fits_in_64 ty) (sdiv x y))) +(rule (lower (has_type (fits_in_64 ty) (sdiv _ x y))) (let ( ;; Look at the divisor to determine whether we need to generate ;; explicit division-by-zero and/or integer-overflow checks. @@ -657,7 +657,7 @@ ;; Implement `srem`. Same as `sdiv`, but finds the remainder in ;; the high half of the result register pair instead. Also, handle ;; the integer overflow case differently, see below. -(rule (lower (has_type (fits_in_64 ty) (srem x y))) +(rule (lower (has_type (fits_in_64 ty) (srem _ x y))) (let ((OFcheck bool (div_overflow_check_needed y)) (ext_x Reg (put_in_reg_sext64 x)) (ext_y Reg (put_in_reg_sext32 y)) @@ -711,7 +711,7 @@ ;; This checks that y == -y, by using Not-Xor for bitwise ;; equality, producing all 0b1's (-1u128) when y == -y. ;; Then it uses band to include the x == -1 check as well. - ;; using (band x (bnot (bxor y neg_divison))) vaiant of vec eval + ;; using (band x (bnot (bxor y neg_division))) variant of vec eval (neg_divisor Reg (vec_neg $I128 y)) (reg Reg (vec_eval $I128 0b00001001 x y neg_divisor)) ;; finally, we check that the combination of x & y == -y is -1 @@ -756,13 +756,13 @@ ;; Implement `sdiv` for 128-bit integers on z17 (only). -(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I128) (sdiv x y))) +(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I128) (sdiv _ x y))) (let ((OFcheck bool (div_overflow_check_needed y)) (_ Reg (maybe_trap_if_sdiv_overflow OFcheck $I128 $I128 x y))) (vec_sdiv $I128 x y))) ;; Implement `srem` for 128-bit integers on z17 (only). -(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I128) (srem x y))) +(rule 1 (lower (has_type (and (vxrs_ext3_enabled) $I128) (srem _ x y))) (let ((OFcheck bool (div_overflow_check_needed y)) (top Reg (maybe_avoid_srem_overflow OFcheck $I128 x y))) (vec_srem $I128 top y))) @@ -771,26 +771,26 @@ ;;;; Rules for `ishl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Shift left, shift amount in register. -(rule 0 (lower (has_type (fits_in_64 ty) (ishl x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (ishl _ x y))) (let ((masked_amt Reg (mask_amt_reg ty (amt_reg y)))) (lshl_reg ty x masked_amt))) ;; Shift left, immediate shift amount. -(rule 1 (lower (has_type (fits_in_64 ty) (ishl x (i64_from_value y)))) +(rule 1 (lower (has_type (fits_in_64 ty) (ishl _ x (i64_from_value y)))) (let ((masked_amt u8 (mask_amt_imm ty y))) (lshl_imm ty x masked_amt))) ;; Vector shift left, shift amount in register. -(rule 2 (lower (has_type (ty_vec128 ty) (ishl x y))) +(rule 2 (lower (has_type (ty_vec128 ty) (ishl _ x y))) (vec_lshl_reg ty x (amt_reg y))) ;; Vector shift left, immediate shift amount. -(rule 3 (lower (has_type (ty_vec128 ty) (ishl x (i64_from_value y)))) +(rule 3 (lower (has_type (ty_vec128 ty) (ishl _ x (i64_from_value y)))) (let ((masked_amt u8 (mask_amt_imm ty y))) (vec_lshl_imm ty x masked_amt))) ;; 128-bit vector shift left. -(rule 4 (lower (has_type $I128 (ishl x y))) +(rule 4 (lower (has_type $I128 (ishl _ x y))) (let ((amt Reg (amt_vr y))) (vec_lshl_by_bit (vec_lshl_by_byte x amt) amt))) @@ -799,29 +799,29 @@ ;; Shift right logical, shift amount in register. ;; For types smaller than 32-bit, the input value must be zero-extended. -(rule 0 (lower (has_type (fits_in_64 ty) (ushr x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (ushr _ x y))) (let ((ext_reg Reg (put_in_reg_zext32 x)) (masked_amt Reg (mask_amt_reg ty (amt_reg y)))) (lshr_reg (ty_ext32 ty) ext_reg masked_amt))) ;; Shift right logical, immediate shift amount. ;; For types smaller than 32-bit, the input value must be zero-extended. -(rule 1 (lower (has_type (fits_in_64 ty) (ushr x (i64_from_value y)))) +(rule 1 (lower (has_type (fits_in_64 ty) (ushr _ x (i64_from_value y)))) (let ((ext_reg Reg (put_in_reg_zext32 x)) (masked_amt u8 (mask_amt_imm ty y))) (lshr_imm (ty_ext32 ty) ext_reg masked_amt))) ;; Vector shift right logical, shift amount in register. -(rule 2 (lower (has_type (ty_vec128 ty) (ushr x y))) +(rule 2 (lower (has_type (ty_vec128 ty) (ushr _ x y))) (vec_lshr_reg ty x (amt_reg y))) ;; Vector shift right logical, immediate shift amount. -(rule 3 (lower (has_type (ty_vec128 ty) (ushr x (i64_from_value y)))) +(rule 3 (lower (has_type (ty_vec128 ty) (ushr _ x (i64_from_value y)))) (let ((masked_amt u8 (mask_amt_imm ty y))) (vec_lshr_imm ty x masked_amt))) ;; 128-bit vector shift right logical. -(rule 4 (lower (has_type $I128 (ushr x y))) +(rule 4 (lower (has_type $I128 (ushr _ x y))) (let ((amt Reg (amt_vr y))) (vec_lshr_by_bit (vec_lshr_by_byte x amt) amt))) @@ -830,29 +830,29 @@ ;; Shift right arithmetic, shift amount in register. ;; For types smaller than 32-bit, the input value must be sign-extended. -(rule 0 (lower (has_type (fits_in_64 ty) (sshr x y))) +(rule 0 (lower (has_type (fits_in_64 ty) (sshr _ x y))) (let ((ext_reg Reg (put_in_reg_sext32 x)) (masked_amt Reg (mask_amt_reg ty (amt_reg y)))) (ashr_reg (ty_ext32 ty) ext_reg masked_amt))) ;; Shift right arithmetic, immediate shift amount. ;; For types smaller than 32-bit, the input value must be sign-extended. -(rule 1 (lower (has_type (fits_in_64 ty) (sshr x (i64_from_value y)))) +(rule 1 (lower (has_type (fits_in_64 ty) (sshr _ x (i64_from_value y)))) (let ((ext_reg Reg (put_in_reg_sext32 x)) (masked_amt u8 (mask_amt_imm ty y))) (ashr_imm (ty_ext32 ty) ext_reg masked_amt))) ;; Vector shift right arithmetic, shift amount in register. -(rule 2 (lower (has_type (ty_vec128 ty) (sshr x y))) +(rule 2 (lower (has_type (ty_vec128 ty) (sshr _ x y))) (vec_ashr_reg ty x (amt_reg y))) ;; Vector shift right arithmetic, immediate shift amount. -(rule 3 (lower (has_type (ty_vec128 ty) (sshr x (i64_from_value y)))) +(rule 3 (lower (has_type (ty_vec128 ty) (sshr _ x (i64_from_value y)))) (let ((masked_amt u8 (mask_amt_imm ty y))) (vec_ashr_imm ty x masked_amt))) ;; 128-bit vector shift right arithmetic. -(rule 4 (lower (has_type $I128 (sshr x y))) +(rule 4 (lower (has_type $I128 (sshr _ x y))) (let ((amt Reg (amt_vr y))) (vec_ashr_by_bit (vec_ashr_by_byte x amt) amt))) @@ -860,17 +860,17 @@ ;;;; Rules for `rotl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Rotate left, shift amount in register. 32-bit or 64-bit types. -(rule 0 (lower (has_type (ty_32_or_64 ty) (rotl x y))) +(rule 0 (lower (has_type (ty_32_or_64 ty) (rotl _ x y))) (rot_reg ty x (amt_reg y))) ;; Rotate left arithmetic, immediate shift amount. 32-bit or 64-bit types. -(rule 1 (lower (has_type (ty_32_or_64 ty) (rotl x (i64_from_value y)))) +(rule 1 (lower (has_type (ty_32_or_64 ty) (rotl _ x (i64_from_value y)))) (let ((masked_amt u8 (mask_amt_imm ty y))) (rot_imm ty x masked_amt))) ;; Rotate left, shift amount in register. 8-bit or 16-bit types. ;; Implemented via a pair of 32-bit shifts on the zero-extended input. -(rule 2 (lower (has_type (ty_8_or_16 ty) (rotl x y))) +(rule 2 (lower (has_type (ty_8_or_16 ty) (rotl _ x y))) (let ((ext_reg Reg (put_in_reg_zext32 x)) (ext_ty Type (ty_ext32 ty)) (pos_amt Reg (amt_reg y)) @@ -882,7 +882,7 @@ ;; Rotate left, immediate shift amount. 8-bit or 16-bit types. ;; Implemented via a pair of 32-bit shifts on the zero-extended input. -(rule 3 (lower (has_type (ty_8_or_16 ty) (rotl x (and (i64_from_value pos_amt) +(rule 3 (lower (has_type (ty_8_or_16 ty) (rotl _ x (and (i64_from_value pos_amt) (i64_from_negated_value neg_amt))))) (let ((ext_reg Reg (put_in_reg_zext32 x)) (ext_ty Type (ty_ext32 ty)) @@ -892,17 +892,17 @@ (lshr_imm ext_ty ext_reg masked_neg_amt)))) ;; Vector rotate left, shift amount in register. -(rule 4 (lower (has_type (ty_vec128 ty) (rotl x y))) +(rule 4 (lower (has_type (ty_vec128 ty) (rotl _ x y))) (vec_rot_reg ty x (amt_reg y))) ;; Vector rotate left, immediate shift amount. -(rule 5 (lower (has_type (ty_vec128 ty) (rotl x (i64_from_value y)))) +(rule 5 (lower (has_type (ty_vec128 ty) (rotl _ x (i64_from_value y)))) (let ((masked_amt u8 (mask_amt_imm ty y))) (vec_rot_imm ty x masked_amt))) ;; 128-bit full vector rotate left. ;; Implemented via a pair of 128-bit full vector shifts. -(rule 6 (lower (has_type $I128 (rotl x y))) +(rule 6 (lower (has_type $I128 (rotl _ x y))) (let ((x_reg Reg x) (pos_amt Reg (amt_vr y)) (neg_amt Reg (vec_neg $I8X16 pos_amt))) @@ -915,19 +915,19 @@ ;; Rotate right, shift amount in register. 32-bit or 64-bit types. ;; Implemented as rotate left with negated rotate amount. -(rule 0 (lower (has_type (ty_32_or_64 ty) (rotr x y))) +(rule 0 (lower (has_type (ty_32_or_64 ty) (rotr _ x y))) (let ((negated_amt Reg (neg_reg $I32 (amt_reg y)))) (rot_reg ty x negated_amt))) ;; Rotate right arithmetic, immediate shift amount. 32-bit or 64-bit types. ;; Implemented as rotate left with negated rotate amount. -(rule 1 (lower (has_type (ty_32_or_64 ty) (rotr x (i64_from_negated_value y)))) +(rule 1 (lower (has_type (ty_32_or_64 ty) (rotr _ x (i64_from_negated_value y)))) (let ((negated_amt u8 (mask_amt_imm ty y))) (rot_imm ty x negated_amt))) ;; Rotate right, shift amount in register. 8-bit or 16-bit types. ;; Implemented as rotate left with negated rotate amount. -(rule 2 (lower (has_type (ty_8_or_16 ty) (rotr x y))) +(rule 2 (lower (has_type (ty_8_or_16 ty) (rotr _ x y))) (let ((ext_reg Reg (put_in_reg_zext32 x)) (ext_ty Type (ty_ext32 ty)) (pos_amt Reg (amt_reg y)) @@ -939,7 +939,7 @@ ;; Rotate right, immediate shift amount. 8-bit or 16-bit types. ;; Implemented as rotate left with negated rotate amount. -(rule 3 (lower (has_type (ty_8_or_16 ty) (rotr x (and (i64_from_value pos_amt) +(rule 3 (lower (has_type (ty_8_or_16 ty) (rotr _ x (and (i64_from_value pos_amt) (i64_from_negated_value neg_amt))))) (let ((ext_reg Reg (put_in_reg_zext32 x)) (ext_ty Type (ty_ext32 ty)) @@ -950,19 +950,19 @@ ;; Vector rotate right, shift amount in register. ;; Implemented as rotate left with negated rotate amount. -(rule 4 (lower (has_type (ty_vec128 ty) (rotr x y))) +(rule 4 (lower (has_type (ty_vec128 ty) (rotr _ x y))) (let ((negated_amt Reg (neg_reg $I32 (amt_reg y)))) (vec_rot_reg ty x negated_amt))) ;; Vector rotate right, immediate shift amount. ;; Implemented as rotate left with negated rotate amount. -(rule 5 (lower (has_type (ty_vec128 ty) (rotr x (i64_from_negated_value y)))) +(rule 5 (lower (has_type (ty_vec128 ty) (rotr _ x (i64_from_negated_value y)))) (let ((negated_amt u8 (mask_amt_imm ty y))) (vec_rot_imm ty x negated_amt))) ;; 128-bit full vector rotate right. ;; Implemented via a pair of 128-bit full vector shifts. -(rule 6 (lower (has_type $I128 (rotr x y))) +(rule 6 (lower (has_type $I128 (rotr _ x y))) (let ((x_reg Reg x) (pos_amt Reg (amt_vr y)) (neg_amt Reg (vec_neg $I8X16 pos_amt))) @@ -974,156 +974,156 @@ ;;;; Rules for `ireduce` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Up to 64-bit source type: Always a no-op. -(rule 1 (lower (ireduce x @ (value_type (fits_in_64 _ty)))) +(rule 1 (lower (ireduce _ x @ (value_type (fits_in_64 _ty)))) x) ;; 128-bit source type: Extract the low half. -(rule (lower (ireduce x @ (value_type (vr128_ty _ty)))) +(rule (lower (ireduce _ x @ (value_type (vr128_ty _ty)))) (vec_extract_lane $I64X2 x 1 (zero_reg))) ;;;; Rules for `uextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 16- or 32-bit target types. -(rule 1 (lower (has_type (gpr32_ty _ty) (uextend x))) +(rule 1 (lower (has_type (gpr32_ty _ty) (uextend _ x))) (put_in_reg_zext32 x)) ;; 64-bit target types. -(rule 2 (lower (has_type (gpr64_ty _ty) (uextend x))) +(rule 2 (lower (has_type (gpr64_ty _ty) (uextend _ x))) (put_in_reg_zext64 x)) ;; 128-bit target types. -(rule (lower (has_type $I128 (uextend x @ (value_type $I8)))) +(rule (lower (has_type $I128 (uextend _ x @ (value_type $I8)))) (vec_insert_lane $I8X16 (vec_imm $I128 0) x 15 (zero_reg))) -(rule (lower (has_type $I128 (uextend x @ (value_type $I16)))) +(rule (lower (has_type $I128 (uextend _ x @ (value_type $I16)))) (vec_insert_lane $I16X8 (vec_imm $I128 0) x 7 (zero_reg))) -(rule (lower (has_type $I128 (uextend x @ (value_type $I32)))) +(rule (lower (has_type $I128 (uextend _ x @ (value_type $I32)))) (vec_insert_lane $I32X4 (vec_imm $I128 0) x 3 (zero_reg))) -(rule (lower (has_type $I128 (uextend x @ (value_type $I64)))) +(rule (lower (has_type $I128 (uextend _ x @ (value_type $I64)))) (vec_insert_lane $I64X2 (vec_imm $I128 0) x 1 (zero_reg))) ;;;; Rules for `sextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 16- or 32-bit target types. -(rule 1 (lower (has_type (gpr32_ty _ty) (sextend x))) +(rule 1 (lower (has_type (gpr32_ty _ty) (sextend _ x))) (put_in_reg_sext32 x)) ;; 64-bit target types. -(rule 2 (lower (has_type (gpr64_ty _ty) (sextend x))) +(rule 2 (lower (has_type (gpr64_ty _ty) (sextend _ x))) (put_in_reg_sext64 x)) ;; 128-bit target types. -(rule (lower (has_type $I128 (sextend x))) +(rule (lower (has_type $I128 (sextend _ x))) (let ((x_ext Reg (put_in_reg_sext64 x))) (mov_to_vec128 $I128 (ashr_imm $I64 x_ext 63) x_ext))) ;;;; Rules for `snarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (snarrow x @ (value_type (ty_vec128 ty)) y)) +(rule (lower (snarrow _ x @ (value_type (ty_vec128 ty)) y)) (vec_pack_ssat_lane_order ty x y)) ;;;; Rules for `uunarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (uunarrow x @ (value_type (ty_vec128 ty)) y)) +(rule (lower (uunarrow _ x @ (value_type (ty_vec128 ty)) y)) (vec_pack_usat_lane_order ty x y)) ;;;; Rules for `unarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (unarrow x @ (value_type (ty_vec128 ty)) y)) +(rule (lower (unarrow _ x @ (value_type (ty_vec128 ty)) y)) (let ((zero Reg (vec_imm ty 0))) (vec_pack_usat_lane_order ty (vec_smax ty x zero) (vec_smax ty y zero)))) ;;;; Rules for `swiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (swiden_low x @ (value_type (ty_vec128 ty)))) +(rule (lower (swiden_low _ x @ (value_type (ty_vec128 ty)))) (vec_unpacks_low_lane_order ty x)) ;;;; Rules for `swiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (swiden_high x @ (value_type (ty_vec128 ty)))) +(rule (lower (swiden_high _ x @ (value_type (ty_vec128 ty)))) (vec_unpacks_high_lane_order ty x)) ;;;; Rules for `uwiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (uwiden_low x @ (value_type (ty_vec128 ty)))) +(rule (lower (uwiden_low _ x @ (value_type (ty_vec128 ty)))) (vec_unpacku_low_lane_order ty x)) ;;;; Rules for `uwiden_high` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (uwiden_high x @ (value_type (ty_vec128 ty)))) +(rule (lower (uwiden_high _ x @ (value_type (ty_vec128 ty)))) (vec_unpacku_high_lane_order ty x)) ;;;; Rules for `bnot` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; z15 version using a single instruction (NOR). -(rule 2 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bnot x))) +(rule 2 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bnot _ x))) (let ((rx Reg x)) (not_or_reg ty rx rx))) ;; z14 version using XOR with -1. -(rule 1 (lower (has_type (and (mie3_disabled) (fits_in_64 ty)) (bnot x))) +(rule 1 (lower (has_type (and (mie3_disabled) (fits_in_64 ty)) (bnot _ x))) (not_reg ty x)) ;; Vector version using vector NOR. -(rule (lower (has_type (vr128_ty ty) (bnot x))) +(rule (lower (has_type (vr128_ty ty) (bnot _ x))) (vec_not ty x)) ;; Float version using vector NOR. -(rule 5 (lower (has_type (ty_scalar_float _) (bnot x))) +(rule 5 (lower (has_type (ty_scalar_float _) (bnot _ x))) (vec_not $F64X2 x)) ;; With z15 (bnot (bxor ...)) can be a single instruction, similar to the ;; (bxor _ (bnot _)) lowering. -(rule 3 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bnot (bxor x y)))) +(rule 3 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bnot _ (bxor _ x y)))) (not_xor_reg ty x y)) ;; Combine a not/xor operation of vector types into one. -(rule 4 (lower (has_type (vr128_ty ty) (bnot (bxor x y)))) +(rule 4 (lower (has_type (vr128_ty ty) (bnot _ (bxor _ x y)))) (vec_not_xor ty x y)) ;;;; Rules for `band` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; And two registers. -(rule -1 (lower (has_type (fits_in_64 ty) (band x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (band _ x y))) (and_reg ty x y)) ;; And a register and an immediate. -(rule 5 (lower (has_type (fits_in_64 ty) (band x (uimm16shifted_from_inverted_value y)))) +(rule 5 (lower (has_type (fits_in_64 ty) (band _ x (uimm16shifted_from_inverted_value y)))) (and_uimm16shifted ty x y)) -(rule 6 (lower (has_type (fits_in_64 ty) (band (uimm16shifted_from_inverted_value x) y))) +(rule 6 (lower (has_type (fits_in_64 ty) (band _ (uimm16shifted_from_inverted_value x) y))) (and_uimm16shifted ty y x)) -(rule 3 (lower (has_type (fits_in_64 ty) (band x (uimm32shifted_from_inverted_value y)))) +(rule 3 (lower (has_type (fits_in_64 ty) (band _ x (uimm32shifted_from_inverted_value y)))) (and_uimm32shifted ty x y)) -(rule 4 (lower (has_type (fits_in_64 ty) (band (uimm32shifted_from_inverted_value x) y))) +(rule 4 (lower (has_type (fits_in_64 ty) (band _ (uimm32shifted_from_inverted_value x) y))) (and_uimm32shifted ty y x)) ;; And a register and memory (32/64-bit types). -(rule 1 (lower (has_type (fits_in_64 ty) (band x (sinkable_load_32_64 y)))) +(rule 1 (lower (has_type (fits_in_64 ty) (band _ x (sinkable_load_32_64 y)))) (and_mem ty x (sink_load y))) -(rule 2 (lower (has_type (fits_in_64 ty) (band (sinkable_load_32_64 x) y))) +(rule 2 (lower (has_type (fits_in_64 ty) (band _ (sinkable_load_32_64 x) y))) (and_mem ty y (sink_load x))) ;; And two vector registers. -(rule 0 (lower (has_type (vr128_ty ty) (band x y))) +(rule 0 (lower (has_type (vr128_ty ty) (band _ x y))) (vec_and ty x y)) ;; And two float registers, using vector overlay. -(rule 11 (lower (has_type (ty_scalar_float _) (band x y))) +(rule 11 (lower (has_type (ty_scalar_float _) (band _ x y))) (vec_and $F64X2 x y)) -(rule 12 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (band x y) z))) +(rule 12 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (band _ x y) z))) (vec_eval ty 0b00000001 x y z)) -(rule 13 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band x (band y z)))) +(rule 13 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ x (band _ y z)))) (vec_eval ty 0b00000001 x y z)) ;; Specialized lowerings for `(band x (bnot y))` which is additionally produced @@ -1131,101 +1131,101 @@ ;; forms early on. ;; z15 version using a single instruction. -(rule 7 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (band x (bnot y)))) +(rule 7 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (band _ x (bnot _ y)))) (and_not_reg ty x y)) -(rule 8 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (band (bnot y) x))) +(rule 8 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (band _ (bnot _ y) x))) (and_not_reg ty x y)) ;; And-not two vector registers. -(rule 9 (lower (has_type (vr128_ty ty) (band x (bnot y)))) +(rule 9 (lower (has_type (vr128_ty ty) (band _ x (bnot _ y)))) (vec_and_not ty x y)) -(rule 10 (lower (has_type (vr128_ty ty) (band (bnot y) x))) +(rule 10 (lower (has_type (vr128_ty ty) (band _ (bnot _ y) x))) (vec_and_not ty x y)) ;; And-not three vector registers. -(rule 14 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (band (bnot x) y) z))) +(rule 14 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (band _ (bnot _ x) y) z))) (vec_eval ty 0b00010000 x y z)) -(rule 15 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (bnot x) (band y z)))) +(rule 15 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (bnot _ x) (band _ y z)))) (vec_eval ty 0b00010000 x y z)) -(rule 16 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (band x y) (bnot z)))) +(rule 16 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (band _ x y) (bnot _ z)))) (vec_eval ty 0b00000010 x y z)) -(rule 17 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band x (band y (bnot z))))) +(rule 17 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ x (band _ y (bnot _ z))))) (vec_eval ty 0b00000010 x y z)) -(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (band x (bnot y)) z))) +(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (band _ x (bnot _ y)) z))) (vec_eval ty 0b00000100 y x z)) -(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band x (band (bnot y) z)))) +(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ x (band _ (bnot _ y) z)))) (vec_eval ty 0b00000100 z x y)) ;; Not-and three vector registers -(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (band (band x y) z)))) +(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (band _ (band _ x y) z)))) (vec_eval ty 0b11111110 x y z)) -(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (band x (band y z))))) +(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (band _ x (band _ y z))))) (vec_eval ty 0b11111110 x y z)) ;; And-Nand three vector registers -(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (bnot (band x y)) z))) +(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (bnot _ (band _ x y)) z))) (vec_eval ty 0b01010100 x y z)) -(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band x (bnot (band y z))))) +(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ x (bnot _ (band _ y z))))) (vec_eval ty 0b00001110 x y z)) ;; And-Or 3 vector registers -(rule 22 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band x (bor y z)))) +(rule 22 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ x (bor _ y z)))) (vec_eval ty 0b00000111 x y z)) -(rule 23 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (bor x y) z))) +(rule 23 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (bor _ x y) z))) (vec_eval ty 0b00010101 x y z)) ;; And-Nor 3 vector registers -(rule 24 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band x (bnot (bor y z))))) +(rule 24 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ x (bnot _ (bor _ y z))))) (vec_eval ty 0b00001000 x y z)) -(rule 25 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (bnot (bor x y)) z))) +(rule 25 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (bnot _ (bor _ x y)) z))) (vec_eval ty 0b01000000 x y z)) ;; And-Xor 3 vector registers -(rule 26 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band x (bxor y z)))) +(rule 26 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ x (bxor _ y z)))) (vec_eval ty 0b00000110 x y z)) -(rule 27 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (bxor x y) z))) +(rule 27 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (bxor _ x y) z))) (vec_eval ty 0b00010100 x y z)) ;; And-Nxor 3 vector registers -(rule 28 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band x (bnot (bxor y z))))) +(rule 28 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ x (bnot _ (bxor _ y z))))) (vec_eval ty 0b00001001 x y z)) -(rule 29 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band (bnot (bxor x y)) z))) +(rule 29 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (band _ (bnot _ (bxor _ x y)) z))) (vec_eval ty 0b01000001 x y z)) ;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Or two registers. -(rule -1 (lower (has_type (fits_in_64 ty) (bor x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (bor _ x y))) (or_reg ty x y)) ;; Or a register and an immediate. -(rule 5 (lower (has_type (fits_in_64 ty) (bor x (uimm16shifted_from_value y)))) +(rule 5 (lower (has_type (fits_in_64 ty) (bor _ x (uimm16shifted_from_value y)))) (or_uimm16shifted ty x y)) -(rule 6 (lower (has_type (fits_in_64 ty) (bor (uimm16shifted_from_value x) y))) +(rule 6 (lower (has_type (fits_in_64 ty) (bor _ (uimm16shifted_from_value x) y))) (or_uimm16shifted ty y x)) -(rule 3 (lower (has_type (fits_in_64 ty) (bor x (uimm32shifted_from_value y)))) +(rule 3 (lower (has_type (fits_in_64 ty) (bor _ x (uimm32shifted_from_value y)))) (or_uimm32shifted ty x y)) -(rule 4 (lower (has_type (fits_in_64 ty) (bor (uimm32shifted_from_value x) y))) +(rule 4 (lower (has_type (fits_in_64 ty) (bor _ (uimm32shifted_from_value x) y))) (or_uimm32shifted ty y x)) ;; Or a register and memory (32/64-bit types). -(rule 1 (lower (has_type (fits_in_64 ty) (bor x (sinkable_load_32_64 y)))) +(rule 1 (lower (has_type (fits_in_64 ty) (bor _ x (sinkable_load_32_64 y)))) (or_mem ty x (sink_load y))) -(rule 2 (lower (has_type (fits_in_64 ty) (bor (sinkable_load_32_64 x) y))) +(rule 2 (lower (has_type (fits_in_64 ty) (bor _ (sinkable_load_32_64 x) y))) (or_mem ty y (sink_load x))) ;; Or two vector registers. -(rule 0 (lower (has_type (vr128_ty ty) (bor x y))) +(rule 0 (lower (has_type (vr128_ty ty) (bor _ x y))) (vec_or ty x y)) ;; Or two floating registers, using vector overlay -(rule 11 (lower (has_type (ty_scalar_float _) (bor x y))) +(rule 11 (lower (has_type (ty_scalar_float _) (bor _ x y))) (vec_or $F64X2 x y)) ;; Or three vector registers. -(rule 12 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (bor x y) z))) +(rule 12 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (bor _ x y) z))) (vec_eval ty 0b01111111 x y z)) -(rule 13 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor x (bor y z)))) +(rule 13 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ x (bor _ y z)))) (vec_eval ty 0b01111111 x y z)) ;; Specialized lowerings for `(bor x (bnot y))` which is additionally produced @@ -1233,115 +1233,115 @@ ;; forms early on. ;; z15 version using a single instruction. -(rule 7 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bor x (bnot y)))) +(rule 7 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bor _ x (bnot _ y)))) (or_not_reg ty x y)) -(rule 8 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bor (bnot y) x))) +(rule 8 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bor _ (bnot _ y) x))) (or_not_reg ty x y)) ;; Or-not two vector registers. -(rule 9 (lower (has_type (vr128_ty ty) (bor x (bnot y)))) +(rule 9 (lower (has_type (vr128_ty ty) (bor _ x (bnot _ y)))) (vec_or_not ty x y)) -(rule 10 (lower (has_type (vr128_ty ty) (bor (bnot y) x))) +(rule 10 (lower (has_type (vr128_ty ty) (bor _ (bnot _ y) x))) (vec_or_not ty x y)) ;; 3-input bor with a single not -(rule 14 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (bor (bnot x) y) z))) +(rule 14 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (bor _ (bnot _ x) y) z))) (vec_eval ty 0b11110111 x y z)) -(rule 15 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (bnot x) (bor y z)))) +(rule 15 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (bnot _ x) (bor _ y z)))) (vec_eval ty 0b11110111 x y z)) -(rule 16 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (bor x (bnot y)) z))) +(rule 16 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (bor _ x (bnot _ y)) z))) (vec_eval ty 0b11011111 x y z)) -(rule 17 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor x (bor (bnot y) z)))) +(rule 17 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ x (bor _ (bnot _ y) z)))) (vec_eval ty 0b11011111 x y z)) -(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (bor x y) (bnot z)))) +(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (bor _ x y) (bnot _ z)))) (vec_eval ty 0b10111111 x y z)) -(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor x (bor y (bnot z))))) +(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ x (bor _ y (bnot _ z))))) (vec_eval ty 0b10111111 x y z)) ;; 3-input bnor -(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor (bor x y) z)))) +(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ (bor _ x y) z)))) (vec_eval ty 0b10000000 x y z)) -(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor x (bor y z))))) +(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ x (bor _ y z))))) (vec_eval ty 0b10000000 x y z)) ;; Or-Nor -(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (bnot (bor x y)) z))) +(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (bnot _ (bor _ x y)) z))) (vec_eval ty 0b11010101 x y z)) -(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor x (bnot (bor y z))))) +(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ x (bnot _ (bor _ y z))))) (vec_eval ty 0b10001111 x y z)) ;; Or-And -(rule 22 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (band x y) z))) +(rule 22 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (band _ x y) z))) (vec_eval ty 0b01010111 x y z)) -(rule 23 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor x (band y z)))) +(rule 23 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ x (band _ y z)))) (vec_eval ty 0b00011111 x y z)) ;; Or-Nand -(rule 24 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (bnot (band x y)) z))) +(rule 24 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (bnot _ (band _ x y)) z))) (vec_eval ty 0b11111101 x y z)) -(rule 25 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor x (bnot (band y z))))) +(rule 25 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ x (bnot _ (band _ y z))))) (vec_eval ty 0b11101111 x y z)) ;; Or-Xor -(rule 26 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (bxor x y) z))) +(rule 26 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (bxor _ x y) z))) (vec_eval ty 0b01111101 x y z)) -(rule 27 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor x (bxor y z)))) +(rule 27 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ x (bxor _ y z)))) (vec_eval ty 0b01101111 x y z)) ;; Or-Nxor -(rule 28 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor (bnot (bxor x y)) z))) +(rule 28 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ (bnot _ (bxor _ x y)) z))) (vec_eval ty 0b11010111 x y z)) -(rule 29 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor x (bnot (bxor y z))))) +(rule 29 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bor _ x (bnot _ (bxor _ y z))))) (vec_eval ty 0b10011111 x y z)) ;; Nor-And -(rule 30 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor (band x y) z)))) +(rule 30 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ (band _ x y) z)))) (vec_eval ty 0b10101000 x y z)) -(rule 31 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor x (band y z))))) +(rule 31 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ x (band _ y z))))) (vec_eval ty 0b11100000 x y z)) ;; Nor-Nand -(rule 32 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor (bnot (band x y)) z)))) +(rule 32 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ (bnot _ (band _ x y)) z)))) (vec_eval ty 0b00000010 x y z)) -(rule 33 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor x (bnot (band y z)))))) +(rule 33 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ x (bnot _ (band _ y z)))))) (vec_eval ty 0b00010000 x y z)) ;; Nor-Xor -(rule 34 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor (bxor x y) z)))) +(rule 34 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ (bxor _ x y) z)))) (vec_eval ty 0b10000010 x y z)) -(rule 35 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor x (bxor y z))))) +(rule 35 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ x (bxor _ y z))))) (vec_eval ty 0b10010000 x y z)) ;; Nor-Nxor -(rule 36 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor (bnot (bxor x y)) z)))) +(rule 36 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ (bnot _ (bxor _ x y)) z)))) (vec_eval ty 0b00101000 x y z)) -(rule 37 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bor x (bnot (bxor y z)))))) +(rule 37 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bor _ x (bnot _ (bxor _ y z)))))) (vec_eval ty 0b01100000 x y z)) ;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Xor two registers. -(rule -1 (lower (has_type (fits_in_64 ty) (bxor x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (bxor _ x y))) (xor_reg ty x y)) ;; Xor a register and an immediate. -(rule 3 (lower (has_type (fits_in_64 ty) (bxor x (uimm32shifted_from_value y)))) +(rule 3 (lower (has_type (fits_in_64 ty) (bxor _ x (uimm32shifted_from_value y)))) (xor_uimm32shifted ty x y)) -(rule 4 (lower (has_type (fits_in_64 ty) (bxor (uimm32shifted_from_value x) y))) +(rule 4 (lower (has_type (fits_in_64 ty) (bxor _ (uimm32shifted_from_value x) y))) (xor_uimm32shifted ty y x)) ;; Xor a register and memory (32/64-bit types). -(rule 1 (lower (has_type (fits_in_64 ty) (bxor x (sinkable_load_32_64 y)))) +(rule 1 (lower (has_type (fits_in_64 ty) (bxor _ x (sinkable_load_32_64 y)))) (xor_mem ty x (sink_load y))) -(rule 2 (lower (has_type (fits_in_64 ty) (bxor (sinkable_load_32_64 x) y))) +(rule 2 (lower (has_type (fits_in_64 ty) (bxor _ (sinkable_load_32_64 x) y))) (xor_mem ty y (sink_load x))) ;; Xor two vector registers. -(rule 0 (lower (has_type (vr128_ty ty) (bxor x y))) +(rule 0 (lower (has_type (vr128_ty ty) (bxor _ x y))) (vec_xor ty x y)) ;; Xor two floating registers, using vector overlay -(rule 9 (lower (has_type (ty_scalar_float _) (bxor x y))) +(rule 9 (lower (has_type (ty_scalar_float _) (bxor _ x y))) (vec_xor $F64X2 x y)) ;; Specialized lowerings for `(bxor x (bnot y))` which is additionally produced @@ -1349,134 +1349,134 @@ ;; forms early on. ;; z15 version using a single instruction. -(rule 5 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bxor x (bnot y)))) +(rule 5 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bxor _ x (bnot _ y)))) (not_xor_reg ty x y)) -(rule 6 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bxor (bnot y) x))) +(rule 6 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bxor _ (bnot _ y) x))) (not_xor_reg ty x y)) ;; Xor-not two vector registers. -(rule 7 (lower (has_type (vr128_ty ty) (bxor x (bnot y)))) +(rule 7 (lower (has_type (vr128_ty ty) (bxor _ x (bnot _ y)))) (vec_not_xor ty x y)) -(rule 8 (lower (has_type (vr128_ty ty) (bxor (bnot y) x))) +(rule 8 (lower (has_type (vr128_ty ty) (bxor _ (bnot _ y) x))) (vec_not_xor ty x y)) ;; 3-input Xor -(rule 10 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor (bxor x y) z))) +(rule 10 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ (bxor _ x y) z))) (vec_eval ty 0b01101001 x y z)) -(rule 11 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor x (bxor y z)))) +(rule 11 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ x (bxor _ y z)))) (vec_eval ty 0b01101001 x y z)) ;; Xor-And -(rule 12 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor (band x y) z))) +(rule 12 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ (band _ x y) z))) (vec_eval ty 0b01010110 x y z)) -(rule 13 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor x (band y z)))) +(rule 13 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ x (band _ y z)))) (vec_eval ty 0b00011110 x y z)) ;; Xor-Nand -(rule 14 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor (bnot (band x y)) z))) +(rule 14 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ (bnot _ (band _ x y)) z))) (vec_eval ty 0b10101001 x y z)) -(rule 15 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor x (bnot (band y z))))) +(rule 15 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ x (bnot _ (band _ y z))))) (vec_eval ty 0b11100001 x y z)) ;; Xor-Or -(rule 16 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor (bor x y) z))) +(rule 16 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ (bor _ x y) z))) (vec_eval ty 0b01101010 x y z)) -(rule 17 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor x (bor y z)))) +(rule 17 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ x (bor _ y z)))) (vec_eval ty 0b01111000 x y z)) ;; Xor-Nor -(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor (bnot (bor x y)) z))) +(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ (bnot _ (bor _ x y)) z))) (vec_eval ty 0b10010101 x y z)) -(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor x (bnot (bor y z))))) +(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ x (bnot _ (bor _ y z))))) (vec_eval ty 0b10000111 x y z)) ;; Xor-Nxor -(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor (bnot (bxor x y)) z))) +(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ (bnot _ (bxor _ x y)) z))) (vec_eval ty 0b10010110 x y z)) -(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor x (bnot (bxor y z))))) +(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bxor _ x (bnot _ (bxor _ y z))))) (vec_eval ty 0b10010110 x y z)) ;; 3-input Nxor -(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor (bxor x y) z)))) +(rule 20 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ (bxor _ x y) z)))) (vec_eval ty 0b10010110 x y z)) -(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor x (bxor y z))))) +(rule 21 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ x (bxor _ y z))))) (vec_eval ty 0b10010110 x y z)) ;; Nxor-And -(rule 22 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor (band x y) z)))) +(rule 22 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ (band _ x y) z)))) (vec_eval ty 0b10101001 x y z)) -(rule 23 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor x (band y z))))) +(rule 23 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ x (band _ y z))))) (vec_eval ty 0b11100001 x y z)) ;; Nxor-Nand -(rule 24 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor (bnot (band x y)) z)))) +(rule 24 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ (bnot _ (band _ x y)) z)))) (vec_eval ty 0b01010110 x y z)) -(rule 25 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor x (bnot (band y z)))))) +(rule 25 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ x (bnot _ (band _ y z)))))) (vec_eval ty 0b00011110 x y z)) ;; Nxor-Or -(rule 26 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor (bor x y) z)))) +(rule 26 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ (bor _ x y) z)))) (vec_eval ty 0b10010101 x y z)) -(rule 27 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor x (bor y z))))) +(rule 27 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ x (bor _ y z))))) (vec_eval ty 0b10000111 x y z)) ;; Nxor-Nor -(rule 28 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor (bnot (bor x y)) z)))) +(rule 28 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ (bnot _ (bor _ x y)) z)))) (vec_eval ty 0b01101010 x y z)) -(rule 29 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor x (bnot (bor y z)))))) +(rule 29 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ x (bnot _ (bor _ y z)))))) (vec_eval ty 0b01111000 x y z)) ;; Xor-Nxor -(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor (bnot (bxor x y)) z)))) +(rule 18 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ (bnot _ (bxor _ x y)) z)))) (vec_eval ty 0b01101001 x y z)) -(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot (bxor x (bnot (bxor y z)))))) +(rule 19 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bnot _ (bxor _ x (bnot _ (bxor _ y z)))))) (vec_eval ty 0b01101001 x y z)) ;;;; Rules for `bitselect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; z15 version using a NAND instruction. -(rule 2 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bitselect x y z))) +(rule 2 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (bitselect _ x y z))) (let ((rx Reg x) (if_true Reg (and_reg ty y rx)) (if_false Reg (and_not_reg ty z rx))) (or_reg ty if_false if_true))) ;; z14 version using XOR with -1. -(rule 1 (lower (has_type (and (mie3_disabled) (fits_in_64 ty)) (bitselect x y z))) +(rule 1 (lower (has_type (and (mie3_disabled) (fits_in_64 ty)) (bitselect _ x y z))) (let ((rx Reg x) (if_true Reg (and_reg ty y rx)) (if_false Reg (and_reg ty z (not_reg ty rx)))) (or_reg ty if_false if_true))) ;; Bitselect vector registers. -(rule (lower (has_type (vr128_ty ty) (bitselect x y z))) +(rule (lower (has_type (vr128_ty ty) (bitselect _ x y z))) (vec_select ty y z x)) ;; Bitselect-not vector registers. -(rule 5 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bitselect x (bnot y) z))) +(rule 5 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bitselect _ x (bnot _ y) z))) (vec_eval ty 0b01011100 x y z)) -(rule 6 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bitselect x y (bnot z)))) +(rule 6 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bitselect _ x y (bnot _ z)))) (vec_eval ty 0b10100011 x y z)) -(rule 7 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bitselect x (bnot y) (bnot z)))) +(rule 7 (lower (has_type (and (vxrs_ext3_enabled) (vr128_ty ty)) (bitselect _ x (bnot _ y) (bnot _ z)))) (vec_eval ty 0b10101100 x y z)) ;; Special-case some float-selection instructions for min/max -(rule 3 (lower (has_type (ty_vec128 ty) (bitselect (bitcast _ (fcmp (FloatCC.LessThan) x y)) x y))) +(rule 3 (lower (has_type (ty_vec128 ty) (bitselect _ (bitcast _ _ (fcmp _ (FloatCC.LessThan) x y)) x y))) (fmin_pseudo_reg ty y x)) -(rule 4 (lower (has_type (ty_vec128 ty) (bitselect (bitcast _ (fcmp (FloatCC.LessThan) y x)) x y))) +(rule 4 (lower (has_type (ty_vec128 ty) (bitselect _ (bitcast _ _ (fcmp _ (FloatCC.LessThan) y x)) x y))) (fmax_pseudo_reg ty y x)) ;;;; Rules for `bmask` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (bmask x))) +(rule (lower (has_type ty (bmask _ x))) (lower_bool_to_mask ty (value_nonzero x))) ;;;; Rules for `bitrev` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (bitrev x))) +(rule (lower (has_type ty (bitrev _ x))) (bitrev_bytes ty (bitrev_bits 4 0xf0f0_f0f0_f0f0_f0f0 ty (bitrev_bits 2 0xcccc_cccc_cccc_cccc ty @@ -1511,7 +1511,7 @@ ;;;; Rules for `bswap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (bswap x))) +(rule (lower (has_type ty (bswap _ x))) (bitrev_bytes ty x)) ;;;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1527,7 +1527,7 @@ ;; Count leading zeros pre-z17, via FLOGR on an input zero-extended to 64 bits, ;; with the result compensated for the extra bits. -(rule 1 (lower (has_type (and (mie4_disabled) (fits_in_64 ty)) (clz x))) +(rule 1 (lower (has_type (and (mie4_disabled) (fits_in_64 ty)) (clz _ x))) (let ((ext_reg Reg (put_in_reg_zext64 x)) ;; Ask for a value of 64 in the all-zero 64-bit input case. ;; After compensation this will match the expected semantics. @@ -1535,7 +1535,7 @@ (clz_offset ty clz))) ;; Count leading zeros, 128-bit full vector pre-z17. -(rule (lower (has_type (and (vxrs_ext3_disabled) $I128) (clz x))) +(rule (lower (has_type (and (vxrs_ext3_disabled) $I128) (clz _ x))) (let ((clz_vec Reg (vec_clz $I64X2 x)) (zero Reg (vec_imm $I64X2 0)) (clz_hi Reg (vec_permute_dw_imm $I64X2 zero 0 clz_vec 0)) @@ -1546,13 +1546,13 @@ ;; Count leading zeros on z17, via CLZG on an input zero-extended to 64 bits, ;; with the result compensated for the extra bits. -(rule 3 (lower (has_type (and (mie4_enabled) (fits_in_64 ty)) (clz x))) +(rule 3 (lower (has_type (and (mie4_enabled) (fits_in_64 ty)) (clz _ x))) (let ((ext_reg Reg (put_in_reg_zext64 x)) (clz Reg (clz_reg ext_reg))) (clz_offset ty clz))) ;; Count leading zeros, 128-bit full vector on z17. -(rule 2 (lower (has_type (and (vxrs_ext3_enabled) $I128) (clz x))) +(rule 2 (lower (has_type (and (vxrs_ext3_enabled) $I128) (clz _ x))) (vec_clz $I128 x)) ;;;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1571,7 +1571,7 @@ ;; i.e. computing ;; cls(x) == clz(x ^ (x >> 63)) - 1 ;; where x is the sign-extended input. -(rule 1 (lower (has_type (and (mie4_disabled) (fits_in_64 ty)) (cls x))) +(rule 1 (lower (has_type (and (mie4_disabled) (fits_in_64 ty)) (cls _ x))) (let ((ext_reg Reg (put_in_reg_sext64 x)) (signbit_copies Reg (ashr_imm $I64 ext_reg 63)) (inv_reg Reg (xor_reg $I64 ext_reg signbit_copies)) @@ -1579,7 +1579,7 @@ (cls_offset ty clz))) ;; Count leading sign-bit copies, 128-bit full vector pre-z17. -(rule (lower (has_type (and (vxrs_ext3_disabled) $I128) (cls x))) +(rule (lower (has_type (and (vxrs_ext3_disabled) $I128) (cls _ x))) (let ((x_reg Reg x) (ones Reg (vec_imm_splat $I8X16 255)) (signbit_copies Reg (vec_ashr_by_bit (vec_ashr_by_byte x_reg ones) ones)) @@ -1593,7 +1593,7 @@ (vec_add $I128 (vec_select $I128 clz_sum clz_hi mask) ones))) ;; Count leading sign-bit copies on z17, similar to above. -(rule 3 (lower (has_type (and (mie4_enabled) (fits_in_64 ty)) (cls x))) +(rule 3 (lower (has_type (and (mie4_enabled) (fits_in_64 ty)) (cls _ x))) (let ((ext_reg Reg (put_in_reg_sext64 x)) (signbit_copies Reg (ashr_imm $I64 ext_reg 63)) (inv_reg Reg (xor_reg $I64 ext_reg signbit_copies)) @@ -1601,7 +1601,7 @@ (cls_offset ty clz))) ;; Count leading sign-bit copies, 128-bit full vector on z17. -(rule 2 (lower (has_type (and (vxrs_ext3_enabled) $I128) (cls x))) +(rule 2 (lower (has_type (and (vxrs_ext3_enabled) $I128) (cls _ x))) (let ((x_reg Reg x) (ones Reg (vec_imm_splat $I8X16 255)) (signbit_copies Reg (vec_ashr_by_bit (vec_ashr_by_byte x_reg ones) ones)) @@ -1624,7 +1624,7 @@ ;; never zero by setting a "guard bit" in the position corresponding to ;; the input type size. This way the 64-bit algorithm above will handle ;; that case correctly automatically. -(rule 2 (lower (has_type (and (mie4_disabled) (gpr32_ty ty)) (ctz x))) +(rule 2 (lower (has_type (and (mie4_disabled) (gpr32_ty ty)) (ctz _ x))) (let ((rx Reg (or_uimm16shifted $I64 x (ctz_guardbit ty))) (lastbit Reg (and_reg $I64 rx (neg_reg $I64 rx))) (clz Reg (clz_flogr_reg 64 lastbit))) @@ -1639,14 +1639,14 @@ ;; via its condition code. We check for that and replace the instruction ;; result with the value -1 via a conditional move, which will then lead to ;; the correct result after the final subtraction from 63. -(rule 1 (lower (has_type (and (mie4_disabled) (gpr64_ty _ty)) (ctz x))) +(rule 1 (lower (has_type (and (mie4_disabled) (gpr64_ty _ty)) (ctz _ x))) (let ((rx Reg x) (lastbit Reg (and_reg $I64 rx (neg_reg $I64 rx))) (clz Reg (clz_flogr_reg -1 lastbit))) (sub_reg $I64 (imm $I64 63) clz))) ;; Count trailing zeros, 128-bit full vector pre-z17. -(rule 0 (lower (has_type (and (vxrs_ext3_disabled) $I128) (ctz x))) +(rule 0 (lower (has_type (and (vxrs_ext3_disabled) $I128) (ctz _ x))) (let ((ctz_vec Reg (vec_ctz $I64X2 x)) (zero Reg (vec_imm $I64X2 0)) (ctz_hi Reg (vec_permute_dw_imm $I64X2 zero 0 ctz_vec 0)) @@ -1657,27 +1657,27 @@ ;; Count leading zeros on z17, via CTZG on types smaller than 64-bit, ;; using the same guard bit mechanism as above. -(rule 5 (lower (has_type (and (mie4_enabled) (gpr32_ty ty)) (ctz x))) +(rule 5 (lower (has_type (and (mie4_enabled) (gpr32_ty ty)) (ctz _ x))) (ctz_reg (or_uimm16shifted $I64 x (ctz_guardbit ty)))) ;; Count leading zeros on z17, via CTZG directly on 64-bit types. -(rule 4 (lower (has_type (and (mie4_enabled) (gpr64_ty _ty)) (ctz x))) +(rule 4 (lower (has_type (and (mie4_enabled) (gpr64_ty _ty)) (ctz _ x))) (ctz_reg x)) ;; Count trailing zeros, 128-bit full vector on z17. -(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (ctz x))) +(rule 3 (lower (has_type (and (vxrs_ext3_enabled) $I128) (ctz _ x))) (vec_ctz $I128 x)) ;;;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Population count for 8-bit types is supported by the POPCNT instruction. -(rule (lower (has_type $I8 (popcnt x))) +(rule (lower (has_type $I8 (popcnt _ x))) (popcnt_byte x)) ;; On z15, the POPCNT instruction has a variant to compute a full 64-bit ;; population count, which we also use for 16- and 32-bit types. -(rule -1 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (popcnt x))) +(rule -1 (lower (has_type (and (mie3_enabled) (fits_in_64 ty)) (popcnt _ x))) (popcnt_reg (put_in_reg_zext64 x))) ;; On z14, we use the regular POPCNT, which computes the population count @@ -1688,18 +1688,18 @@ ;; $I16, where we instead accumulate in the low byte and clear high bits ;; via an explicit and operation.) -(rule (lower (has_type (and (mie3_disabled) $I16) (popcnt x))) +(rule (lower (has_type (and (mie3_disabled) $I16) (popcnt _ x))) (let ((cnt2 Reg (popcnt_byte x)) (cnt1 Reg (add_reg $I32 cnt2 (lshr_imm $I32 cnt2 8)))) (and_uimm16shifted $I32 cnt1 (uimm16shifted 255 0)))) -(rule (lower (has_type (and (mie3_disabled) $I32) (popcnt x))) +(rule (lower (has_type (and (mie3_disabled) $I32) (popcnt _ x))) (let ((cnt4 Reg (popcnt_byte x)) (cnt2 Reg (add_reg $I32 cnt4 (lshl_imm $I32 cnt4 16))) (cnt1 Reg (add_reg $I32 cnt2 (lshl_imm $I32 cnt2 8)))) (lshr_imm $I32 cnt1 24))) -(rule (lower (has_type (and (mie3_disabled) $I64) (popcnt x))) +(rule (lower (has_type (and (mie3_disabled) $I64) (popcnt _ x))) (let ((cnt8 Reg (popcnt_byte x)) (cnt4 Reg (add_reg $I64 cnt8 (lshl_imm $I64 cnt8 32))) (cnt2 Reg (add_reg $I64 cnt4 (lshl_imm $I64 cnt4 16))) @@ -1707,11 +1707,11 @@ (lshr_imm $I64 cnt1 56))) ;; Population count for vector types. -(rule 1 (lower (has_type (ty_vec128 ty) (popcnt x))) +(rule 1 (lower (has_type (ty_vec128 ty) (popcnt _ x))) (vec_popcnt ty x)) ;; Population count, 128-bit full vector. -(rule (lower (has_type $I128 (popcnt x))) +(rule (lower (has_type $I128 (popcnt _ x))) (let ((popcnt_vec Reg (vec_popcnt $I64X2 x)) (zero Reg (vec_imm $I64X2 0)) (popcnt_hi Reg (vec_permute_dw_imm $I64X2 zero 0 popcnt_vec 0)) @@ -1722,141 +1722,141 @@ ;;;; Rules for `fadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Add two registers. -(rule (lower (has_type ty (fadd x y))) +(rule (lower (has_type ty (fadd _ x y))) (fadd_reg ty x y)) ;;;; Rules for `fsub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Subtract two registers. -(rule (lower (has_type ty (fsub x y))) +(rule (lower (has_type ty (fsub _ x y))) (fsub_reg ty x y)) ;;;; Rules for `fmul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Multiply two registers. -(rule (lower (has_type ty (fmul x y))) +(rule (lower (has_type ty (fmul _ x y))) (fmul_reg ty x y)) ;;;; Rules for `fdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Divide two registers. -(rule (lower (has_type ty (fdiv x y))) +(rule (lower (has_type ty (fdiv _ x y))) (fdiv_reg ty x y)) ;;;; Rules for `fmin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Minimum of two registers. -(rule (lower (has_type ty (fmin x y))) +(rule (lower (has_type ty (fmin _ x y))) (fmin_reg ty x y)) ;;;; Rules for `fmax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Maximum of two registers. -(rule (lower (has_type ty (fmax x y))) +(rule (lower (has_type ty (fmax _ x y))) (fmax_reg ty x y)) ;;;; Rules for `fcopysign` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Copysign of two registers. -(rule (lower (has_type $F32 (fcopysign x y))) +(rule (lower (has_type $F32 (fcopysign _ x y))) (vec_select $F32 x y (imm $F32 2147483647))) -(rule (lower (has_type $F64 (fcopysign x y))) +(rule (lower (has_type $F64 (fcopysign _ x y))) (vec_select $F64 x y (imm $F64 9223372036854775807))) -(rule (lower (has_type $F128 (fcopysign x y))) +(rule (lower (has_type $F128 (fcopysign _ x y))) (vec_select $F128 x y (vec_imm $F128 (imm8x16 127 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)))) -(rule (lower (has_type $F32X4 (fcopysign x y))) +(rule (lower (has_type $F32X4 (fcopysign _ x y))) (vec_select $F32X4 x y (vec_imm_bit_mask $F32X4 1 31))) -(rule (lower (has_type $F64X2 (fcopysign x y))) +(rule (lower (has_type $F64X2 (fcopysign _ x y))) (vec_select $F64X2 x y (vec_imm_bit_mask $F64X2 1 63))) ;;;; Rules for `fma` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Multiply-and-add of three registers. -(rule (lower (has_type ty (fma x y z))) +(rule (lower (has_type ty (fma _ x y z))) (fma_reg ty x y z)) ;;;; Rules for `sqrt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Square root of a register. -(rule (lower (has_type ty (sqrt x))) +(rule (lower (has_type ty (sqrt _ x))) (sqrt_reg ty x)) ;;;; Rules for `fneg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Negated value of a register. -(rule (lower (has_type ty (fneg x))) +(rule (lower (has_type ty (fneg _ x))) (fneg_reg ty x)) ;;;; Rules for `fabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Absolute value of a register. -(rule (lower (has_type ty (fabs x))) +(rule (lower (has_type ty (fabs _ x))) (fabs_reg ty x)) ;;;; Rules for `ceil` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Round value in a register towards positive infinity. -(rule (lower (has_type ty (ceil x))) +(rule (lower (has_type ty (ceil _ x))) (ceil_reg ty x)) ;;;; Rules for `floor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Round value in a register towards negative infinity. -(rule (lower (has_type ty (floor x))) +(rule (lower (has_type ty (floor _ x))) (floor_reg ty x)) ;;;; Rules for `trunc` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Round value in a register towards zero. -(rule (lower (has_type ty (trunc x))) +(rule (lower (has_type ty (trunc _ x))) (trunc_reg ty x)) ;;;; Rules for `nearest` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Round value in a register towards nearest. -(rule (lower (has_type ty (nearest x))) +(rule (lower (has_type ty (nearest _ x))) (nearest_reg ty x)) ;;;; Rules for `fpromote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Promote a register. -(rule (lower (has_type dst_ty (fpromote x @ (value_type src_ty)))) +(rule (lower (has_type dst_ty (fpromote _ x @ (value_type src_ty)))) (fpromote_reg dst_ty src_ty x)) -(rule 1 (lower (has_type $F128 (fpromote x @ (value_type $F32)))) +(rule 1 (lower (has_type $F128 (fpromote _ x @ (value_type $F32)))) (fpromote_reg $F128 $F64 (fpromote_reg $F64 $F32 x))) ;;;; Rules for `fvpromote_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Promote a register. -(rule (lower (has_type $F64X2 (fvpromote_low x @ (value_type $F32X4)))) +(rule (lower (has_type $F64X2 (fvpromote_low _ x @ (value_type $F32X4)))) (fpromote_reg $F64X2 $F32X4 (vec_merge_low_lane_order $I32X4 x x))) ;;;; Rules for `fdemote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Demote a register. -(rule (lower (has_type dst_ty (fdemote x @ (value_type src_ty)))) +(rule (lower (has_type dst_ty (fdemote _ x @ (value_type src_ty)))) (fdemote_reg dst_ty src_ty (FpuRoundMode.Current) x)) -(rule 1 (lower (has_type $F32 (fdemote x @ (value_type $F128)))) +(rule 1 (lower (has_type $F32 (fdemote _ x @ (value_type $F128)))) (fdemote_reg $F32 $F64 (FpuRoundMode.Current) (fdemote_reg $F64 $F128 (FpuRoundMode.ShorterPrecision) x))) @@ -1864,7 +1864,7 @@ ;;;; Rules for `fvdemote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Demote a register. -(rule (lower (has_type $F32X4 (fvdemote x @ (value_type $F64X2)))) +(rule (lower (has_type $F32X4 (fvdemote _ x @ (value_type $F64X2)))) (let ((dst Reg (fdemote_reg $F32X4 $F64X2 (FpuRoundMode.Current) x))) (vec_pack_lane_order $I64X2 (vec_lshr_imm $I64X2 dst 32) (vec_imm $I64X2 0)))) @@ -1874,39 +1874,39 @@ ;; Convert a 32-bit or smaller unsigned integer to $F32 (z15 instruction). (rule 1 (lower (has_type $F32 - (fcvt_from_uint x @ (value_type (and (vxrs_ext2_enabled) (fits_in_32 ty)))))) + (fcvt_from_uint _ x @ (value_type (and (vxrs_ext2_enabled) (fits_in_32 ty)))))) (fcvt_from_uint_reg $F32 $I32 (FpuRoundMode.ToNearestTiesToEven) (put_in_reg_zext32 x))) ;; Convert a 64-bit or smaller unsigned integer to $F32, via an intermediate $F64. -(rule (lower (has_type $F32 (fcvt_from_uint x @ (value_type (fits_in_64 ty))))) +(rule (lower (has_type $F32 (fcvt_from_uint _ x @ (value_type (fits_in_64 ty))))) (fdemote_reg $F32 $F64 (FpuRoundMode.ToNearestTiesToEven) (fcvt_from_uint_reg $F64 $I64 (FpuRoundMode.ShorterPrecision) (put_in_reg_zext64 x)))) ;; Convert a 64-bit or smaller unsigned integer to $F64. -(rule (lower (has_type $F64 (fcvt_from_uint x @ (value_type (fits_in_64 ty))))) +(rule (lower (has_type $F64 (fcvt_from_uint _ x @ (value_type (fits_in_64 ty))))) (fcvt_from_uint_reg $F64 $I64 (FpuRoundMode.ToNearestTiesToEven) (put_in_reg_zext64 x))) ;; Convert a 32-bit or smaller unsigned integer to $F128. -(rule 1 (lower (has_type $F128 (fcvt_from_uint x @ (value_type (fits_in_32 ty))))) +(rule 1 (lower (has_type $F128 (fcvt_from_uint _ x @ (value_type (fits_in_32 ty))))) (fcvt_from_uint_reg $F128 $I32 (FpuRoundMode.ToNearestTiesToEven) (put_in_reg_zext32 x))) ;; Convert a 64-bit or smaller unsigned integer to $F128. -(rule (lower (has_type $F128 (fcvt_from_uint x @ (value_type (fits_in_64 ty))))) +(rule (lower (has_type $F128 (fcvt_from_uint _ x @ (value_type (fits_in_64 ty))))) (fcvt_from_uint_reg $F128 $I64 (FpuRoundMode.ToNearestTiesToEven) (put_in_reg_zext64 x))) ;; Convert $I32X4 to $F32X4 (z15 instruction). (rule 1 (lower (has_type (and (vxrs_ext2_enabled) $F32X4) - (fcvt_from_uint x @ (value_type $I32X4)))) + (fcvt_from_uint _ x @ (value_type $I32X4)))) (fcvt_from_uint_reg $F32X4 $I32X4 (FpuRoundMode.ToNearestTiesToEven) x)) ;; Convert $I32X4 to $F32X4 (via two $F64X2 on z14). (rule (lower (has_type (and (vxrs_ext2_disabled) $F32X4) - (fcvt_from_uint x @ (value_type $I32X4)))) + (fcvt_from_uint _ x @ (value_type $I32X4)))) (vec_permute $F32X4 (fdemote_reg $F32X4 $F64X2 (FpuRoundMode.ToNearestTiesToEven) (fcvt_from_uint_reg $F64X2 $I64X2 (FpuRoundMode.ShorterPrecision) @@ -1917,7 +1917,7 @@ (vec_imm $I8X16 (imm8x16 0 1 2 3 8 9 10 11 16 17 18 19 24 25 26 27)))) ;; Convert $I64X2 to $F64X2. -(rule (lower (has_type $F64X2 (fcvt_from_uint x @ (value_type $I64X2)))) +(rule (lower (has_type $F64X2 (fcvt_from_uint _ x @ (value_type $I64X2)))) (fcvt_from_uint_reg $F64X2 $I64X2 (FpuRoundMode.ToNearestTiesToEven) x)) @@ -1925,39 +1925,39 @@ ;; Convert a 32-bit or smaller signed integer to $F32 (z15 instruction). (rule 1 (lower (has_type $F32 - (fcvt_from_sint x @ (value_type (and (vxrs_ext2_enabled) (fits_in_32 ty)))))) + (fcvt_from_sint _ x @ (value_type (and (vxrs_ext2_enabled) (fits_in_32 ty)))))) (fcvt_from_sint_reg $F32 $I32 (FpuRoundMode.ToNearestTiesToEven) (put_in_reg_sext32 x))) ;; Convert a 64-bit or smaller signed integer to $F32, via an intermediate $F64. -(rule (lower (has_type $F32 (fcvt_from_sint x @ (value_type (fits_in_64 ty))))) +(rule (lower (has_type $F32 (fcvt_from_sint _ x @ (value_type (fits_in_64 ty))))) (fdemote_reg $F32 $F64 (FpuRoundMode.ToNearestTiesToEven) (fcvt_from_sint_reg $F64 $I64 (FpuRoundMode.ShorterPrecision) (put_in_reg_sext64 x)))) ;; Convert a 64-bit or smaller signed integer to $F64. -(rule (lower (has_type $F64 (fcvt_from_sint x @ (value_type (fits_in_64 ty))))) +(rule (lower (has_type $F64 (fcvt_from_sint _ x @ (value_type (fits_in_64 ty))))) (fcvt_from_sint_reg $F64 $I64 (FpuRoundMode.ToNearestTiesToEven) (put_in_reg_sext64 x))) ;; Convert a 32-bit or smaller signed integer to $F128. -(rule 1 (lower (has_type $F128 (fcvt_from_sint x @ (value_type (fits_in_32 ty))))) +(rule 1 (lower (has_type $F128 (fcvt_from_sint _ x @ (value_type (fits_in_32 ty))))) (fcvt_from_sint_reg $F128 $I32 (FpuRoundMode.ToNearestTiesToEven) (put_in_reg_sext32 x))) ;; Convert a 64-bit or smaller signed integer to $F128. -(rule (lower (has_type $F128 (fcvt_from_sint x @ (value_type (fits_in_64 ty))))) +(rule (lower (has_type $F128 (fcvt_from_sint _ x @ (value_type (fits_in_64 ty))))) (fcvt_from_sint_reg $F128 $I64 (FpuRoundMode.ToNearestTiesToEven) (put_in_reg_sext64 x))) ;; Convert $I32X4 to $F32X4 (z15 instruction). (rule 1 (lower (has_type (and (vxrs_ext2_enabled) $F32X4) - (fcvt_from_sint x @ (value_type $I32X4)))) + (fcvt_from_sint _ x @ (value_type $I32X4)))) (fcvt_from_sint_reg $F32X4 $I32X4 (FpuRoundMode.ToNearestTiesToEven) x)) ;; Convert $I32X4 to $F32X4 (via two $F64X2 on z14). (rule (lower (has_type (and (vxrs_ext2_disabled) $F32X4) - (fcvt_from_sint x @ (value_type $I32X4)))) + (fcvt_from_sint _ x @ (value_type $I32X4)))) (vec_permute $F32X4 (fdemote_reg $F32X4 $F64X2 (FpuRoundMode.ToNearestTiesToEven) (fcvt_from_sint_reg $F64X2 $I64X2 (FpuRoundMode.ShorterPrecision) @@ -1968,7 +1968,7 @@ (vec_imm $I8X16 (imm8x16 0 1 2 3 8 9 10 11 16 17 18 19 24 25 26 27)))) ;; Convert $I64X2 to $F64X2. -(rule (lower (has_type $F64X2 (fcvt_from_sint x @ (value_type $I64X2)))) +(rule (lower (has_type $F64X2 (fcvt_from_sint _ x @ (value_type $I64X2)))) (fcvt_from_sint_reg $F64X2 $I64X2 (FpuRoundMode.ToNearestTiesToEven) x)) @@ -1977,7 +1977,7 @@ ;; Convert a scalar floating-point value in a register to an unsigned integer. ;; Traps if the input cannot be represented in the output type. (rule (lower (has_type (fits_in_64 dst_ty) - (fcvt_to_uint x @ (value_type src_ty)))) + (fcvt_to_uint _ x @ (value_type src_ty)))) (let ((src Reg (put_in_reg x)) ;; First, check whether the input is a NaN, and trap if so. (_ Reg (trap_if (fcmp_reg src_ty src src) @@ -2002,7 +2002,7 @@ ;; Convert a scalar floating-point value in a register to a signed integer. ;; Traps if the input cannot be represented in the output type. (rule (lower (has_type (fits_in_64 dst_ty) - (fcvt_to_sint x @ (value_type src_ty)))) + (fcvt_to_sint _ x @ (value_type src_ty)))) (let ((src Reg (put_in_reg x)) ;; First, check whether the input is a NaN, and trap if so. (_ Reg (trap_if (fcmp_reg src_ty src src) @@ -2027,7 +2027,7 @@ ;; Convert a scalar floating-point value in a register to an unsigned integer. (rule -1 (lower (has_type (fits_in_64 dst_ty) - (fcvt_to_uint_sat x @ (value_type src_ty)))) + (fcvt_to_uint_sat _ x @ (value_type src_ty)))) (let ((src Reg (put_in_reg x)) ;; Perform the conversion using the larger type size. (flt_ty Type (fcvt_flt_ty dst_ty src_ty)) @@ -2039,12 +2039,12 @@ ;; Convert $F32X4 to $I32X4 (z15 instruction). (rule 1 (lower (has_type (and (vxrs_ext2_enabled) $I32X4) - (fcvt_to_uint_sat x @ (value_type $F32X4)))) + (fcvt_to_uint_sat _ x @ (value_type $F32X4)))) (fcvt_to_uint_reg $I32X4 $F32X4 (FpuRoundMode.ToZero) x)) ;; Convert $F32X4 to $I32X4 (via two $F64X2 on z14). (rule (lower (has_type (and (vxrs_ext2_disabled) $I32X4) - (fcvt_to_uint_sat x @ (value_type $F32X4)))) + (fcvt_to_uint_sat _ x @ (value_type $F32X4)))) (vec_pack_usat $I64X2 (fcvt_to_uint_reg $I64X2 $F64X2 (FpuRoundMode.ToZero) (fpromote_reg $F64X2 $F32X4 (vec_merge_high $I32X4 x x))) @@ -2052,7 +2052,7 @@ (fpromote_reg $F64X2 $F32X4 (vec_merge_low $I32X4 x x))))) ;; Convert $F64X2 to $I64X2. -(rule (lower (has_type $I64X2 (fcvt_to_uint_sat x @ (value_type $F64X2)))) +(rule (lower (has_type $I64X2 (fcvt_to_uint_sat _ x @ (value_type $F64X2)))) (fcvt_to_uint_reg $I64X2 $F64X2 (FpuRoundMode.ToZero) x)) @@ -2060,7 +2060,7 @@ ;; Convert a scalar floating-point value in a register to a signed integer. (rule -1 (lower (has_type (fits_in_64 dst_ty) - (fcvt_to_sint_sat x @ (value_type src_ty)))) + (fcvt_to_sint_sat _ x @ (value_type src_ty)))) (let ((src Reg (put_in_reg x)) ;; Perform the conversion using the larger type size. (flt_ty Type (fcvt_flt_ty dst_ty src_ty)) @@ -2079,7 +2079,7 @@ ;; Convert $F32X4 to $I32X4 (z15 instruction). (rule 1 (lower (has_type (and (vxrs_ext2_enabled) $I32X4) - (fcvt_to_sint_sat src @ (value_type $F32X4)))) + (fcvt_to_sint_sat _ src @ (value_type $F32X4)))) ;; See above for why we need to handle NaNs specially. (vec_select $I32X4 (fcvt_to_sint_reg $I32X4 $F32X4 (FpuRoundMode.ToZero) src) @@ -2087,7 +2087,7 @@ ;; Convert $F32X4 to $I32X4 (via two $F64X2 on z14). (rule (lower (has_type (and (vxrs_ext2_disabled) $I32X4) - (fcvt_to_sint_sat src @ (value_type $F32X4)))) + (fcvt_to_sint_sat _ src @ (value_type $F32X4)))) ;; See above for why we need to handle NaNs specially. (vec_select $I32X4 (vec_pack_ssat $I64X2 @@ -2098,7 +2098,7 @@ (vec_imm $I32X4 0) (vec_fcmpeq $F32X4 src src))) ;; Convert $F64X2 to $I64X2. -(rule (lower (has_type $I64X2 (fcvt_to_sint_sat src @ (value_type $F64X2)))) +(rule (lower (has_type $I64X2 (fcvt_to_sint_sat _ src @ (value_type $F64X2)))) ;; See above for why we need to handle NaNs specially. (vec_select $I64X2 (fcvt_to_sint_reg $I64X2 $F64X2 (FpuRoundMode.ToZero) src) @@ -2108,45 +2108,45 @@ ;;;; Rules for `bitcast` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Reinterpret a 64-bit integer value as floating-point. -(rule (lower (has_type $F64 (bitcast _ x @ (value_type $I64)))) +(rule (lower (has_type $F64 (bitcast _ _ x @ (value_type $I64)))) (vec_insert_lane_undef $F64X2 x 0 (zero_reg))) ;; Reinterpret a 64-bit floating-point value as integer. -(rule (lower (has_type $I64 (bitcast _ x @ (value_type $F64)))) +(rule (lower (has_type $I64 (bitcast _ _ x @ (value_type $F64)))) (vec_extract_lane $F64X2 x 0 (zero_reg))) ;; Reinterpret a 32-bit integer value as floating-point. -(rule (lower (has_type $F32 (bitcast _ x @ (value_type $I32)))) +(rule (lower (has_type $F32 (bitcast _ _ x @ (value_type $I32)))) (vec_insert_lane_undef $F32X4 x 0 (zero_reg))) ;; Reinterpret a 32-bit floating-point value as integer. -(rule (lower (has_type $I32 (bitcast _ x @ (value_type $F32)))) +(rule (lower (has_type $I32 (bitcast _ _ x @ (value_type $F32)))) (vec_extract_lane $F32X4 x 0 (zero_reg))) ;; Reinterpret a 16-bit integer value as floating-point. -(rule (lower (has_type $F16 (bitcast _ x @ (value_type $I16)))) +(rule (lower (has_type $F16 (bitcast _ _ x @ (value_type $I16)))) (vec_insert_lane_undef $F16X8 x 0 (zero_reg))) ;; Reinterpret a 16-bit floating-point value as integer. -(rule (lower (has_type $I16 (bitcast _ x @ (value_type $F16)))) +(rule (lower (has_type $I16 (bitcast _ _ x @ (value_type $F16)))) (vec_extract_lane $F16X8 x 0 (zero_reg))) ;; Bitcast between types residing in GPRs is a no-op. (rule 1 (lower (has_type (gpr32_ty _) - (bitcast _ x @ (value_type (gpr32_ty _))))) + (bitcast _ _ x @ (value_type (gpr32_ty _))))) x) (rule 2 (lower (has_type (gpr64_ty _) - (bitcast _ x @ (value_type (gpr64_ty _))))) + (bitcast _ _ x @ (value_type (gpr64_ty _))))) x) ;; Bitcast between types residing in FPRs is a no-op. (rule 3 (lower (has_type (ty_scalar_float _) - (bitcast _ x @ (value_type (ty_scalar_float _))))) + (bitcast _ _ x @ (value_type (ty_scalar_float _))))) x) ;; Bitcast between types residing in VRs is a no-op if lane count is unchanged. (rule 5 (lower (has_type (multi_lane bits count) - (bitcast _ x @ (value_type (multi_lane bits count))))) + (bitcast _ _ x @ (value_type (multi_lane bits count))))) x) ;; Bitcast between types residing in VRs with different lane counts is a @@ -2157,7 +2157,7 @@ ;; choice of lane order depending on the current function ABI, this case will ;; currently never arise in practice. (rule 4 (lower (has_type (vr128_ty out_ty) - (bitcast flags x @ (value_type (vr128_ty in_ty))))) + (bitcast _ flags x @ (value_type (vr128_ty in_ty))))) (abi_vec_elt_rev (lane_order_from_memflags flags) out_ty (abi_vec_elt_rev (lane_order_from_memflags flags) in_ty x))) @@ -2171,37 +2171,37 @@ ;;;; Rules for `insertlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Insert vector lane from general-purpose register. -(rule 1 (lower (insertlane x @ (value_type ty) +(rule 1 (lower (insertlane _ x @ (value_type ty) y @ (value_type in_ty) (u8_from_uimm8 idx))) (if (ty_int_ref_scalar_64 in_ty)) (vec_insert_lane ty x y (be_lane_idx ty idx) (zero_reg))) ;; Insert vector lane from floating-point register. -(rule 0 (lower (insertlane x @ (value_type ty) +(rule 0 (lower (insertlane _ x @ (value_type ty) y @ (value_type (ty_scalar_float _)) (u8_from_uimm8 idx))) (vec_move_lane_and_insert ty x (be_lane_idx ty idx) y 0)) ;; Insert vector lane from another vector lane. -(rule 2 (lower (insertlane x @ (value_type ty) - (extractlane y (u8_from_uimm8 src_idx)) +(rule 2 (lower (insertlane _ x @ (value_type ty) + (extractlane _ y (u8_from_uimm8 src_idx)) (u8_from_uimm8 dst_idx))) (vec_move_lane_and_insert ty x (be_lane_idx ty dst_idx) y (be_lane_idx ty src_idx))) ;; Insert vector lane from signed 16-bit immediate. -(rule 3 (lower (insertlane x @ (value_type ty) (i16_from_value y) +(rule 3 (lower (insertlane _ x @ (value_type ty) (i16_from_value y) (u8_from_uimm8 idx))) (vec_insert_lane_imm ty x y (be_lane_idx ty idx))) ;; Insert vector lane from big-endian memory. -(rule 4 (lower (insertlane x @ (value_type ty) (sinkable_load y) +(rule 4 (lower (insertlane _ x @ (value_type ty) (sinkable_load y) (u8_from_uimm8 idx))) (vec_load_lane ty x (sink_load y) (be_lane_idx ty idx))) ;; Insert vector lane from little-endian memory. -(rule 5 (lower (insertlane x @ (value_type ty) (sinkable_load_little y) +(rule 5 (lower (insertlane _ x @ (value_type ty) (sinkable_load_little y) (u8_from_uimm8 idx))) (vec_load_lane_little ty x (sink_load y) (be_lane_idx ty idx))) @@ -2289,25 +2289,25 @@ ;; Extract vector lane to general-purpose register. (rule 1 (lower (has_type out_ty - (extractlane x @ (value_type ty) (u8_from_uimm8 idx)))) + (extractlane _ x @ (value_type ty) (u8_from_uimm8 idx)))) (if (ty_int_ref_scalar_64 out_ty)) (vec_extract_lane ty x (be_lane_idx ty idx) (zero_reg))) ;; Extract vector lane to floating-point register. (rule 0 (lower (has_type (ty_scalar_float _) - (extractlane x @ (value_type ty) (u8_from_uimm8 idx)))) + (extractlane _ x @ (value_type ty) (u8_from_uimm8 idx)))) (vec_replicate_lane ty x (be_lane_idx ty idx))) ;; Extract vector lane and store to big-endian memory. (rule 6 (lower (store flags @ (bigendian) - (extractlane x @ (value_type ty) (u8_from_uimm8 idx)) + (extractlane _ x @ (value_type ty) (u8_from_uimm8 idx)) addr offset)) (side_effect (vec_store_lane ty x (lower_address flags addr offset) (be_lane_idx ty idx)))) ;; Extract vector lane and store to little-endian memory. (rule 5 (lower (store flags @ (littleendian) - (extractlane x @ (value_type ty) (u8_from_uimm8 idx)) + (extractlane _ x @ (value_type ty) (u8_from_uimm8 idx)) addr offset)) (side_effect (vec_store_lane_little ty x (lower_address flags addr offset) (be_lane_idx ty idx)))) @@ -2346,29 +2346,29 @@ ;;;; Rules for `splat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Load replicated value from general-purpose register. -(rule 1 (lower (has_type ty (splat x @ (value_type in_ty)))) +(rule 1 (lower (has_type ty (splat _ x @ (value_type in_ty)))) (if (ty_int_ref_scalar_64 in_ty)) (vec_replicate_lane ty (vec_insert_lane_undef ty x 0 (zero_reg)) 0)) ;; Load replicated value from floating-point register. -(rule 0 (lower (has_type ty (splat +(rule 0 (lower (has_type ty (splat _ x @ (value_type (ty_scalar_float _))))) (vec_replicate_lane ty x 0)) ;; Load replicated value from vector lane. -(rule 2 (lower (has_type ty (splat (extractlane x (u8_from_uimm8 idx))))) +(rule 2 (lower (has_type ty (splat _ (extractlane _ x (u8_from_uimm8 idx))))) (vec_replicate_lane ty x (be_lane_idx ty idx))) ;; Load replicated 16-bit immediate value. -(rule 3 (lower (has_type ty (splat (i16_from_value x)))) +(rule 3 (lower (has_type ty (splat _ (i16_from_value x)))) (vec_imm_replicate ty x)) ;; Load replicated value from big-endian memory. -(rule 4 (lower (has_type ty (splat (sinkable_load x)))) +(rule 4 (lower (has_type ty (splat _ (sinkable_load x)))) (vec_load_replicate ty (sink_load x))) ;; Load replicated value from little-endian memory. -(rule 5 (lower (has_type ty (splat (sinkable_load_little x)))) +(rule 5 (lower (has_type ty (splat _ (sinkable_load_little x)))) (vec_load_replicate_little ty (sink_load x))) @@ -2405,31 +2405,31 @@ ;;;; Rules for `scalar_to_vector` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Load scalar value from general-purpose register. -(rule 1 (lower (has_type ty (scalar_to_vector +(rule 1 (lower (has_type ty (scalar_to_vector _ x @ (value_type in_ty)))) (if (ty_int_ref_scalar_64 in_ty)) (vec_insert_lane ty (vec_imm ty 0) x (be_lane_idx ty 0) (zero_reg))) ;; Load scalar value from floating-point register. -(rule 0 (lower (has_type ty (scalar_to_vector +(rule 0 (lower (has_type ty (scalar_to_vector _ x @ (value_type (ty_scalar_float _))))) (vec_move_lane_and_zero ty (be_lane_idx ty 0) x 0)) ;; Load scalar value from vector lane. -(rule 2 (lower (has_type ty (scalar_to_vector - (extractlane x (u8_from_uimm8 idx))))) +(rule 2 (lower (has_type ty (scalar_to_vector _ + (extractlane _ x (u8_from_uimm8 idx))))) (vec_move_lane_and_zero ty (be_lane_idx ty 0) x (be_lane_idx ty idx))) ;; Load scalar 16-bit immediate value. -(rule 3 (lower (has_type ty (scalar_to_vector (i16_from_value x)))) +(rule 3 (lower (has_type ty (scalar_to_vector _ (i16_from_value x)))) (vec_insert_lane_imm ty (vec_imm ty 0) x (be_lane_idx ty 0))) ;; Load scalar value from big-endian memory. -(rule 4 (lower (has_type ty (scalar_to_vector (sinkable_load x)))) +(rule 4 (lower (has_type ty (scalar_to_vector _ (sinkable_load x)))) (vec_load_lane ty (vec_imm ty 0) (sink_load x) (be_lane_idx ty 0))) ;; Load scalar value lane from little-endian memory. -(rule 5 (lower (has_type ty (scalar_to_vector (sinkable_load_little x)))) +(rule 5 (lower (has_type ty (scalar_to_vector _ (sinkable_load_little x)))) (vec_load_lane_little ty (vec_imm ty 0) (sink_load x) (be_lane_idx ty 0))) @@ -2456,157 +2456,157 @@ ;;;; Rules for `shuffle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; General case: use vec_permute and then mask off zero lanes. -(rule -2 (lower (shuffle x y (shuffle_mask permute_mask and_mask))) +(rule -2 (lower (shuffle _ x y (shuffle_mask permute_mask and_mask))) (vec_and $I8X16 (vec_imm_byte_mask $I8X16 and_mask) (vec_permute $I8X16 x y (vec_imm $I8X16 permute_mask)))) ;; If the pattern has no zero lanes, just a vec_permute suffices. -(rule -1 (lower (shuffle x y (shuffle_mask permute_mask 65535))) +(rule -1 (lower (shuffle _ x y (shuffle_mask permute_mask 65535))) (vec_permute $I8X16 x y (vec_imm $I8X16 permute_mask))) ;; Special patterns that can be implemented via MERGE HIGH. -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23) 65535))) (vec_merge_high $I64X2 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 1 2 3 16 17 18 19 4 5 6 7 20 21 22 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 1 2 3 16 17 18 19 4 5 6 7 20 21 22 23) 65535))) (vec_merge_high $I32X4 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 1 16 17 2 3 18 19 4 5 20 21 6 7 22 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 1 16 17 2 3 18 19 4 5 20 21 6 7 22 23) 65535))) (vec_merge_high $I16X8 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 16 1 17 2 18 3 19 4 20 5 21 6 22 7 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 16 1 17 2 18 3 19 4 20 5 21 6 22 7 23) 65535))) (vec_merge_high $I8X16 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 17 18 19 20 21 22 23 0 1 2 3 4 5 6 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 17 18 19 20 21 22 23 0 1 2 3 4 5 6 7) 65535))) (vec_merge_high $I64X2 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 17 18 19 0 1 2 3 20 21 22 23 4 5 6 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 17 18 19 0 1 2 3 20 21 22 23 4 5 6 7) 65535))) (vec_merge_high $I32X4 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 17 0 1 18 19 2 3 20 21 4 5 22 23 6 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 17 0 1 18 19 2 3 20 21 4 5 22 23 6 7) 65535))) (vec_merge_high $I16X8 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 0 17 1 18 2 19 3 20 4 21 5 22 6 23 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 0 17 1 18 2 19 3 20 4 21 5 22 6 23 7) 65535))) (vec_merge_high $I8X16 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7) 65535))) (vec_merge_high $I64X2 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 1 2 3 0 1 2 3 4 5 6 7 4 5 6 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 1 2 3 0 1 2 3 4 5 6 7 4 5 6 7) 65535))) (vec_merge_high $I32X4 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 1 0 1 2 3 2 3 4 5 4 5 6 7 6 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 1 0 1 2 3 2 3 4 5 4 5 6 7 6 7) 65535))) (vec_merge_high $I16X8 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7) 65535))) (vec_merge_high $I8X16 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 17 18 19 20 21 22 23 16 17 18 19 20 21 22 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 17 18 19 20 21 22 23 16 17 18 19 20 21 22 23) 65535))) (vec_merge_high $I64X2 y y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 17 18 19 16 17 18 19 20 21 22 23 20 21 22 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 17 18 19 16 17 18 19 20 21 22 23 20 21 22 23) 65535))) (vec_merge_high $I32X4 y y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 17 16 17 18 19 18 19 20 21 20 21 22 23 22 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 17 16 17 18 19 18 19 20 21 20 21 22 23 22 23) 65535))) (vec_merge_high $I16X8 y y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 16 17 17 18 18 19 19 20 20 21 21 22 22 23 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 16 17 17 18 18 19 19 20 20 21 21 22 22 23 23) 65535))) (vec_merge_high $I8X16 y y)) ;; Special patterns that can be implemented via MERGE LOW. -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31) 65535))) (vec_merge_low $I64X2 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 9 10 11 24 25 26 27 12 13 14 15 28 29 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 9 10 11 24 25 26 27 12 13 14 15 28 29 30 31) 65535))) (vec_merge_low $I32X4 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 9 24 25 10 11 26 27 12 13 28 29 14 15 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 9 24 25 10 11 26 27 12 13 28 29 14 15 30 31) 65535))) (vec_merge_low $I16X8 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 24 9 25 10 26 11 27 12 28 13 29 14 30 15 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 24 9 25 10 26 11 27 12 28 13 29 14 30 15 31) 65535))) (vec_merge_low $I8X16 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 25 26 27 28 29 30 31 8 9 10 11 12 13 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 25 26 27 28 29 30 31 8 9 10 11 12 13 14 15) 65535))) (vec_merge_low $I64X2 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 25 26 27 8 9 10 11 28 29 30 31 12 13 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 25 26 27 8 9 10 11 28 29 30 31 12 13 14 15) 65535))) (vec_merge_low $I32X4 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 25 8 9 26 27 10 11 28 29 12 13 30 31 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 25 8 9 26 27 10 11 28 29 12 13 30 31 14 15) 65535))) (vec_merge_low $I16X8 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 8 25 9 26 10 27 11 28 12 29 13 30 14 31 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 8 25 9 26 10 27 11 28 12 29 13 30 14 31 15) 65535))) (vec_merge_low $I8X16 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 9 10 11 12 13 14 15 8 9 10 11 12 13 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 9 10 11 12 13 14 15 8 9 10 11 12 13 14 15) 65535))) (vec_merge_low $I64X2 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 9 10 11 8 9 10 11 12 13 14 15 12 13 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 9 10 11 8 9 10 11 12 13 14 15 12 13 14 15) 65535))) (vec_merge_low $I32X4 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 9 8 9 10 11 10 11 12 13 12 13 14 15 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 9 8 9 10 11 10 11 12 13 12 13 14 15 14 15) 65535))) (vec_merge_low $I16X8 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15) 65535))) (vec_merge_low $I8X16 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 25 26 27 28 29 30 31 24 25 26 27 28 29 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 25 26 27 28 29 30 31 24 25 26 27 28 29 30 31) 65535))) (vec_merge_low $I64X2 y y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 25 26 27 24 25 26 27 28 29 30 31 28 29 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 25 26 27 24 25 26 27 28 29 30 31 28 29 30 31) 65535))) (vec_merge_low $I32X4 y y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 25 24 25 26 27 26 27 28 29 28 29 30 31 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 25 24 25 26 27 26 27 28 29 28 29 30 31 30 31) 65535))) (vec_merge_low $I16X8 y y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 24 25 25 26 26 27 27 28 28 29 29 30 30 31 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 24 25 25 26 26 27 27 28 28 29 29 30 30 31 31) 65535))) (vec_merge_low $I8X16 y y)) ;; Special patterns that can be implemented via PACK. -(rule (lower (shuffle x y (shuffle_mask (imm8x16 4 5 6 7 12 13 14 15 20 21 22 23 28 29 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 4 5 6 7 12 13 14 15 20 21 22 23 28 29 30 31) 65535))) (vec_pack $I64X2 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 2 3 6 7 10 11 14 15 18 19 22 23 26 27 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 2 3 6 7 10 11 14 15 18 19 22 23 26 27 30 31) 65535))) (vec_pack $I32X4 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31) 65535))) (vec_pack $I16X8 x y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 20 21 22 23 28 29 30 31 4 5 6 7 12 13 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 20 21 22 23 28 29 30 31 4 5 6 7 12 13 14 15) 65535))) (vec_pack $I64X2 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 18 19 22 23 26 27 30 31 2 3 6 7 10 11 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 18 19 22 23 26 27 30 31 2 3 6 7 10 11 14 15) 65535))) (vec_pack $I32X4 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 17 19 21 23 25 27 29 31 1 3 5 7 9 11 13 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 17 19 21 23 25 27 29 31 1 3 5 7 9 11 13 15) 65535))) (vec_pack $I16X8 y x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 4 5 6 7 12 13 14 15 4 5 6 7 12 13 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 4 5 6 7 12 13 14 15 4 5 6 7 12 13 14 15) 65535))) (vec_pack $I64X2 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 2 3 6 7 10 11 14 15 2 3 6 7 10 11 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 2 3 6 7 10 11 14 15 2 3 6 7 10 11 14 15) 65535))) (vec_pack $I32X4 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 1 3 5 7 9 11 13 15 1 3 5 7 9 11 13 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 1 3 5 7 9 11 13 15 1 3 5 7 9 11 13 15) 65535))) (vec_pack $I16X8 x x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 20 21 22 23 28 29 30 31 20 21 22 23 28 29 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 20 21 22 23 28 29 30 31 20 21 22 23 28 29 30 31) 65535))) (vec_pack $I64X2 y y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 18 19 22 23 26 27 30 31 18 19 22 23 26 27 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 18 19 22 23 26 27 30 31 18 19 22 23 26 27 30 31) 65535))) (vec_pack $I32X4 y y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 17 19 21 23 25 27 29 31 17 19 21 23 25 27 29 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 17 19 21 23 25 27 29 31 17 19 21 23 25 27 29 31) 65535))) (vec_pack $I16X8 y y)) ;; Special patterns that can be implemented via UNPACK HIGH. -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ _ _ _ 0 1 2 3 _ _ _ _ 4 5 6 7) 3855))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ _ _ _ 0 1 2 3 _ _ _ _ 4 5 6 7) 3855))) (vec_unpacku_high $I32X4 x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ _ 0 1 _ _ 2 3 _ _ 4 5 _ _ 6 7) 13107))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ _ 0 1 _ _ 2 3 _ _ 4 5 _ _ 6 7) 13107))) (vec_unpacku_high $I16X8 x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ 0 _ 1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7) 21845))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ 0 _ 1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7) 21845))) (vec_unpacku_high $I8X16 x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ _ _ _ 16 17 18 19 _ _ _ _ 20 21 22 23) 3855))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ _ _ _ 16 17 18 19 _ _ _ _ 20 21 22 23) 3855))) (vec_unpacku_high $I32X4 y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ _ 16 17 _ _ 18 19 _ _ 20 21 _ _ 22 23) 13107))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ _ 16 17 _ _ 18 19 _ _ 20 21 _ _ 22 23) 13107))) (vec_unpacku_high $I16X8 y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ 16 _ 17 _ 18 _ 19 _ 20 _ 21 _ 22 _ 23) 21845))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ 16 _ 17 _ 18 _ 19 _ 20 _ 21 _ 22 _ 23) 21845))) (vec_unpacku_high $I8X16 y)) ;; Special patterns that can be implemented via UNPACK LOW. -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ _ _ _ 8 9 10 11 _ _ _ _ 12 13 14 15) 3855))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ _ _ _ 8 9 10 11 _ _ _ _ 12 13 14 15) 3855))) (vec_unpacku_low $I32X4 x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ _ 8 9 _ _ 10 11 _ _ 12 13 _ _ 14 15) 13107))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ _ 8 9 _ _ 10 11 _ _ 12 13 _ _ 14 15) 13107))) (vec_unpacku_low $I16X8 x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ 8 _ 9 _ 10 _ 11 _ 12 _ 13 _ 14 _ 15) 21845))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ 8 _ 9 _ 10 _ 11 _ 12 _ 13 _ 14 _ 15) 21845))) (vec_unpacku_low $I8X16 x)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ _ _ _ 24 25 26 27 _ _ _ _ 28 29 30 31) 3855))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ _ _ _ 24 25 26 27 _ _ _ _ 28 29 30 31) 3855))) (vec_unpacku_low $I32X4 y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ _ 24 25 _ _ 26 27 _ _ 28 29 _ _ 30 31) 13107))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ _ 24 25 _ _ 26 27 _ _ 28 29 _ _ 30 31) 13107))) (vec_unpacku_low $I16X8 y)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 _ 24 _ 25 _ 26 _ 27 _ 28 _ 29 _ 30 _ 31) 21845))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 _ 24 _ 25 _ 26 _ 27 _ 28 _ 29 _ 30 _ 31) 21845))) (vec_unpacku_low $I8X16 y)) ;; Special patterns that can be implemented via PERMUTE DOUBLEWORD IMMEDIATE. -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 1 2 3 4 5 6 7 24 25 26 27 28 29 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 1 2 3 4 5 6 7 24 25 26 27 28 29 30 31) 65535))) (vec_permute_dw_imm $I8X16 x 0 y 1)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) 65535))) (vec_permute_dw_imm $I8X16 x 1 y 0)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 17 18 19 20 21 22 23 8 9 10 11 12 13 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 17 18 19 20 21 22 23 8 9 10 11 12 13 14 15) 65535))) (vec_permute_dw_imm $I8X16 y 0 x 1)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 25 26 27 28 29 30 31 0 1 2 3 4 5 6 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 25 26 27 28 29 30 31 0 1 2 3 4 5 6 7) 65535))) (vec_permute_dw_imm $I8X16 y 1 x 0)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) 65535))) (vec_permute_dw_imm $I8X16 x 0 x 1)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7) 65535))) (vec_permute_dw_imm $I8X16 x 1 x 0)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31) 65535))) (vec_permute_dw_imm $I8X16 y 0 y 1)) -(rule (lower (shuffle x y (shuffle_mask (imm8x16 24 25 26 27 28 29 30 31 16 17 18 19 20 21 22 23) 65535))) +(rule (lower (shuffle _ x y (shuffle_mask (imm8x16 24 25 26 27 28 29 30 31 16 17 18 19 20 21 22 23) 65535))) (vec_permute_dw_imm $I8X16 y 1 y 0)) ;;;; Rules for `blendv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type (and (ty_vec128 ty) (vxrs_ext3_enabled)) (blendv p x y))) +(rule 1 (lower (has_type (and (ty_vec128 ty) (vxrs_ext3_enabled)) (blendv _ p x y))) (vec_blend ty p x y)) @@ -2619,7 +2619,7 @@ ;; permute-lane-element := umin (16, swizzle-lane-element) ;; and pass a zero vector as second operand to the permute instruction. -(rule 1 (lower (has_type (ty_vec128 ty) (swizzle x y))) +(rule 1 (lower (has_type (ty_vec128 ty) (swizzle _ x y))) (if-let (LaneOrder.BigEndian) (lane_order)) (vec_permute ty x (vec_imm ty 0) (vec_umin $I8X16 (vec_imm_splat $I8X16 16) y))) @@ -2638,7 +2638,7 @@ ;; the input vector as second operand (covering lanes 16 .. 31) ;; to implement the required swizzle semantics. -(rule (lower (has_type (ty_vec128 ty) (swizzle x y))) +(rule (lower (has_type (ty_vec128 ty) (swizzle _ x y))) (if-let (LaneOrder.LittleEndian) (lane_order)) (vec_permute ty (vec_imm ty 0) x (vec_umax $I8X16 (vec_imm_splat $I8X16 239) @@ -2648,38 +2648,38 @@ ;;;; Rules for `stack_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Load the address of a stack slot. -(rule (lower (has_type ty (stack_addr stack_slot offset))) +(rule (lower (has_type ty (stack_addr _ stack_slot offset))) (stack_addr_impl ty stack_slot offset)) ;;;; Rules for `func_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Load the address of a function, target reachable via PC-relative instruction. -(rule 1 (lower (func_addr (func_ref_data _ name (RelocDistance.Near) _))) +(rule 1 (lower (func_addr _ (func_ref_data _ name (RelocDistance.Near) _))) (load_addr (memarg_symbol name 0 (memflags_trusted)))) ;; Load the address of a function, general case. -(rule (lower (func_addr (func_ref_data _ name _ _))) +(rule (lower (func_addr _ (func_ref_data _ name _ _))) (load_symbol_reloc (SymbolReloc.Absolute name 0))) ;;;; Rules for `symbol_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Load the address of a symbol, target reachable via PC-relative instruction. -(rule 1 (lower (symbol_value (symbol_value_data name (RelocDistance.Near) +(rule 1 (lower (symbol_value _ (symbol_value_data name (RelocDistance.Near) off))) (if-let offset (memarg_symbol_offset off)) (load_addr (memarg_symbol name offset (memflags_trusted)))) ;; Load the address of a symbol, general case. -(rule (lower (symbol_value (symbol_value_data name _ offset))) +(rule (lower (symbol_value _ (symbol_value_data name _ offset))) (load_symbol_reloc (SymbolReloc.Absolute name offset))) ;;;; Rules for `tls_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Load the address of a TLS symbol (ELF general-dynamic model). -(rule (lower (tls_value (symbol_value_data name _ 0))) +(rule (lower (tls_value _ (symbol_value_data name _ 0))) (if (tls_model_is_elf_gd)) (let ((symbol SymbolReloc (SymbolReloc.TlsGd name)) (got Reg (load_addr (memarg_got))) @@ -2707,74 +2707,74 @@ ;;;; Rules for `load` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Load 8-bit integers. -(rule (lower (has_type $I8 (load flags addr offset))) +(rule (lower (has_type $I8 (load _ flags addr offset))) (zext32_mem $I8 (lower_address flags addr offset))) ;; Load 16-bit big-endian integers. -(rule (lower (has_type $I16 (load flags @ (bigendian) addr offset))) +(rule (lower (has_type $I16 (load _ flags @ (bigendian) addr offset))) (zext32_mem $I16 (lower_address flags addr offset))) ;; Load 16-bit little-endian integers. -(rule -1 (lower (has_type $I16 (load flags @ (littleendian) addr offset))) +(rule -1 (lower (has_type $I16 (load _ flags @ (littleendian) addr offset))) (loadrev16 (lower_address flags addr offset))) ;; Load 32-bit big-endian integers. -(rule (lower (has_type $I32 (load flags @ (bigendian) addr offset))) +(rule (lower (has_type $I32 (load _ flags @ (bigendian) addr offset))) (load32 (lower_address flags addr offset))) ;; Load 32-bit little-endian integers. -(rule -1 (lower (has_type $I32 (load flags @ (littleendian) addr offset))) +(rule -1 (lower (has_type $I32 (load _ flags @ (littleendian) addr offset))) (loadrev32 (lower_address flags addr offset))) ;; Load 64-bit big-endian integers. -(rule (lower (has_type $I64 (load flags @ (bigendian) addr offset))) +(rule (lower (has_type $I64 (load _ flags @ (bigendian) addr offset))) (load64 (lower_address flags addr offset))) ;; Load 64-bit little-endian integers. -(rule -1 (lower (has_type $I64 (load flags @ (littleendian) addr offset))) +(rule -1 (lower (has_type $I64 (load _ flags @ (littleendian) addr offset))) (loadrev64 (lower_address flags addr offset))) ;; Load 16-bit big-endian floating-point values (as vector lane). -(rule (lower (has_type $F16 (load flags @ (bigendian) addr offset))) +(rule (lower (has_type $F16 (load _ flags @ (bigendian) addr offset))) (vec_load_lane_undef $F16X8 (lower_address flags addr offset) 0)) ;; Load 16-bit little-endian floating-point values (as vector lane). -(rule -1 (lower (has_type $F16 (load flags @ (littleendian) addr offset))) +(rule -1 (lower (has_type $F16 (load _ flags @ (littleendian) addr offset))) (vec_load_lane_little_undef $F16X8 (lower_address flags addr offset) 0)) ;; Load 32-bit big-endian floating-point values (as vector lane). -(rule (lower (has_type $F32 (load flags @ (bigendian) addr offset))) +(rule (lower (has_type $F32 (load _ flags @ (bigendian) addr offset))) (vec_load_lane_undef $F32X4 (lower_address flags addr offset) 0)) ;; Load 32-bit little-endian floating-point values (as vector lane). -(rule -1 (lower (has_type $F32 (load flags @ (littleendian) addr offset))) +(rule -1 (lower (has_type $F32 (load _ flags @ (littleendian) addr offset))) (vec_load_lane_little_undef $F32X4 (lower_address flags addr offset) 0)) ;; Load 64-bit big-endian floating-point values (as vector lane). -(rule (lower (has_type $F64 (load flags @ (bigendian) addr offset))) +(rule (lower (has_type $F64 (load _ flags @ (bigendian) addr offset))) (vec_load_lane_undef $F64X2 (lower_address flags addr offset) 0)) ;; Load 64-bit little-endian floating-point values (as vector lane). -(rule -1 (lower (has_type $F64 (load flags @ (littleendian) addr offset))) +(rule -1 (lower (has_type $F64 (load _ flags @ (littleendian) addr offset))) (vec_load_lane_little_undef $F64X2 (lower_address flags addr offset) 0)) ;; Load 128-bit big-endian vector values, BE lane order - direct load. -(rule 4 (lower (has_type (vr128_ty ty) (load flags @ (bigendian) addr offset))) +(rule 4 (lower (has_type (vr128_ty ty) (load _ flags @ (bigendian) addr offset))) (if-let (LaneOrder.BigEndian) (lane_order)) (vec_load ty (lower_address flags addr offset))) ;; Load 128-bit little-endian vector values, BE lane order - byte-reversed load. -(rule 3 (lower (has_type (vr128_ty ty) (load flags @ (littleendian) addr offset))) +(rule 3 (lower (has_type (vr128_ty ty) (load _ flags @ (littleendian) addr offset))) (if-let (LaneOrder.BigEndian) (lane_order)) (vec_load_byte_rev ty flags addr offset)) ;; Load 128-bit big-endian vector values, LE lane order - element-reversed load. -(rule 2 (lower (has_type (vr128_ty ty) (load flags @ (bigendian) addr offset))) +(rule 2 (lower (has_type (vr128_ty ty) (load _ flags @ (bigendian) addr offset))) (if-let (LaneOrder.LittleEndian) (lane_order)) (vec_load_elt_rev ty flags addr offset)) ;; Load 128-bit little-endian vector values, LE lane order - fully-reversed load. -(rule 1 (lower (has_type (vr128_ty ty) (load flags @ (littleendian) addr offset))) +(rule 1 (lower (has_type (vr128_ty ty) (load _ flags @ (littleendian) addr offset))) (if-let (LaneOrder.LittleEndian) (lane_order)) (vec_load_full_rev ty flags addr offset)) @@ -2875,22 +2875,22 @@ ;;;; Rules for `uload8` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 16- or 32-bit target types. -(rule (lower (has_type (gpr32_ty _ty) (uload8 flags addr offset))) +(rule (lower (has_type (gpr32_ty _ty) (uload8 _ flags addr offset))) (zext32_mem $I8 (lower_address flags addr offset))) ;; 64-bit target types. -(rule 1 (lower (has_type (gpr64_ty _ty) (uload8 flags addr offset))) +(rule 1 (lower (has_type (gpr64_ty _ty) (uload8 _ flags addr offset))) (zext64_mem $I8 (lower_address flags addr offset))) ;;;; Rules for `sload8` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 16- or 32-bit target types. -(rule (lower (has_type (gpr32_ty _ty) (sload8 flags addr offset))) +(rule (lower (has_type (gpr32_ty _ty) (sload8 _ flags addr offset))) (sext32_mem $I8 (lower_address flags addr offset))) ;; 64-bit target types. -(rule 1 (lower (has_type (gpr64_ty _ty) (sload8 flags addr offset))) +(rule 1 (lower (has_type (gpr64_ty _ty) (sload8 _ flags addr offset))) (sext64_mem $I8 (lower_address flags addr offset))) @@ -2898,23 +2898,23 @@ ;; 32-bit target type, big-endian source value. (rule 3 (lower (has_type (gpr32_ty _ty) - (uload16 flags @ (bigendian) addr offset))) + (uload16 _ flags @ (bigendian) addr offset))) (zext32_mem $I16 (lower_address flags addr offset))) ;; 32-bit target type, little-endian source value (via explicit extension). (rule 1 (lower (has_type (gpr32_ty _ty) - (uload16 flags @ (littleendian) addr offset))) + (uload16 _ flags @ (littleendian) addr offset))) (let ((reg16 Reg (loadrev16 (lower_address flags addr offset)))) (zext32_reg $I16 reg16))) ;; 64-bit target type, big-endian source value. (rule 4 (lower (has_type (gpr64_ty _ty) - (uload16 flags @ (bigendian) addr offset))) + (uload16 _ flags @ (bigendian) addr offset))) (zext64_mem $I16 (lower_address flags addr offset))) ;; 64-bit target type, little-endian source value (via explicit extension). (rule 2 (lower (has_type (gpr64_ty _ty) - (uload16 flags @ (littleendian) addr offset))) + (uload16 _ flags @ (littleendian) addr offset))) (let ((reg16 Reg (loadrev16 (lower_address flags addr offset)))) (zext64_reg $I16 reg16))) @@ -2923,23 +2923,23 @@ ;; 32-bit target type, big-endian source value. (rule 2 (lower (has_type (gpr32_ty _ty) - (sload16 flags @ (bigendian) addr offset))) + (sload16 _ flags @ (bigendian) addr offset))) (sext32_mem $I16 (lower_address flags addr offset))) ;; 32-bit target type, little-endian source value (via explicit extension). (rule 0 (lower (has_type (gpr32_ty _ty) - (sload16 flags @ (littleendian) addr offset))) + (sload16 _ flags @ (littleendian) addr offset))) (let ((reg16 Reg (loadrev16 (lower_address flags addr offset)))) (sext32_reg $I16 reg16))) ;; 64-bit target type, big-endian source value. (rule 3 (lower (has_type (gpr64_ty _ty) - (sload16 flags @ (bigendian) addr offset))) + (sload16 _ flags @ (bigendian) addr offset))) (sext64_mem $I16 (lower_address flags addr offset))) ;; 64-bit target type, little-endian source value (via explicit extension). (rule 1 (lower (has_type (gpr64_ty _ty) - (sload16 flags @ (littleendian) addr offset))) + (sload16 _ flags @ (littleendian) addr offset))) (let ((reg16 Reg (loadrev16 (lower_address flags addr offset)))) (sext64_reg $I16 reg16))) @@ -2948,12 +2948,12 @@ ;; 64-bit target type, big-endian source value. (rule 1 (lower (has_type (gpr64_ty _ty) - (uload32 flags @ (bigendian) addr offset))) + (uload32 _ flags @ (bigendian) addr offset))) (zext64_mem $I32 (lower_address flags addr offset))) ;; 64-bit target type, little-endian source value (via explicit extension). (rule (lower (has_type (gpr64_ty _ty) - (uload32 flags @ (littleendian) addr offset))) + (uload32 _ flags @ (littleendian) addr offset))) (let ((reg32 Reg (loadrev32 (lower_address flags addr offset)))) (zext64_reg $I32 reg32))) @@ -2962,12 +2962,12 @@ ;; 64-bit target type, big-endian source value. (rule 1 (lower (has_type (gpr64_ty _ty) - (sload32 flags @ (bigendian) addr offset))) + (sload32 _ flags @ (bigendian) addr offset))) (sext64_mem $I32 (lower_address flags addr offset))) ;; 64-bit target type, little-endian source value (via explicit extension). (rule (lower (has_type (gpr64_ty _ty) - (sload32 flags @ (littleendian) addr offset))) + (sload32 _ flags @ (littleendian) addr offset))) (let ((reg32 Reg (loadrev32 (lower_address flags addr offset)))) (sext64_reg $I32 reg32))) @@ -2975,27 +2975,27 @@ ;;;; Rules for `uloadNxM` and `sloadNxM` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Unsigned 8->16 bit extension. -(rule (lower (has_type $I16X8 (uload8x8 flags addr offset))) +(rule (lower (has_type $I16X8 (uload8x8 _ flags addr offset))) (vec_unpacku_high $I8X16 (load_v64 $I8X16 flags addr offset))) ;; Signed 8->16 bit extension. -(rule (lower (has_type $I16X8 (sload8x8 flags addr offset))) +(rule (lower (has_type $I16X8 (sload8x8 _ flags addr offset))) (vec_unpacks_high $I8X16 (load_v64 $I8X16 flags addr offset))) ;; Unsigned 16->32 bit extension. -(rule (lower (has_type $I32X4 (uload16x4 flags addr offset))) +(rule (lower (has_type $I32X4 (uload16x4 _ flags addr offset))) (vec_unpacku_high $I16X8 (load_v64 $I16X8 flags addr offset))) ;; Signed 16->32 bit extension. -(rule (lower (has_type $I32X4 (sload16x4 flags addr offset))) +(rule (lower (has_type $I32X4 (sload16x4 _ flags addr offset))) (vec_unpacks_high $I16X8 (load_v64 $I16X8 flags addr offset))) ;; Unsigned 32->64 bit extension. -(rule (lower (has_type $I64X2 (uload32x2 flags addr offset))) +(rule (lower (has_type $I64X2 (uload32x2 _ flags addr offset))) (vec_unpacku_high $I32X4 (load_v64 $I32X4 flags addr offset))) ;; Signed 32->64 bit extension. -(rule (lower (has_type $I64X2 (sload32x2 flags addr offset))) +(rule (lower (has_type $I64X2 (sload32x2 _ flags addr offset))) (vec_unpacks_high $I32X4 (load_v64 $I32X4 flags addr offset))) @@ -3312,49 +3312,49 @@ ;; Atomic AND for 32/64-bit big-endian types, using a single instruction. (rule 1 (lower (has_type (ty_32_or_64 ty) - (atomic_rmw flags @ (bigendian) (AtomicRmwOp.And) addr src))) + (atomic_rmw _ flags @ (bigendian) (AtomicRmwOp.And) addr src))) (atomic_rmw_and ty (put_in_reg src) (lower_address flags addr (zero_offset)))) ;; Atomic AND for 32/64-bit big-endian types, using byte-swapped input/output. (rule (lower (has_type (ty_32_or_64 ty) - (atomic_rmw flags @ (littleendian) (AtomicRmwOp.And) addr src))) + (atomic_rmw _ flags @ (littleendian) (AtomicRmwOp.And) addr src))) (bswap_reg ty (atomic_rmw_and ty (bswap_reg ty (put_in_reg src)) (lower_address flags addr (zero_offset))))) ;; Atomic OR for 32/64-bit big-endian types, using a single instruction. (rule 1 (lower (has_type (ty_32_or_64 ty) - (atomic_rmw flags @ (bigendian) (AtomicRmwOp.Or) addr src))) + (atomic_rmw _ flags @ (bigendian) (AtomicRmwOp.Or) addr src))) (atomic_rmw_or ty (put_in_reg src) (lower_address flags addr (zero_offset)))) ;; Atomic OR for 32/64-bit little-endian types, using byte-swapped input/output. (rule (lower (has_type (ty_32_or_64 ty) - (atomic_rmw flags @ (littleendian) (AtomicRmwOp.Or) addr src))) + (atomic_rmw _ flags @ (littleendian) (AtomicRmwOp.Or) addr src))) (bswap_reg ty (atomic_rmw_or ty (bswap_reg ty (put_in_reg src)) (lower_address flags addr (zero_offset))))) ;; Atomic XOR for 32/64-bit big-endian types, using a single instruction. (rule 1 (lower (has_type (ty_32_or_64 ty) - (atomic_rmw flags @ (bigendian) (AtomicRmwOp.Xor) addr src))) + (atomic_rmw _ flags @ (bigendian) (AtomicRmwOp.Xor) addr src))) (atomic_rmw_xor ty (put_in_reg src) (lower_address flags addr (zero_offset)))) ;; Atomic XOR for 32/64-bit little-endian types, using byte-swapped input/output. (rule (lower (has_type (ty_32_or_64 ty) - (atomic_rmw flags @ (littleendian) (AtomicRmwOp.Xor) addr src))) + (atomic_rmw _ flags @ (littleendian) (AtomicRmwOp.Xor) addr src))) (bswap_reg ty (atomic_rmw_xor ty (bswap_reg ty (put_in_reg src)) (lower_address flags addr (zero_offset))))) ;; Atomic ADD for 32/64-bit big-endian types, using a single instruction. (rule (lower (has_type (ty_32_or_64 ty) - (atomic_rmw flags @ (bigendian) (AtomicRmwOp.Add) addr src))) + (atomic_rmw _ flags @ (bigendian) (AtomicRmwOp.Add) addr src))) (atomic_rmw_add ty (put_in_reg src) (lower_address flags addr (zero_offset)))) ;; Atomic SUB for 32/64-bit big-endian types, using atomic ADD with negated input. (rule (lower (has_type (ty_32_or_64 ty) - (atomic_rmw flags @ (bigendian) (AtomicRmwOp.Sub) addr src))) + (atomic_rmw _ flags @ (bigendian) (AtomicRmwOp.Sub) addr src))) (atomic_rmw_add ty (neg_reg ty (put_in_reg src)) (lower_address flags addr (zero_offset)))) @@ -3362,7 +3362,7 @@ ;; Atomic operations that require a compare-and-swap loop. ;; Operations for 32/64-bit types can use a fullword compare-and-swap loop. -(rule -1 (lower (has_type (ty_32_or_64 ty) (atomic_rmw flags op addr src))) +(rule -1 (lower (has_type (ty_32_or_64 ty) (atomic_rmw _ flags op addr src))) (let ((src_reg Reg (put_in_reg src)) (addr_reg Reg (put_in_reg addr)) ;; Create body of compare-and-swap loop. @@ -3374,7 +3374,7 @@ (casloop ib ty flags addr_reg val1))) ;; Operations for 8/16-bit types must operate on the surrounding aligned word. -(rule -2 (lower (has_type (ty_8_or_16 ty) (atomic_rmw flags op addr src))) +(rule -2 (lower (has_type (ty_8_or_16 ty) (atomic_rmw _ flags op addr src))) (let ((src_reg Reg (put_in_reg src)) (addr_reg Reg (put_in_reg addr)) ;; Prepare access to surrounding aligned word. @@ -3568,20 +3568,20 @@ ;; 32/64-bit big-endian atomic compare-and-swap instruction. (rule 2 (lower (has_type (ty_32_or_64 ty) - (atomic_cas flags @ (bigendian) addr src1 src2))) + (atomic_cas _ flags @ (bigendian) addr src1 src2))) (atomic_cas_impl ty (put_in_reg src1) (put_in_reg src2) (lower_address flags addr (zero_offset)))) ;; 32/64-bit little-endian atomic compare-and-swap instruction. ;; Implemented by byte-swapping old/new inputs and the output. (rule 1 (lower (has_type (ty_32_or_64 ty) - (atomic_cas flags @ (littleendian) addr src1 src2))) + (atomic_cas _ flags @ (littleendian) addr src1 src2))) (bswap_reg ty (atomic_cas_impl ty (bswap_reg ty (put_in_reg src1)) (bswap_reg ty (put_in_reg src2)) (lower_address flags addr (zero_offset))))) ;; 8/16-bit atomic compare-and-swap implemented via loop. -(rule (lower (has_type (ty_8_or_16 ty) (atomic_cas flags addr src1 src2))) +(rule (lower (has_type (ty_8_or_16 ty) (atomic_cas _ flags addr src1 src2))) (let ((src1_reg Reg (put_in_reg src1)) (src2_reg Reg (put_in_reg src2)) (addr_reg Reg (put_in_reg addr)) @@ -3638,31 +3638,31 @@ ;; Atomic loads can be implemented via regular loads on this platform. ;; 8-bit atomic load. -(rule (lower (has_type $I8 (atomic_load flags addr))) +(rule (lower (has_type $I8 (atomic_load _ flags addr))) (zext32_mem $I8 (lower_address flags addr (zero_offset)))) ;; 16-bit big-endian atomic load. -(rule 1 (lower (has_type $I16 (atomic_load flags @ (bigendian) addr))) +(rule 1 (lower (has_type $I16 (atomic_load _ flags @ (bigendian) addr))) (zext32_mem $I16 (lower_address flags addr (zero_offset)))) ;; 16-bit little-endian atomic load. -(rule (lower (has_type $I16 (atomic_load flags @ (littleendian) addr))) +(rule (lower (has_type $I16 (atomic_load _ flags @ (littleendian) addr))) (loadrev16 (lower_address flags addr (zero_offset)))) ;; 32-bit big-endian atomic load. -(rule 1 (lower (has_type $I32 (atomic_load flags @ (bigendian) addr))) +(rule 1 (lower (has_type $I32 (atomic_load _ flags @ (bigendian) addr))) (load32 (lower_address flags addr (zero_offset)))) ;; 32-bit little-endian atomic load. -(rule (lower (has_type $I32 (atomic_load flags @ (littleendian) addr))) +(rule (lower (has_type $I32 (atomic_load _ flags @ (littleendian) addr))) (loadrev32 (lower_address flags addr (zero_offset)))) ;; 64-bit big-endian atomic load. -(rule 1 (lower (has_type $I64 (atomic_load flags @ (bigendian) addr))) +(rule 1 (lower (has_type $I64 (atomic_load _ flags @ (bigendian) addr))) (load64 (lower_address flags addr (zero_offset)))) ;; 64-bit little-endian atomic load. -(rule (lower (has_type $I64 (atomic_load flags @ (littleendian) addr))) +(rule (lower (has_type $I64 (atomic_load _ flags @ (littleendian) addr))) (loadrev64 (lower_address flags addr (zero_offset)))) @@ -3727,7 +3727,7 @@ ;; Main `icmp` entry point. Generate a `ProducesBool` capturing the ;; integer comparison and immediately lower it to a 0/1 integer result. ;; In this case, it is safe to sink memory loads. -(rule -1 (lower (has_type (fits_in_64 ty) (icmp int_cc x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (icmp _ int_cc x y))) (lower_bool ty (icmp_val true int_cc x y))) @@ -3857,25 +3857,25 @@ ;; Vector `icmp` produces a boolean vector. ;; We need to handle the various IntCC flags separately here. -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.Equal) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.Equal) x y))) (vec_cmpeq ty x y)) -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.NotEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.NotEqual) x y))) (vec_not ty (vec_cmpeq ty x y))) -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.SignedGreaterThan) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.SignedGreaterThan) x y))) (vec_cmph ty x y)) -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.SignedLessThanOrEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.SignedLessThanOrEqual) x y))) (vec_not ty (vec_cmph ty x y))) -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.SignedLessThan) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.SignedLessThan) x y))) (vec_cmph ty y x)) -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.SignedGreaterThanOrEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.SignedGreaterThanOrEqual) x y))) (vec_not ty (vec_cmph ty y x))) -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.UnsignedGreaterThan) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.UnsignedGreaterThan) x y))) (vec_cmphl ty x y)) -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.UnsignedLessThanOrEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.UnsignedLessThanOrEqual) x y))) (vec_not ty (vec_cmphl ty x y))) -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.UnsignedLessThan) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.UnsignedLessThan) x y))) (vec_cmphl ty y x)) -(rule (lower (has_type (ty_vec128 ty) (icmp (IntCC.UnsignedGreaterThanOrEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (icmp _ (IntCC.UnsignedGreaterThanOrEqual) x y))) (vec_not ty (vec_cmphl ty y x))) @@ -3883,7 +3883,7 @@ ;; Main `fcmp` entry point. Generate a `ProducesBool` capturing the ;; integer comparison and immediately lower it to a 0/1 integer result. -(rule -1 (lower (has_type (fits_in_64 ty) (fcmp float_cc x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (fcmp _ float_cc x y))) (lower_bool ty (fcmp_val float_cc x y))) ;; Return a `ProducesBool` to implement any floating-point comparison. @@ -3895,33 +3895,33 @@ ;; Vector `fcmp` produces a boolean vector. ;; We need to handle the various FloatCC flags separately here. -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.Equal) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.Equal) x y))) (vec_fcmpeq ty x y)) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.NotEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.NotEqual) x y))) (vec_not ty (vec_fcmpeq ty x y))) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.GreaterThan) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.GreaterThan) x y))) (vec_fcmph ty x y)) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.UnorderedOrLessThanOrEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.UnorderedOrLessThanOrEqual) x y))) (vec_not ty (vec_fcmph ty x y))) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.GreaterThanOrEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.GreaterThanOrEqual) x y))) (vec_fcmphe ty x y)) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.UnorderedOrLessThan) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.UnorderedOrLessThan) x y))) (vec_not ty (vec_fcmphe ty x y))) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.LessThan) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.LessThan) x y))) (vec_fcmph ty y x)) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.UnorderedOrGreaterThanOrEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.UnorderedOrGreaterThanOrEqual) x y))) (vec_not ty (vec_fcmph ty y x))) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.LessThanOrEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.LessThanOrEqual) x y))) (vec_fcmphe ty y x)) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.UnorderedOrGreaterThan) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.UnorderedOrGreaterThan) x y))) (vec_not ty (vec_fcmphe ty y x))) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.Ordered) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.Ordered) x y))) (vec_or ty (vec_fcmphe ty x y) (vec_fcmphe ty y x))) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.Unordered) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.Unordered) x y))) (vec_not_or ty (vec_fcmphe ty x y) (vec_fcmphe ty y x))) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.OrderedNotEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.OrderedNotEqual) x y))) (vec_or ty (vec_fcmph ty x y) (vec_fcmph ty y x))) -(rule (lower (has_type (ty_vec128 ty) (fcmp (FloatCC.UnorderedOrEqual) x y))) +(rule (lower (has_type (ty_vec128 ty) (fcmp _ (FloatCC.UnorderedOrEqual) x y))) (vec_not_or ty (vec_fcmph ty x y) (vec_fcmph ty y x))) @@ -3929,7 +3929,7 @@ ;; Main `vall_true` entry point. Generate a `ProducesBool` capturing the ;; comparison and immediately lower it to a 0/1 integer result. -(rule (lower (has_type (fits_in_64 ty) (vall_true x))) +(rule (lower (has_type (fits_in_64 ty) (vall_true _ x))) (lower_bool ty (vall_true_val x))) ;; Return a `ProducesBool` to implement `vall_true`. @@ -3939,66 +3939,66 @@ (floatcc_as_cond (FloatCC.Unordered)))) ;; Short-circuit `vall_true` on the result of a `icmp`. -(rule (vall_true_val (has_type ty (icmp (IntCC.Equal) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.Equal) x y))) (bool (vec_cmpeqs ty x y) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (icmp (IntCC.NotEqual) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.NotEqual) x y))) (bool (vec_cmpeqs ty x y) (floatcc_as_cond (FloatCC.Unordered)))) -(rule (vall_true_val (has_type ty (icmp (IntCC.SignedGreaterThan) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.SignedGreaterThan) x y))) (bool (vec_cmphs ty x y) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (icmp (IntCC.SignedLessThanOrEqual) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.SignedLessThanOrEqual) x y))) (bool (vec_cmphs ty x y) (floatcc_as_cond (FloatCC.Unordered)))) -(rule (vall_true_val (has_type ty (icmp (IntCC.SignedLessThan) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.SignedLessThan) x y))) (bool (vec_cmphs ty y x) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (icmp (IntCC.SignedGreaterThanOrEqual) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.SignedGreaterThanOrEqual) x y))) (bool (vec_cmphs ty y x) (floatcc_as_cond (FloatCC.Unordered)))) -(rule (vall_true_val (has_type ty (icmp (IntCC.UnsignedGreaterThan) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.UnsignedGreaterThan) x y))) (bool (vec_cmphls ty x y) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (icmp (IntCC.UnsignedLessThanOrEqual) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.UnsignedLessThanOrEqual) x y))) (bool (vec_cmphls ty x y) (floatcc_as_cond (FloatCC.Unordered)))) -(rule (vall_true_val (has_type ty (icmp (IntCC.UnsignedLessThan) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.UnsignedLessThan) x y))) (bool (vec_cmphls ty y x) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (icmp (IntCC.UnsignedGreaterThanOrEqual) x y))) +(rule (vall_true_val (has_type ty (icmp _ (IntCC.UnsignedGreaterThanOrEqual) x y))) (bool (vec_cmphls ty y x) (floatcc_as_cond (FloatCC.Unordered)))) ;; Short-circuit `vall_true` on the result of a `fcmp` where possible. -(rule (vall_true_val (has_type ty (fcmp (FloatCC.Equal) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.Equal) x y))) (bool (vec_fcmpeqs ty x y) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (fcmp (FloatCC.NotEqual) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.NotEqual) x y))) (bool (vec_fcmpeqs ty x y) (floatcc_as_cond (FloatCC.Unordered)))) -(rule (vall_true_val (has_type ty (fcmp (FloatCC.GreaterThan) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.GreaterThan) x y))) (bool (vec_fcmphs ty x y) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (fcmp (FloatCC.UnorderedOrLessThanOrEqual) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.UnorderedOrLessThanOrEqual) x y))) (bool (vec_fcmphs ty x y) (floatcc_as_cond (FloatCC.Unordered)))) -(rule (vall_true_val (has_type ty (fcmp (FloatCC.GreaterThanOrEqual) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.GreaterThanOrEqual) x y))) (bool (vec_fcmphes ty x y) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (fcmp (FloatCC.UnorderedOrLessThan) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.UnorderedOrLessThan) x y))) (bool (vec_fcmphes ty x y) (floatcc_as_cond (FloatCC.Unordered)))) -(rule (vall_true_val (has_type ty (fcmp (FloatCC.LessThan) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.LessThan) x y))) (bool (vec_fcmphs ty y x) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (fcmp (FloatCC.UnorderedOrGreaterThanOrEqual) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.UnorderedOrGreaterThanOrEqual) x y))) (bool (vec_fcmphs ty y x) (floatcc_as_cond (FloatCC.Unordered)))) -(rule (vall_true_val (has_type ty (fcmp (FloatCC.LessThanOrEqual) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.LessThanOrEqual) x y))) (bool (vec_fcmphes ty y x) (floatcc_as_cond (FloatCC.Equal)))) -(rule (vall_true_val (has_type ty (fcmp (FloatCC.UnorderedOrGreaterThan) x y))) +(rule (vall_true_val (has_type ty (fcmp _ (FloatCC.UnorderedOrGreaterThan) x y))) (bool (vec_fcmphes ty y x) (floatcc_as_cond (FloatCC.Unordered)))) @@ -4007,7 +4007,7 @@ ;; Main `vany_true` entry point. Generate a `ProducesBool` capturing the ;; comparison and immediately lower it to a 0/1 integer result. -(rule (lower (has_type (fits_in_64 ty) (vany_true x))) +(rule (lower (has_type (fits_in_64 ty) (vany_true _ x))) (lower_bool ty (vany_true_val x))) ;; Return a `ProducesBool` to implement `vany_true`. @@ -4017,111 +4017,111 @@ (floatcc_as_cond (FloatCC.NotEqual)))) ;; Short-circuit `vany_true` on the result of a `icmp`. -(rule (vany_true_val (has_type ty (icmp (IntCC.Equal) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.Equal) x y))) (bool (vec_cmpeqs ty x y) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (icmp (IntCC.NotEqual) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.NotEqual) x y))) (bool (vec_cmpeqs ty x y) (floatcc_as_cond (FloatCC.NotEqual)))) -(rule (vany_true_val (has_type ty (icmp (IntCC.SignedGreaterThan) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.SignedGreaterThan) x y))) (bool (vec_cmphs ty x y) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (icmp (IntCC.SignedLessThanOrEqual) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.SignedLessThanOrEqual) x y))) (bool (vec_cmphs ty x y) (floatcc_as_cond (FloatCC.NotEqual)))) -(rule (vany_true_val (has_type ty (icmp (IntCC.SignedLessThan) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.SignedLessThan) x y))) (bool (vec_cmphs ty y x) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (icmp (IntCC.SignedGreaterThanOrEqual) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.SignedGreaterThanOrEqual) x y))) (bool (vec_cmphs ty y x) (floatcc_as_cond (FloatCC.NotEqual)))) -(rule (vany_true_val (has_type ty (icmp (IntCC.UnsignedGreaterThan) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.UnsignedGreaterThan) x y))) (bool (vec_cmphls ty x y) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (icmp (IntCC.UnsignedLessThanOrEqual) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.UnsignedLessThanOrEqual) x y))) (bool (vec_cmphls ty x y) (floatcc_as_cond (FloatCC.NotEqual)))) -(rule (vany_true_val (has_type ty (icmp (IntCC.UnsignedLessThan) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.UnsignedLessThan) x y))) (bool (vec_cmphls ty y x) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (icmp (IntCC.UnsignedGreaterThanOrEqual) x y))) +(rule (vany_true_val (has_type ty (icmp _ (IntCC.UnsignedGreaterThanOrEqual) x y))) (bool (vec_cmphls ty y x) (floatcc_as_cond (FloatCC.NotEqual)))) ;; Short-circuit `vany_true` on the result of a `fcmp` where possible. -(rule (vany_true_val (has_type ty (fcmp (FloatCC.Equal) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.Equal) x y))) (bool (vec_fcmpeqs ty x y) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (fcmp (FloatCC.NotEqual) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.NotEqual) x y))) (bool (vec_fcmpeqs ty x y) (floatcc_as_cond (FloatCC.NotEqual)))) -(rule (vany_true_val (has_type ty (fcmp (FloatCC.GreaterThan) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.GreaterThan) x y))) (bool (vec_fcmphs ty x y) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (fcmp (FloatCC.UnorderedOrLessThanOrEqual) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.UnorderedOrLessThanOrEqual) x y))) (bool (vec_fcmphs ty x y) (floatcc_as_cond (FloatCC.NotEqual)))) -(rule (vany_true_val (has_type ty (fcmp (FloatCC.GreaterThanOrEqual) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.GreaterThanOrEqual) x y))) (bool (vec_fcmphes ty x y) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (fcmp (FloatCC.UnorderedOrLessThan) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.UnorderedOrLessThan) x y))) (bool (vec_fcmphes ty x y) (floatcc_as_cond (FloatCC.NotEqual)))) -(rule (vany_true_val (has_type ty (fcmp (FloatCC.LessThan) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.LessThan) x y))) (bool (vec_fcmphs ty y x) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (fcmp (FloatCC.UnorderedOrGreaterThanOrEqual) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.UnorderedOrGreaterThanOrEqual) x y))) (bool (vec_fcmphs ty y x) (floatcc_as_cond (FloatCC.NotEqual)))) -(rule (vany_true_val (has_type ty (fcmp (FloatCC.LessThanOrEqual) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.LessThanOrEqual) x y))) (bool (vec_fcmphes ty y x) (floatcc_as_cond (FloatCC.Ordered)))) -(rule (vany_true_val (has_type ty (fcmp (FloatCC.UnorderedOrGreaterThan) x y))) +(rule (vany_true_val (has_type ty (fcmp _ (FloatCC.UnorderedOrGreaterThan) x y))) (bool (vec_fcmphes ty y x) (floatcc_as_cond (FloatCC.NotEqual)))) ;;;; Rules for `vhigh_bits` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (vhigh_bits x @ (value_type (multi_lane 8 16)))) +(rule (lower (vhigh_bits _ x @ (value_type (multi_lane 8 16)))) (if-let (LaneOrder.LittleEndian) (lane_order)) (let ((mask Reg (vec_imm $I8X16 (imm8x16 0 8 16 24 32 40 48 56 64 72 80 88 96 104 112 120)))) (vec_extract_lane $I64X2 (vec_bitpermute x mask) 0 (zero_reg)))) -(rule 1 (lower (vhigh_bits x @ (value_type (multi_lane 8 16)))) +(rule 1 (lower (vhigh_bits _ x @ (value_type (multi_lane 8 16)))) (if-let (LaneOrder.BigEndian) (lane_order)) (let ((mask Reg (vec_imm $I8X16 (imm8x16 120 112 104 96 88 80 72 64 56 48 40 32 24 16 8 0)))) (vec_extract_lane $I64X2 (vec_bitpermute x mask) 0 (zero_reg)))) -(rule (lower (vhigh_bits x @ (value_type (multi_lane 16 8)))) +(rule (lower (vhigh_bits _ x @ (value_type (multi_lane 16 8)))) (if-let (LaneOrder.LittleEndian) (lane_order)) (let ((mask Reg (vec_imm $I8X16 (imm8x16 128 128 128 128 128 128 128 128 0 16 32 48 64 80 96 112)))) (vec_extract_lane $I64X2 (vec_bitpermute x mask) 0 (zero_reg)))) -(rule 1 (lower (vhigh_bits x @ (value_type (multi_lane 16 8)))) +(rule 1 (lower (vhigh_bits _ x @ (value_type (multi_lane 16 8)))) (if-let (LaneOrder.BigEndian) (lane_order)) (let ((mask Reg (vec_imm $I8X16 (imm8x16 128 128 128 128 128 128 128 128 112 96 80 64 48 32 16 0)))) (vec_extract_lane $I64X2 (vec_bitpermute x mask) 0 (zero_reg)))) -(rule (lower (vhigh_bits x @ (value_type (multi_lane 32 4)))) +(rule (lower (vhigh_bits _ x @ (value_type (multi_lane 32 4)))) (if-let (LaneOrder.LittleEndian) (lane_order)) (let ((mask Reg (vec_imm $I8X16 (imm8x16 128 128 128 128 128 128 128 128 128 128 128 128 0 32 64 96)))) (vec_extract_lane $I64X2 (vec_bitpermute x mask) 0 (zero_reg)))) -(rule 1 (lower (vhigh_bits x @ (value_type (multi_lane 32 4)))) +(rule 1 (lower (vhigh_bits _ x @ (value_type (multi_lane 32 4)))) (if-let (LaneOrder.BigEndian) (lane_order)) (let ((mask Reg (vec_imm $I8X16 (imm8x16 128 128 128 128 128 128 128 128 128 128 128 128 96 64 32 0)))) (vec_extract_lane $I64X2 (vec_bitpermute x mask) 0 (zero_reg)))) -(rule (lower (vhigh_bits x @ (value_type (multi_lane 64 2)))) +(rule (lower (vhigh_bits _ x @ (value_type (multi_lane 64 2)))) (if-let (LaneOrder.LittleEndian) (lane_order)) (let ((mask Reg (vec_imm $I8X16 (imm8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 0 64)))) (vec_extract_lane $I64X2 (vec_bitpermute x mask) 0 (zero_reg)))) -(rule 1 (lower (vhigh_bits x @ (value_type (multi_lane 64 2)))) +(rule 1 (lower (vhigh_bits _ x @ (value_type (multi_lane 64 2)))) (if-let (LaneOrder.BigEndian) (lane_order)) (let ((mask Reg (vec_imm $I8X16 (imm8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 64 0)))) @@ -4135,8 +4135,8 @@ ;; instruction, directly use that compare. Note that it is not safe to sink ;; memory loads here, see the `icmp` comment. (decl value_nonzero (Value) ProducesBool) -(rule (value_nonzero (icmp int_cc x y)) (icmp_val false int_cc x y)) -(rule (value_nonzero (fcmp float_cc x y)) (fcmp_val float_cc x y)) +(rule (value_nonzero (icmp _ int_cc x y)) (icmp_val false int_cc x y)) +(rule (value_nonzero (fcmp _ float_cc x y)) (fcmp_val float_cc x y)) (rule -1 (value_nonzero val @ (value_type (gpr32_ty ty))) (bool (icmps_simm16 $I32 (put_in_reg_sext32 val) 0) (intcc_as_cond (IntCC.NotEqual)))) @@ -4148,14 +4148,14 @@ (floatcc_as_cond (FloatCC.NotEqual)))) ;; Main `select` entry point. Lower the `value_nonzero` result. -(rule (lower (has_type ty (select val_cond val_true val_false))) +(rule (lower (has_type ty (select _ val_cond val_true val_false))) (select_bool_reg ty (value_nonzero val_cond) (put_in_reg val_true) (put_in_reg val_false))) ;; Special-case some float-selection instructions for min/max -(rule 1 (lower (has_type (ty_scalar_float ty) (select (maybe_uextend (fcmp (FloatCC.LessThan) x y)) x y))) +(rule 1 (lower (has_type (ty_scalar_float ty) (select _ (maybe_uextend (fcmp _ (FloatCC.LessThan) x y)) x y))) (fmin_pseudo_reg ty y x)) -(rule 2 (lower (has_type (ty_scalar_float ty) (select (maybe_uextend (fcmp (FloatCC.LessThan) y x)) x y))) +(rule 2 (lower (has_type (ty_scalar_float ty) (select _ (maybe_uextend (fcmp _ (FloatCC.LessThan) y x)) x y))) (fmax_pseudo_reg ty y x)) @@ -4164,7 +4164,7 @@ ;; We need to guarantee a conditional move instruction. But on this platform ;; this is already the best way to implement select in general, so the ;; implementation of `select_spectre_guard` is identical to `select`. -(rule (lower (has_type ty (select_spectre_guard +(rule (lower (has_type ty (select_spectre_guard _ val_cond val_true val_false))) (select_bool_reg ty (value_nonzero val_cond) (put_in_reg val_true) (put_in_reg val_false))) @@ -4256,62 +4256,62 @@ ;; remap the IntCC::UnsignedGreaterThan value that we have here as result ;; of the unsigned_add_overflow_condition call to the correct mask. -(rule 0 (lower (has_type (fits_in_64 ty) (uadd_overflow_trap x y tc))) +(rule 0 (lower (has_type (fits_in_64 ty) (uadd_overflow_trap _ x y tc))) (with_flags (add_logical_reg_with_flags_paired ty x y) (trap_if_impl (mask_as_cond 3) tc))) ;; Add a register an a zero-extended register. (rule 4 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap x (zext32_value y) tc))) + (uadd_overflow_trap _ x (zext32_value y) tc))) (with_flags (add_logical_reg_zext32_with_flags_paired ty x y) (trap_if_impl (mask_as_cond 3) tc))) (rule 8 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap (zext32_value x) y tc))) + (uadd_overflow_trap _ (zext32_value x) y tc))) (with_flags (add_logical_reg_zext32_with_flags_paired ty y x) (trap_if_impl (mask_as_cond 3) tc))) ;; Add a register and an immediate (rule 3 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap x (u32_from_value y) tc))) + (uadd_overflow_trap _ x (u32_from_value y) tc))) (with_flags (add_logical_zimm32_with_flags_paired ty x y) (trap_if_impl (mask_as_cond 3) tc))) (rule 7 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap (u32_from_value x) y tc))) + (uadd_overflow_trap _ (u32_from_value x) y tc))) (with_flags (add_logical_zimm32_with_flags_paired ty y x) (trap_if_impl (mask_as_cond 3) tc))) ;; Add a register and memory (32/64-bit types). (rule 2 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap x (sinkable_load_32_64 y) tc))) + (uadd_overflow_trap _ x (sinkable_load_32_64 y) tc))) (with_flags (add_logical_mem_with_flags_paired ty x (sink_load y)) (trap_if_impl (mask_as_cond 3) tc))) (rule 6 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap (sinkable_load_32_64 x) y tc))) + (uadd_overflow_trap _ (sinkable_load_32_64 x) y tc))) (with_flags (add_logical_mem_with_flags_paired ty y (sink_load x)) (trap_if_impl (mask_as_cond 3) tc))) ;; Add a register and zero-extended memory. (rule 1 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap x (sinkable_uload32 y) tc))) + (uadd_overflow_trap _ x (sinkable_uload32 y) tc))) (with_flags (add_logical_mem_zext32_with_flags_paired ty x (sink_uload32 y)) (trap_if_impl (mask_as_cond 3) tc))) (rule 5 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap (sinkable_uload32 x) y tc))) + (uadd_overflow_trap _ (sinkable_uload32 x) y tc))) (with_flags (add_logical_mem_zext32_with_flags_paired ty y (sink_uload32 x)) (trap_if_impl (mask_as_cond 3) tc))) ;;;; Rules for `uadd_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_32_or_64 ty) (uadd_overflow x y))) +(rule 0 (lower (has_type (ty_32_or_64 ty) (uadd_overflow _ x y))) (let ((sum Reg (add_reg ty x y)) (overflow Reg (lower_bool $I8 @@ -4428,18 +4428,18 @@ ;;;; Rules for `get_{frame,stack}_pointer` and `get_return_address` ;;;;;;;;;;;; -(rule (lower (get_stack_pointer)) +(rule (lower (get_stack_pointer _)) (sp)) -(rule (lower (get_frame_pointer)) +(rule (lower (get_frame_pointer _)) (load64 (memarg_frame_pointer_offset))) -(rule (lower (get_return_address)) +(rule (lower (get_return_address _)) (load64 (memarg_return_address_offset))) ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (s390x_label_address succ_label))) diff --git a/cranelift/codegen/src/isa/s390x/lower.rs b/cranelift/codegen/src/isa/s390x/lower.rs index 9af0b96147e5..2577c6372673 100644 --- a/cranelift/codegen/src/isa/s390x/lower.rs +++ b/cranelift/codegen/src/isa/s390x/lower.rs @@ -25,6 +25,4 @@ impl LowerBackend for S390xBackend { ) -> Option<()> { isle::lower_branch(ctx, self, ir_inst, targets) } - - type FactFlowState = (); } diff --git a/cranelift/codegen/src/isa/s390x/lower/isle.rs b/cranelift/codegen/src/isa/s390x/lower/isle.rs index ea3de8b413e1..392eaa397aa5 100644 --- a/cranelift/codegen/src/isa/s390x/lower/isle.rs +++ b/cranelift/codegen/src/isa/s390x/lower/isle.rs @@ -106,7 +106,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> { } // Adjust the stack before performing a tail call. The actual stack - // adjustment is defered to the call instruction itself, but we create + // adjustment is deferred to the call instruction itself, but we create // a temporary backchain copy in the proper place here, if necessary // for unwinding. fn abi_emit_return_call_adjust_stack(&mut self, abi: Sig) -> Unit { diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle index ef058fb988c2..1140773e8138 100644 --- a/cranelift/codegen/src/isa/x64/inst.isle +++ b/cranelift/codegen/src/isa/x64/inst.isle @@ -236,20 +236,15 @@ ;; ;; For `AtomicRmwOp::Xchg`, use `Atomic128XchgSeq` instead. ;; + ;; This requires that `mem_high` addresses 8 bytes beyond `mem_low`. + ;; ;; This instruction sequence has fixed register uses as follows: - ;; - %rax (low), %rdx (high) (written) the old value at `mem` + ;; - %rax (low), %rdx (high) (written) the old value at `mem_low` ;; - %rbx (low), %rcx (high) (written) used as temp registers to hold ;; the replacement value ;; - %rflags is written. Do not assume anything about it after the ;; instruction. - (Atomic128RmwSeq (op Atomic128RmwSeqOp) - (mem BoxSyntheticAmode) - (operand_low Gpr) - (operand_high Gpr) - (temp_low WritableGpr) - (temp_high WritableGpr) - (dst_old_low WritableGpr) - (dst_old_high WritableGpr)) + (Atomic128RmwSeq (args BoxAtomic128RmwSeqArgs)) ;; A synthetic instruction, based on a loop around a native `lock ;; cmpxchg16b` instruction. @@ -258,16 +253,14 @@ ;; replacement value is the same every time, this instruction doesn't ;; require any temporary registers. ;; + ;; This requires that `mem_high` addresses 8 bytes beyond `mem_low`. + ;; ;; This instruction sequence has fixed register uses as follows: - ;; - %rax (low), %rdx (high) (written) the old value at `mem` + ;; - %rax (low), %rdx (high) (written) the old value at `mem_low` ;; - %rbx (low), %rcx (high) (read) the replacement value ;; - %rflags is written. Do not assume anything about it after the ;; instruction. - (Atomic128XchgSeq (mem SyntheticAmode) - (operand_low Gpr) - (operand_high Gpr) - (dst_old_low WritableGpr) - (dst_old_high WritableGpr)) + (Atomic128XchgSeq (args BoxAtomic128XchgSeqArgs)) ;; ========================================= ;; Meta-instructions generating no code. @@ -338,11 +331,8 @@ (type BoxCallIndInfo extern (enum)) (type BoxReturnCallInfo extern (enum)) (type BoxReturnCallIndInfo extern (enum)) -(type BoxSyntheticAmode extern (enum)) - -(decl pure box_synthetic_amode (SyntheticAmode) BoxSyntheticAmode) -(extern constructor box_synthetic_amode box_synthetic_amode) -(convert SyntheticAmode BoxSyntheticAmode box_synthetic_amode) +(type BoxAtomic128RmwSeqArgs extern (enum)) +(type BoxAtomic128XchgSeqArgs extern (enum)) ;; Get the `OperandSize` for a given `Type`, rounding smaller types up to 32 bits. (decl operand_size_of_type_32_64 (Type) OperandSize) @@ -352,6 +342,16 @@ (decl raw_operand_size_of_type (Type) OperandSize) (extern constructor raw_operand_size_of_type raw_operand_size_of_type) +(decl atomic128_rmw_seq_args + (Atomic128RmwSeqOp SyntheticAmode SyntheticAmode Gpr Gpr WritableGpr WritableGpr WritableGpr WritableGpr) + BoxAtomic128RmwSeqArgs) +(extern constructor atomic128_rmw_seq_args atomic128_rmw_seq_args) + +(decl atomic128_xchg_seq_args + (SyntheticAmode SyntheticAmode Gpr Gpr WritableGpr WritableGpr) + BoxAtomic128XchgSeqArgs) +(extern constructor atomic128_xchg_seq_args atomic128_xchg_seq_args) + ;; Get the bit width of an `OperandSize`. (decl operand_size_bits (OperandSize) u16) (rule (operand_size_bits (OperandSize.Size8)) 8) @@ -509,11 +509,11 @@ (decl to_amode (MemFlags Value Offset32) SyntheticAmode) (rule 0 (to_amode flags base offset) (amode_imm_reg flags base offset)) -(rule 1 (to_amode flags (iadd x y) offset) +(rule 1 (to_amode flags (iadd _ x y) offset) (to_amode_add flags x y offset)) (rule 2 - (to_amode flags (stack_addr slot offset1) offset2) + (to_amode flags (stack_addr _ slot offset1) offset2) (stackslot_amode slot offset1 offset2)) ;; Same as `to_amode`, except that the base address is computed via the addition @@ -542,16 +542,16 @@ (rule to_amode_add_const_lhs 2 (to_amode_add flags (i32_from_iconst c) x offset) (if-let sum (i32_checked_add offset c)) (amode_imm_reg flags x sum)) -(rule to_amode_add_const_fold_iadd_lhs_rhs 3 (to_amode_add flags (iadd x (i32_from_iconst c)) y offset) +(rule to_amode_add_const_fold_iadd_lhs_rhs 3 (to_amode_add flags (iadd _ x (i32_from_iconst c)) y offset) (if-let sum (i32_checked_add offset c)) (amode_imm_reg_reg_shift flags x y sum)) -(rule to_amode_add_const_fold_iadd_lhs_lhs 4 (to_amode_add flags (iadd (i32_from_iconst c) x) y offset) +(rule to_amode_add_const_fold_iadd_lhs_lhs 4 (to_amode_add flags (iadd _ (i32_from_iconst c) x) y offset) (if-let sum (i32_checked_add offset c)) (amode_imm_reg_reg_shift flags x y sum)) -(rule to_amode_add_const_fold_iadd_rhs_rhs 5 (to_amode_add flags x (iadd y (i32_from_iconst c)) offset) +(rule to_amode_add_const_fold_iadd_rhs_rhs 5 (to_amode_add flags x (iadd _ y (i32_from_iconst c)) offset) (if-let sum (i32_checked_add offset c)) (amode_imm_reg_reg_shift flags x y sum)) -(rule to_amode_add_const_fold_iadd_rhs_lhs 6 (to_amode_add flags x (iadd (i32_from_iconst c) y) offset) +(rule to_amode_add_const_fold_iadd_rhs_lhs 6 (to_amode_add flags x (iadd _ (i32_from_iconst c) y) offset) (if-let sum (i32_checked_add offset c)) (amode_imm_reg_reg_shift flags x y sum)) @@ -565,7 +565,7 @@ (decl amode_imm_reg (MemFlags Value Offset32) Amode) (rule amode_imm_reg_base 0 (amode_imm_reg flags base offset) (Amode.ImmReg offset base flags)) -(rule amode_imm_reg_iadd 1 (amode_imm_reg flags (iadd x y) offset) +(rule amode_imm_reg_iadd 1 (amode_imm_reg flags (iadd _ x y) offset) (amode_imm_reg_reg_shift flags x y offset)) (spec (amode_imm_reg_reg_shift flags x y offset) @@ -579,17 +579,28 @@ (decl amode_imm_reg_reg_shift (MemFlags Value Value Offset32) Amode) (rule amode_imm_reg_reg_shift_no_shift 0 (amode_imm_reg_reg_shift flags x y offset) (Amode.ImmRegRegShift offset x y 0 flags)) ;; 0 == y<<0 == "no shift" -(rule amode_imm_reg_reg_shift_shl_rhs 1 (amode_imm_reg_reg_shift flags x (ishl y (iconst (uimm8 shift))) offset) +(rule amode_imm_reg_reg_shift_shl_rhs 1 (amode_imm_reg_reg_shift flags x (ishl _ y (iconst _ (uimm8 shift))) offset) (if-let true (u32_lt_eq shift 3)) (Amode.ImmRegRegShift offset x y shift flags)) -(rule amode_imm_reg_reg_shift_shl_lhs 2 (amode_imm_reg_reg_shift flags (ishl y (iconst (uimm8 shift))) x offset) +(rule amode_imm_reg_reg_shift_shl_lhs 2 (amode_imm_reg_reg_shift flags (ishl _ y (iconst _ (uimm8 shift))) x offset) (if-let true (u32_lt_eq shift 3)) (Amode.ImmRegRegShift offset x y shift flags)) ;; Offsetting an Amode. Used when we need to do consecutive ;; loads/stores to adjacent addresses. -(decl amode_offset (SyntheticAmode i32) SyntheticAmode) -(extern constructor amode_offset amode_offset) +;; +;; Note that the input `Amode` might have a static offset that's too large to +;; add the specified `i32` to so in the case of overflow a new `Amode` is +;; generated entirely using `lea` +(decl amode_offset (SyntheticAmode MemFlags i32) SyntheticAmode) +(rule 1 (amode_offset base _flags offset) + (if-let new (amode_try_offset base offset)) + new) +(rule 0 (amode_offset base flags offset) + (Amode.ImmReg offset (x64_lea $I64 base) flags)) + +(decl pure partial amode_try_offset (SyntheticAmode i32) SyntheticAmode) +(extern constructor amode_try_offset amode_try_offset) ;; Return a zero offset as an `Offset32`. (spec (zero_offset) (provide (= result #x00000000))) @@ -1219,7 +1230,7 @@ ;; even not generate a zero-extended move in this case. (rule 2 (extend_to_gpr src @ (value_type $I32) $I64 (ExtendKind.Zero)) (if-let true (value32_zeros_upper32 src)) - (add_range_fact src 64 0 0xffff_ffff)) + src) ;; Both extend instructions are guaranteed to load exactly the source type's size. ;; So we can use `sinkable_load_exact` here to sink loads for small types (<= 16 bits). @@ -1258,15 +1269,15 @@ ;; be a 32-bit operation, will automatically zero the upper 32-bits of the ;; destination register that `Value` is placed in. (decl pure value32_zeros_upper32 (Value) bool) -(rule (value32_zeros_upper32 (iadd _ _)) true) -(rule (value32_zeros_upper32 (isub _ _)) true) -(rule (value32_zeros_upper32 (imul _ _)) true) -(rule (value32_zeros_upper32 (band _ _)) true) -(rule (value32_zeros_upper32 (bor _ _)) true) -(rule (value32_zeros_upper32 (bxor _ _)) true) -(rule (value32_zeros_upper32 (ishl _ _)) true) -(rule (value32_zeros_upper32 (ushr _ _)) true) -(rule (value32_zeros_upper32 (uload32 _ _ _)) true) +(rule (value32_zeros_upper32 (iadd _ _ _)) true) +(rule (value32_zeros_upper32 (isub _ _ _)) true) +(rule (value32_zeros_upper32 (imul _ _ _)) true) +(rule (value32_zeros_upper32 (band _ _ _)) true) +(rule (value32_zeros_upper32 (bor _ _ _)) true) +(rule (value32_zeros_upper32 (bxor _ _ _)) true) +(rule (value32_zeros_upper32 (ishl _ _ _)) true) +(rule (value32_zeros_upper32 (ushr _ _ _)) true) +(rule (value32_zeros_upper32 (uload32 _ _ _ _)) true) (rule -1 (value32_zeros_upper32 _) false) ;;;; Helpers for Working SSE tidbits ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1355,33 +1366,14 @@ ;;;; Helpers for Emitting Loads ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Load a value into a register. -(decl x64_load (Type SyntheticAmode ExtKind) Reg) - -(rule 1 (x64_load (fits_in_32 ty) addr (ExtKind.SignExtend)) - (x64_movsx (ext_mode (ty_bytes ty) 8) - addr)) - -(rule 2 (x64_load $I64 addr _ext_kind) - (x64_movq_rm addr)) - -(rule 2 (x64_load $F32 addr _ext_kind) - (x64_movss_load addr)) - -(rule 2 (x64_load $F64 addr _ext_kind) - (x64_movsd_load addr)) - -(rule 2 (x64_load $F128 addr _ext_kind) - (x64_movdqu_load addr)) - -(rule 2 (x64_load $F32X4 addr _ext_kind) - (x64_movups_load addr)) - -(rule 2 (x64_load $F64X2 addr _ext_kind) - (x64_movupd_load addr)) - -(rule 0 (x64_load (multi_lane _bits _lanes) addr _ext_kind) - (x64_movdqu_load addr)) +;; Load a value into an XMM register. +(decl x64_load_xmm (Type SyntheticAmode) Xmm) +(rule 1 (x64_load_xmm $F32 addr) (x64_movss_load addr)) +(rule 1 (x64_load_xmm $F64 addr) (x64_movsd_load addr)) +(rule 1 (x64_load_xmm $F128 addr) (x64_movdqu_load addr)) +(rule 1 (x64_load_xmm $F32X4 addr) (x64_movups_load addr)) +(rule 1 (x64_load_xmm $F64X2 addr) (x64_movupd_load addr)) +(rule 0 (x64_load_xmm (multi_lane _bits _lanes) addr) (x64_movdqu_load addr)) (decl x64_mov (SyntheticAmode) Reg) (spec (x64_mov addr) @@ -1521,7 +1513,7 @@ ;; Load a constant into an XMM register. (decl x64_xmm_load_const (Type VCodeConstant) Xmm) (rule (x64_xmm_load_const ty const) - (x64_load ty (const_to_synthetic_amode const) (ExtKind.None))) + (x64_load_xmm ty (const_to_synthetic_amode const))) ;;;; Flag Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3442,11 +3434,11 @@ ;; Special case some instructions where lowerings directly produce condition ;; codes. -(rule 2 (is_nonzero (vall_true vec)) (is_vall_true vec)) -(rule 2 (is_nonzero (vany_true vec)) (is_vany_true vec)) -(rule 2 (is_nonzero (uextend (vall_true vec))) (is_vall_true vec)) -(rule 2 (is_nonzero (uextend (vany_true vec))) (is_vany_true vec)) -(rule 2 (is_nonzero (band a @ (value_type (ty_int (fits_in_64 ty))) b)) +(rule 2 (is_nonzero (vall_true _ vec)) (is_vall_true vec)) +(rule 2 (is_nonzero (vany_true _ vec)) (is_vany_true vec)) +(rule 2 (is_nonzero (uextend _ (vall_true _ vec))) (is_vall_true vec)) +(rule 2 (is_nonzero (uextend _ (vany_true _ vec))) (is_vany_true vec)) +(rule 2 (is_nonzero (band _ a @ (value_type (ty_int (fits_in_64 ty))) b)) (is_nonzero_band ty a b)) @@ -3455,10 +3447,10 @@ ;; avoid unbounded recursion. (decl is_nonzero_cmp (Value) CondResult) -(rule 1 (is_nonzero_cmp (fcmp cc a b)) (emit_fcmp cc a b)) -(rule 1 (is_nonzero_cmp (icmp cc a b)) (emit_cmp cc a b)) -(rule 1 (is_nonzero_cmp (uextend (fcmp cc a b))) (emit_fcmp cc a b)) -(rule 1 (is_nonzero_cmp (uextend (icmp cc a b))) (emit_cmp cc a b)) +(rule 1 (is_nonzero_cmp (fcmp _ cc a b)) (emit_fcmp cc a b)) +(rule 1 (is_nonzero_cmp (icmp _ cc a b)) (emit_cmp cc a b)) +(rule 1 (is_nonzero_cmp (uextend _ (fcmp _ cc a b))) (emit_fcmp cc a b)) +(rule 1 (is_nonzero_cmp (uextend _ (icmp _ cc a b))) (emit_cmp cc a b)) (rule 0 (is_nonzero_cmp val) (is_nonzero val)) (decl is_nonzero_band (Type Value Value) CondResult) @@ -3469,7 +3461,7 @@ ;; same semantics this requires that `a` is in a register which forces `bt` to ;; use modulo semantics for the second operand `b`, thus `put_in_gpr` is ;; manually used. -(rule 1 (is_nonzero_band (ty_32_or_64 ty) a (ishl (u64_from_iconst 1) b)) +(rule 1 (is_nonzero_band (ty_32_or_64 ty) a (ishl _ (u64_from_iconst 1) b)) (CondResult.CC (x64_bt ty (put_in_gpr a) b) (CC.B))) ;; If a value is and'd one shifted by a variable value that matches `bt` as @@ -3712,32 +3704,38 @@ (_ Unit (emit (MInst.AtomicRmwSeq ty op mem input tmp dst)))) dst)) -(decl x64_atomic_128_rmw_seq (AtomicRmwOp SyntheticAmode ValueRegs) ValueRegs) -(rule (x64_atomic_128_rmw_seq op mem input) +(decl x64_atomic_128_rmw_seq (AtomicRmwOp SyntheticAmode MemFlags ValueRegs) ValueRegs) +(rule (x64_atomic_128_rmw_seq op mem_low flags input) (let ((dst_low WritableGpr (temp_writable_gpr)) (dst_high WritableGpr (temp_writable_gpr)) (tmp_low WritableGpr (temp_writable_gpr)) (tmp_high WritableGpr (temp_writable_gpr)) (input_low Gpr (value_regs_get_gpr input 0)) (input_high Gpr (value_regs_get_gpr input 1)) - (_ Unit (emit (MInst.Atomic128RmwSeq (atomic_128_rmw_seq_op op) mem input_low input_high tmp_low tmp_high dst_low dst_high)))) + (mem_high SyntheticAmode (amode_offset mem_low flags 8)) + (args BoxAtomic128RmwSeqArgs (atomic128_rmw_seq_args (atomic_128_rmw_seq_op op) mem_low mem_high input_low input_high tmp_low tmp_high dst_low dst_high)) + (_ Unit (emit (MInst.Atomic128RmwSeq args)))) (value_regs dst_low dst_high))) -(rule 1 (x64_atomic_128_rmw_seq (AtomicRmwOp.Xchg) mem input) +(rule 1 (x64_atomic_128_rmw_seq (AtomicRmwOp.Xchg) mem_low flags input) (let ((dst_low WritableGpr (temp_writable_gpr)) (dst_high WritableGpr (temp_writable_gpr)) (input_low Gpr (value_regs_get_gpr input 0)) (input_high Gpr (value_regs_get_gpr input 1)) - (_ Unit (emit (MInst.Atomic128XchgSeq mem input_low input_high dst_low dst_high)))) + (mem_high SyntheticAmode (amode_offset mem_low flags 8)) + (args BoxAtomic128XchgSeqArgs (atomic128_xchg_seq_args mem_low mem_high input_low input_high dst_low dst_high)) + (_ Unit (emit (MInst.Atomic128XchgSeq args)))) (value_regs dst_low dst_high))) -(decl x64_atomic_128_store_seq (SyntheticAmode ValueRegs) SideEffectNoResult) -(rule (x64_atomic_128_store_seq mem input) +(decl x64_atomic_128_store_seq (SyntheticAmode MemFlags ValueRegs) SideEffectNoResult) +(rule (x64_atomic_128_store_seq mem_low flags input) (let ((dst_low WritableGpr (temp_writable_gpr)) (dst_high WritableGpr (temp_writable_gpr)) (input_low Gpr (value_regs_get_gpr input 0)) - (input_high Gpr (value_regs_get_gpr input 1))) - (SideEffectNoResult.Inst (MInst.Atomic128XchgSeq mem input_low input_high dst_low dst_high)))) + (input_high Gpr (value_regs_get_gpr input 1)) + (mem_high SyntheticAmode (amode_offset mem_low flags 8)) + (args BoxAtomic128XchgSeqArgs (atomic128_xchg_seq_args mem_low mem_high input_low input_high dst_low dst_high))) + (SideEffectNoResult.Inst (MInst.Atomic128XchgSeq args)))) (type AtomicRmwSeqOp diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs index 233299966a16..7e3b7394b9d7 100644 --- a/cranelift/codegen/src/isa/x64/inst/args.rs +++ b/cranelift/codegen/src/isa/x64/inst/args.rs @@ -439,14 +439,14 @@ impl Amode { } /// Offset the amode by a fixed offset. - pub(crate) fn offset(&self, offset: i32) -> Self { + pub(crate) fn offset(&self, offset: i32) -> Option { let mut ret = self.clone(); - match &mut ret { - &mut Amode::ImmReg { ref mut simm32, .. } => *simm32 += offset, - &mut Amode::ImmRegRegShift { ref mut simm32, .. } => *simm32 += offset, + let simm32 = match &mut ret { + Amode::ImmReg { simm32, .. } | Amode::ImmRegRegShift { simm32, .. } => simm32, _ => panic!("Cannot offset amode: {self:?}"), - } - ret + }; + *simm32 = simm32.checked_add(offset)?; + Some(ret) } pub(crate) fn aligned(&self) -> bool { @@ -575,18 +575,18 @@ impl SyntheticAmode { } /// Offset the synthetic amode by a fixed offset. - pub(crate) fn offset(&self, offset: i32) -> Self { + pub(crate) fn offset(&self, offset: i32) -> Option { let mut ret = self.clone(); match &mut ret { - SyntheticAmode::Real(amode) => *amode = amode.offset(offset), - SyntheticAmode::SlotOffset { simm32 } => *simm32 += offset, + SyntheticAmode::Real(amode) => *amode = amode.offset(offset)?, + SyntheticAmode::SlotOffset { simm32 } => *simm32 = simm32.checked_add(offset)?, // `amode_offset` is used only in i128.load/store which // takes a synthetic amode from `to_amode`; `to_amode` can // only produce Real or SlotOffset amodes, never // IncomingArg or ConstantOffset. _ => panic!("Cannot offset SyntheticAmode: {self:?}"), } - ret + Some(ret) } } @@ -1061,3 +1061,34 @@ impl OperandSize { self.to_bytes() * 8 } } + +pub use crate::isa::x64::lower::isle::generated_code::Atomic128RmwSeqOp; + +/// "Package" of the arguments for the instruction `Atomic128RmwSeq` to avoid +/// making the `Inst` enum massive. +#[derive(Debug, Clone)] +#[expect(missing_docs, reason = "self-describing fields")] +pub struct Atomic128RmwSeqArgs { + pub op: Atomic128RmwSeqOp, + pub mem_low: SyntheticAmode, + pub mem_high: SyntheticAmode, + pub operand_low: Gpr, + pub operand_high: Gpr, + pub temp_low: WritableGpr, + pub temp_high: WritableGpr, + pub dst_old_low: WritableGpr, + pub dst_old_high: WritableGpr, +} + +/// "Package" of the arguments for the instruction `Atomic128XchgSeq` to avoid +/// making the `Inst` enum massive. +#[derive(Debug, Clone)] +#[expect(missing_docs, reason = "self-describing fields")] +pub struct Atomic128XchgSeqArgs { + pub mem_low: SyntheticAmode, + pub mem_high: SyntheticAmode, + pub operand_low: Gpr, + pub operand_high: Gpr, + pub dst_old_low: WritableGpr, + pub dst_old_high: WritableGpr, +} diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 363f9194bd6f..7f6f1aeb763d 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -1544,16 +1544,18 @@ pub(crate) fn emit( one_way_jmp(sink, CC::NZ, again_label); } - Inst::Atomic128RmwSeq { - op, - mem, - operand_low, - operand_high, - temp_low, - temp_high, - dst_old_low, - dst_old_high, - } => { + Inst::Atomic128RmwSeq { args } => { + let Atomic128RmwSeqArgs { + op, + mem_low, + mem_high, + operand_low, + operand_high, + temp_low, + temp_high, + dst_old_low, + dst_old_high, + } = &**args; let operand_low = *operand_low; let operand_high = *operand_high; let temp_low = *temp_low; @@ -1564,13 +1566,14 @@ pub(crate) fn emit( debug_assert_eq!(temp_high.to_reg(), regs::rcx()); debug_assert_eq!(dst_old_low.to_reg(), regs::rax()); debug_assert_eq!(dst_old_high.to_reg(), regs::rdx()); - let mem = mem.finalize(state.frame_layout(), sink).clone(); + let mem_low = mem_low.finalize(state.frame_layout(), sink).clone(); + let mem_high = mem_high.finalize(state.frame_layout(), sink).clone(); let again_label = sink.get_label(); // Load the initial value. - asm::inst::movq_rm::new(dst_old_low, mem.clone()).emit(sink, info, state); - asm::inst::movq_rm::new(dst_old_high, mem.offset(8)).emit(sink, info, state); + asm::inst::movq_rm::new(dst_old_low, mem_low.clone()).emit(sink, info, state); + asm::inst::movq_rm::new(dst_old_high, mem_high).emit(sink, info, state); // again: sink.bind_label(again_label, state.ctrl_plane_mut()); @@ -1656,7 +1659,7 @@ pub(crate) fn emit( PairedGpr::from(dst_old_high), temp_low.to_reg(), temp_high.to_reg(), - mem, + mem_low, ) .emit(sink, info, state); @@ -1664,13 +1667,15 @@ pub(crate) fn emit( one_way_jmp(sink, CC::NZ, again_label); } - Inst::Atomic128XchgSeq { - mem, - operand_low, - operand_high, - dst_old_low, - dst_old_high, - } => { + Inst::Atomic128XchgSeq { args } => { + let Atomic128XchgSeqArgs { + mem_low, + mem_high, + operand_low, + operand_high, + dst_old_low, + dst_old_high, + } = &**args; let operand_low = *operand_low; let operand_high = *operand_high; let dst_old_low = *dst_old_low; @@ -1679,13 +1684,14 @@ pub(crate) fn emit( debug_assert_eq!(operand_high, regs::rcx()); debug_assert_eq!(dst_old_low.to_reg(), regs::rax()); debug_assert_eq!(dst_old_high.to_reg(), regs::rdx()); - let mem = mem.finalize(state.frame_layout(), sink).clone(); + let mem_low = mem_low.finalize(state.frame_layout(), sink).clone(); + let mem_high = mem_high.finalize(state.frame_layout(), sink).clone(); let again_label = sink.get_label(); // Load the initial value. - asm::inst::movq_rm::new(dst_old_low, mem.clone()).emit(sink, info, state); - asm::inst::movq_rm::new(dst_old_high, mem.offset(8)).emit(sink, info, state); + asm::inst::movq_rm::new(dst_old_low, mem_low.clone()).emit(sink, info, state); + asm::inst::movq_rm::new(dst_old_high, mem_high).emit(sink, info, state); // again: sink.bind_label(again_label, state.ctrl_plane_mut()); @@ -1696,7 +1702,7 @@ pub(crate) fn emit( PairedGpr::from(dst_old_high), operand_low, operand_high, - mem, + mem_low, ) .emit(sink, info, state); @@ -1908,7 +1914,7 @@ fn emit_return_call_common_sequence( } } -/// Conveniene trait to have an `emit` method on all `asm::inst::*` variants. +/// Convenience trait to have an `emit` method on all `asm::inst::*` variants. trait ExternalEmit { fn emit(self, sink: &mut MachBuffer, info: &EmitInfo, state: &mut EmitState); } diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs index 1bd8b0f6b28e..77d53ba38400 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs @@ -244,72 +244,70 @@ fn test_x64_emit() { )); // Atomic128RmwSeq + let rmw_args = Atomic128RmwSeqArgs { + op: Atomic128RmwSeqOp::Or, + mem_low: am3.clone(), + mem_high: am3.offset(8).unwrap(), + operand_low: Gpr::unwrap_new(r10), + operand_high: Gpr::unwrap_new(r11), + temp_low: w_rbx.map(Gpr::unwrap_new), + temp_high: w_rcx.map(Gpr::unwrap_new), + dst_old_low: w_rax.map(Gpr::unwrap_new), + dst_old_high: w_rdx.map(Gpr::unwrap_new), + }; insns.push(( Inst::Atomic128RmwSeq { - op: Atomic128RmwSeqOp::Or, - mem: Box::new(am3.clone()), - operand_low: Gpr::unwrap_new(r10), - operand_high: Gpr::unwrap_new(r11), - temp_low: w_rbx.map(Gpr::unwrap_new), - temp_high: w_rcx.map(Gpr::unwrap_new), - dst_old_low: w_rax.map(Gpr::unwrap_new), - dst_old_high: w_rdx.map(Gpr::unwrap_new), + args: Box::new(Atomic128RmwSeqArgs { + op: Atomic128RmwSeqOp::Or, + ..rmw_args.clone() + }), }, "498B01498B51084889C34889D1490BDA490BCBF0490FC7090F85E9FFFFFF", - "atomically { %rdx:%rax = 0(%r9); %rcx:%rbx = %rdx:%rax Or %r11:%r10; 0(%r9) = %rcx:%rbx }", + "atomically { %rdx:%rax = 0(%r9):8(%r9); %rcx:%rbx = %rdx:%rax Or %r11:%r10; 0(%r9):8(%r9) = %rcx:%rbx }" )); insns.push(( Inst::Atomic128RmwSeq { - op: Atomic128RmwSeqOp::And, - mem: Box::new(am3.clone()), - operand_low: Gpr::unwrap_new(r10), - operand_high: Gpr::unwrap_new(r11), - temp_low: w_rbx.map(Gpr::unwrap_new), - temp_high: w_rcx.map(Gpr::unwrap_new), - dst_old_low: w_rax.map(Gpr::unwrap_new), - dst_old_high: w_rdx.map(Gpr::unwrap_new), + args: Box::new(Atomic128RmwSeqArgs { + op: Atomic128RmwSeqOp::And, + ..rmw_args.clone() + }), }, "498B01498B51084889C34889D14923DA4923CBF0490FC7090F85E9FFFFFF", - "atomically { %rdx:%rax = 0(%r9); %rcx:%rbx = %rdx:%rax And %r11:%r10; 0(%r9) = %rcx:%rbx }" + "atomically { %rdx:%rax = 0(%r9):8(%r9); %rcx:%rbx = %rdx:%rax And %r11:%r10; 0(%r9):8(%r9) = %rcx:%rbx }" )); insns.push(( Inst::Atomic128RmwSeq { - op: Atomic128RmwSeqOp::Umin, - mem: Box::new(am3.clone()), - operand_low: Gpr::unwrap_new(r10), - operand_high: Gpr::unwrap_new(r11), - temp_low: w_rbx.map(Gpr::unwrap_new), - temp_high: w_rcx.map(Gpr::unwrap_new), - dst_old_low: w_rax.map(Gpr::unwrap_new), - dst_old_high: w_rdx.map(Gpr::unwrap_new), + args: Box::new(Atomic128RmwSeqArgs { + op: Atomic128RmwSeqOp::Umin, + ..rmw_args.clone() + }), }, "498B01498B51084889C34889D14C39D3491BCB4889D1490F43DA490F43CBF0490FC7090F85DEFFFFFF", - "atomically { %rdx:%rax = 0(%r9); %rcx:%rbx = %rdx:%rax Umin %r11:%r10; 0(%r9) = %rcx:%rbx }" + "atomically { %rdx:%rax = 0(%r9):8(%r9); %rcx:%rbx = %rdx:%rax Umin %r11:%r10; 0(%r9):8(%r9) = %rcx:%rbx }" )); insns.push(( Inst::Atomic128RmwSeq { - op: Atomic128RmwSeqOp::Add, - mem: Box::new(am3.clone()), - operand_low: Gpr::unwrap_new(r10), - operand_high: Gpr::unwrap_new(r11), - temp_low: w_rbx.map(Gpr::unwrap_new), - temp_high: w_rcx.map(Gpr::unwrap_new), - dst_old_low: w_rax.map(Gpr::unwrap_new), - dst_old_high: w_rdx.map(Gpr::unwrap_new), + args: Box::new(Atomic128RmwSeqArgs { + op: Atomic128RmwSeqOp::Add, + ..rmw_args.clone() + }), }, "498B01498B51084889C34889D14903DA4913CBF0490FC7090F85E9FFFFFF", - "atomically { %rdx:%rax = 0(%r9); %rcx:%rbx = %rdx:%rax Add %r11:%r10; 0(%r9) = %rcx:%rbx }" + "atomically { %rdx:%rax = 0(%r9):8(%r9); %rcx:%rbx = %rdx:%rax Add %r11:%r10; 0(%r9):8(%r9) = %rcx:%rbx }", )); insns.push(( Inst::Atomic128XchgSeq { - mem: am3.clone(), - operand_low: Gpr::unwrap_new(rbx), - operand_high: Gpr::unwrap_new(rcx), - dst_old_low: w_rax.map(Gpr::unwrap_new), - dst_old_high: w_rdx.map(Gpr::unwrap_new), + args: Box::new(Atomic128XchgSeqArgs { + mem_low: am3.clone(), + mem_high: am3.offset(8).unwrap(), + operand_low: Gpr::unwrap_new(rbx), + operand_high: Gpr::unwrap_new(rcx), + dst_old_low: w_rax.map(Gpr::unwrap_new), + dst_old_high: w_rdx.map(Gpr::unwrap_new), + }), }, "498B01498B5108F0490FC7090F85F5FFFFFF", - "atomically { %rdx:%rax = 0(%r9); 0(%r9) = %rcx:%rbx }", + "atomically { %rdx:%rax = 0(%r9):8(%r9); 0(%r9):8(%r9) = %rcx:%rbx }", )); // ======================================================== diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index 8bff031e3fc4..3dc1e4614c70 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -156,7 +156,7 @@ impl Inst { Inst::External { inst } } - /// Writes the `simm64` immedaite into `dst`. + /// Writes the `simm64` immediate into `dst`. /// /// Note that if `dst_size` is less than 64-bits then the upper bits of /// `simm64` will be converted to zero. @@ -757,42 +757,48 @@ impl PrettyPrint for Inst { ) } - Inst::Atomic128RmwSeq { - op, - mem, - operand_low, - operand_high, - temp_low, - temp_high, - dst_old_low, - dst_old_high, - } => { + Inst::Atomic128RmwSeq { args } => { + let Atomic128RmwSeqArgs { + op, + mem_low, + mem_high, + operand_low, + operand_high, + temp_low, + temp_high, + dst_old_low, + dst_old_high, + } = &**args; let operand_low = pretty_print_reg(**operand_low, 8); let operand_high = pretty_print_reg(**operand_high, 8); let temp_low = pretty_print_reg(*temp_low.to_reg(), 8); let temp_high = pretty_print_reg(*temp_high.to_reg(), 8); let dst_old_low = pretty_print_reg(*dst_old_low.to_reg(), 8); let dst_old_high = pretty_print_reg(*dst_old_high.to_reg(), 8); - let mem = mem.pretty_print(16); + let mem_low = mem_low.pretty_print(16); + let mem_high = mem_high.pretty_print(16); format!( - "atomically {{ {dst_old_high}:{dst_old_low} = {mem}; {temp_high}:{temp_low} = {dst_old_high}:{dst_old_low} {op:?} {operand_high}:{operand_low}; {mem} = {temp_high}:{temp_low} }}" + "atomically {{ {dst_old_high}:{dst_old_low} = {mem_low}:{mem_high}; {temp_high}:{temp_low} = {dst_old_high}:{dst_old_low} {op:?} {operand_high}:{operand_low}; {mem_low}:{mem_high} = {temp_high}:{temp_low} }}" ) } - Inst::Atomic128XchgSeq { - mem, - operand_low, - operand_high, - dst_old_low, - dst_old_high, - } => { + Inst::Atomic128XchgSeq { args } => { + let Atomic128XchgSeqArgs { + mem_low, + mem_high, + operand_low, + operand_high, + dst_old_low, + dst_old_high, + } = &**args; let operand_low = pretty_print_reg(**operand_low, 8); let operand_high = pretty_print_reg(**operand_high, 8); let dst_old_low = pretty_print_reg(*dst_old_low.to_reg(), 8); let dst_old_high = pretty_print_reg(*dst_old_high.to_reg(), 8); - let mem = mem.pretty_print(16); + let mem_low = mem_low.pretty_print(16); + let mem_high = mem_high.pretty_print(16); format!( - "atomically {{ {dst_old_high}:{dst_old_low} = {mem}; {mem} = {operand_high}:{operand_low} }}" + "atomically {{ {dst_old_high}:{dst_old_low} = {mem_low}:{mem_high}; {mem_low}:{mem_high} = {operand_high}:{operand_low} }}" ) } @@ -1105,16 +1111,18 @@ fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { mem.get_operands_late(collector) } - Inst::Atomic128RmwSeq { - operand_low, - operand_high, - temp_low, - temp_high, - dst_old_low, - dst_old_high, - mem, - .. - } => { + Inst::Atomic128RmwSeq { args } => { + let Atomic128RmwSeqArgs { + mem_low, + mem_high, + operand_low, + operand_high, + temp_low, + temp_high, + dst_old_low, + dst_old_high, + op: _, + } = &mut **args; // All registers are collected in the `Late` position so that they don't overlap. collector.reg_late_use(operand_low); collector.reg_late_use(operand_high); @@ -1122,23 +1130,26 @@ fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { collector.reg_fixed_def(temp_high, regs::rcx()); collector.reg_fixed_def(dst_old_low, regs::rax()); collector.reg_fixed_def(dst_old_high, regs::rdx()); - mem.get_operands_late(collector) + mem_low.get_operands_late(collector); + mem_high.get_operands_late(collector); } - Inst::Atomic128XchgSeq { - operand_low, - operand_high, - dst_old_low, - dst_old_high, - mem, - .. - } => { + Inst::Atomic128XchgSeq { args } => { + let Atomic128XchgSeqArgs { + mem_low, + mem_high, + operand_low, + operand_high, + dst_old_low, + dst_old_high, + } = &mut **args; // All registers are collected in the `Late` position so that they don't overlap. collector.reg_fixed_late_use(operand_low, regs::rbx()); collector.reg_fixed_late_use(operand_high, regs::rcx()); collector.reg_fixed_def(dst_old_low, regs::rax()); collector.reg_fixed_def(dst_old_high, regs::rdx()); - mem.get_operands_late(collector) + mem_low.get_operands_late(collector); + mem_high.get_operands_late(collector); } Inst::Args { args } => { diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle index 3d09b9931d73..dfe4916b4677 100644 --- a/cranelift/codegen/src/isa/x64/lower.isle +++ b/cranelift/codegen/src/isa/x64/lower.isle @@ -15,36 +15,36 @@ ;; `i64` and smaller. (rule (lower (has_type (fits_in_64 ty) - (iconst (u64_from_imm64 x)))) + (iconst _ (u64_from_imm64 x)))) (imm ty x)) ;; `i128` (rule 1 (lower (has_type $I128 - (iconst (u64_from_imm64 x)))) + (iconst _ (u64_from_imm64 x)))) (value_regs (imm $I64 x) (imm $I64 0))) ;;;; Rules for `f16const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f16const (u16_from_ieee16 x))) +(rule (lower (f16const _ (u16_from_ieee16 x))) (imm $F16 x)) ;;;; Rules for `f32const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f32const (u32_from_ieee32 x))) +(rule (lower (f32const _ (u32_from_ieee32 x))) (imm $F32 x)) ;;;; Rules for `f64const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f64const (u64_from_ieee64 x))) +(rule (lower (f64const _ (u64_from_ieee64 x))) (imm $F64 x)) ;;;; Rules for `f128const` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (f128const const)) +(rule (lower (f128const _ const)) ;; TODO use Inst::gen_constant() instead. (x64_xmm_load_const $F128 (const_to_vconst const))) -(rule 1 (lower (f128const (u128_from_constant 0))) +(rule 1 (lower (f128const _ (u128_from_constant 0))) (xmm_zero $F128)) ;;;; Rules for `iadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -53,7 +53,7 @@ ;; Base case for 8 and 16-bit types (rule -6 (lower (has_type (fits_in_16 ty) - (iadd x y))) + (iadd _ x y))) (x64_add ty x y)) ;; Base case for 32 and 64-bit types which might end up using the `lea` @@ -63,39 +63,39 @@ ;; but the actual instruction emitted might be an `add` if it's equivalent. ;; For more details on this see the `emit.rs` logic to emit ;; `LoadEffectiveAddress`. -(rule iadd_base_case_32_or_64_lea -5 (lower (has_type (ty_32_or_64 ty) (iadd x y))) +(rule iadd_base_case_32_or_64_lea -5 (lower (has_type (ty_32_or_64 ty) (iadd _ x y))) (x64_lea ty (to_amode_add (mem_flags_trusted) x y (zero_offset)))) ;; Higher-priority cases than the previous two where a load can be sunk into ;; the add instruction itself. Note that both operands are tested for ;; sink-ability since addition is commutative (rule -4 (lower (has_type (fits_in_64 ty) - (iadd x (sinkable_load y)))) + (iadd _ x (sinkable_load y)))) (x64_add ty x y)) (rule -3 (lower (has_type (fits_in_64 ty) - (iadd (sinkable_load x) y))) + (iadd _ (sinkable_load x) y))) (x64_add ty y x)) ;; SSE. (rule (lower (has_type (multi_lane 8 16) - (iadd x y))) + (iadd _ x y))) (x64_paddb x y)) (rule (lower (has_type (multi_lane 16 8) - (iadd x y))) + (iadd _ x y))) (x64_paddw x y)) (rule (lower (has_type (multi_lane 32 4) - (iadd x y))) + (iadd _ x y))) (x64_paddd x y)) (rule (lower (has_type (multi_lane 64 2) - (iadd x y))) + (iadd _ x y))) (x64_paddq x y)) ;; `i128` -(rule 1 (lower (has_type $I128 (iadd x y))) +(rule 1 (lower (has_type $I128 (iadd _ x y))) ;; Get the high/low registers for `x`. (let ((x_regs ValueRegs x) (y_regs ValueRegs y)) @@ -104,10 +104,10 @@ (value_regs_get_gpr x_regs 1) (value_regs_get_gpr y_regs 0) (value_regs_get_gpr y_regs 1)))) -(rule 2 (lower (has_type $I128 (iadd x (iconcat y_lo y_hi)))) +(rule 2 (lower (has_type $I128 (iadd _ x (iconcat _ y_lo y_hi)))) (let ((x_regs ValueRegs x)) (iadd128 (value_regs_get_gpr x 0) (value_regs_get_gpr x 1) y_lo y_hi))) -(rule 3 (lower (has_type $I128 (iadd x (uextend y @ (value_type $I64))))) +(rule 3 (lower (has_type $I128 (iadd _ x (uextend _ y @ (value_type $I64))))) (let ((x_regs ValueRegs x)) (iadd128 (value_regs_get_gpr x 0) (value_regs_get_gpr x 1) y (RegMemImm.Imm 0)))) @@ -115,7 +115,7 @@ ;; Specialized lowering rule for `iadd` of two 64-bit unsigned integers, meaning ;; that we can skip the `adc` and instead use `setb`. This is in some sense a ;; way of modeling `uadd_overflow`. -(rule 4 (lower (has_type $I128 (iadd (uextend x) (uextend y @ (value_type $I64))))) +(rule 4 (lower (has_type $I128 (iadd _ (uextend _ x) (uextend _ y @ (value_type $I64))))) (let ( (x Gpr (extend_to_gpr x $I64 (ExtendKind.Zero))) (ret ValueRegs (with_flags (x64_add_with_flags_paired $I64 x y) @@ -173,71 +173,71 @@ ;;;; Rules for `uadd_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (uadd_overflow x y @ (value_type (fits_in_64 ty)))) +(rule 1 (lower (uadd_overflow _ x y @ (value_type (fits_in_64 ty)))) (construct_overflow_op_alu ty (CC.B) (ProduceFlagsOp.Add) x y)) ;; i128 gets lowered into adc and add -(rule 0 (lower (uadd_overflow x y @ (value_type $I128))) +(rule 0 (lower (uadd_overflow _ x y @ (value_type $I128))) (construct_overflow_op_alu_128 (CC.B) (ProduceFlagsOp.Add) (ChainFlagsOp.Adc) x y)) ;;;; Rules for `sadd_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (sadd_overflow x y @ (value_type (fits_in_64 ty)))) +(rule 1 (lower (sadd_overflow _ x y @ (value_type (fits_in_64 ty)))) (construct_overflow_op_alu ty (CC.O) (ProduceFlagsOp.Add) x y)) -(rule 0 (lower (sadd_overflow x y @ (value_type $I128))) +(rule 0 (lower (sadd_overflow _ x y @ (value_type $I128))) (construct_overflow_op_alu_128 (CC.O) (ProduceFlagsOp.Add) (ChainFlagsOp.Adc) x y)) ;;;; Rules for `usub_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (usub_overflow x y @ (value_type (fits_in_64 ty)))) +(rule 1 (lower (usub_overflow _ x y @ (value_type (fits_in_64 ty)))) (construct_overflow_op_alu ty (CC.B) (ProduceFlagsOp.Sub) x y)) -(rule 0 (lower (usub_overflow x y @ (value_type $I128))) +(rule 0 (lower (usub_overflow _ x y @ (value_type $I128))) (construct_overflow_op_alu_128 (CC.B) (ProduceFlagsOp.Sub) (ChainFlagsOp.Sbb) x y)) ;;;; Rules for `ssub_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (ssub_overflow x y @ (value_type (fits_in_64 ty)))) +(rule 1 (lower (ssub_overflow _ x y @ (value_type (fits_in_64 ty)))) (construct_overflow_op_alu ty (CC.O) (ProduceFlagsOp.Sub) x y)) -(rule 0 (lower (ssub_overflow x y @ (value_type $I128))) +(rule 0 (lower (ssub_overflow _ x y @ (value_type $I128))) (construct_overflow_op_alu_128 (CC.O) (ProduceFlagsOp.Sub) (ChainFlagsOp.Sbb) x y)) ;;;; Rules for `umul_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 2 (lower (umul_overflow x y @ (value_type $I8))) +(rule 2 (lower (umul_overflow _ x y @ (value_type $I8))) (construct_overflow_op (CC.O) (x64_mul8_with_flags_paired false x y))) -(rule 3 (lower (umul_overflow x y @ (value_type (ty_int_ref_16_to_64 ty)))) +(rule 3 (lower (umul_overflow _ x y @ (value_type (ty_int_ref_16_to_64 ty)))) (construct_overflow_op (CC.O) (x64_mul_lo_with_flags_paired ty false x y))) ;;;; Rules for `smul_overflow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 2 (lower (smul_overflow x y @ (value_type $I8))) +(rule 2 (lower (smul_overflow _ x y @ (value_type $I8))) (construct_overflow_op (CC.O) (x64_mul8_with_flags_paired true x y))) -(rule 3 (lower (smul_overflow x y @ (value_type (ty_int_ref_16_to_64 ty)))) +(rule 3 (lower (smul_overflow _ x y @ (value_type (ty_int_ref_16_to_64 ty)))) (construct_overflow_op (CC.O) (x64_mul_lo_with_flags_paired ty true x y))) ;;;; Rules for `sadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type (multi_lane 8 16) - (sadd_sat x y))) + (sadd_sat _ x y))) (x64_paddsb x y)) (rule (lower (has_type (multi_lane 16 8) - (sadd_sat x y))) + (sadd_sat _ x y))) (x64_paddsw x y)) ;;;; Rules for `uadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type (multi_lane 8 16) - (uadd_sat x y))) + (uadd_sat _ x y))) (x64_paddusb x y)) (rule (lower (has_type (multi_lane 16 8) - (uadd_sat x y))) + (uadd_sat _ x y))) (x64_paddusw x y)) ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -246,29 +246,29 @@ ;; Sub two registers. (rule -3 (lower (has_type (fits_in_64 ty) - (isub x y))) + (isub _ x y))) (x64_sub ty x y)) ;; SSE. (rule (lower (has_type (multi_lane 8 16) - (isub x y))) + (isub _ x y))) (x64_psubb x y)) (rule (lower (has_type (multi_lane 16 8) - (isub x y))) + (isub _ x y))) (x64_psubw x y)) (rule (lower (has_type (multi_lane 32 4) - (isub x y))) + (isub _ x y))) (x64_psubd x y)) (rule (lower (has_type (multi_lane 64 2) - (isub x y))) + (isub _ x y))) (x64_psubq x y)) ;; `i128` -(rule 1 (lower (has_type $I128 (isub x y))) +(rule 1 (lower (has_type $I128 (isub _ x y))) ;; Get the high/low registers for `x`. (let ((x_regs ValueRegs x) (y_regs ValueRegs y)) @@ -277,10 +277,10 @@ (value_regs_get_gpr x_regs 1) (value_regs_get_gpr y_regs 0) (value_regs_get_gpr y_regs 1)))) -(rule 2 (lower (has_type $I128 (isub x (iconcat y_lo y_hi)))) +(rule 2 (lower (has_type $I128 (isub _ x (iconcat _ y_lo y_hi)))) (let ((x_regs ValueRegs x)) (isub128 (value_regs_get_gpr x 0) (value_regs_get_gpr x 1) y_lo y_hi))) -(rule 3 (lower (has_type $I128 (isub x (uextend y @ (value_type $I64))))) +(rule 3 (lower (has_type $I128 (isub _ x (uextend _ y @ (value_type $I64))))) (let ((x_regs ValueRegs x)) (isub128 (value_regs_get_gpr x 0) (value_regs_get_gpr x 1) y (RegMemImm.Imm 0)))) @@ -296,21 +296,21 @@ ;;;; Rules for `ssub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type (multi_lane 8 16) - (ssub_sat x y))) + (ssub_sat _ x y))) (x64_psubsb x y)) (rule (lower (has_type (multi_lane 16 8) - (ssub_sat x y))) + (ssub_sat _ x y))) (x64_psubsw x y)) ;;;; Rules for `usub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type (multi_lane 8 16) - (usub_sat x y))) + (usub_sat _ x y))) (x64_psubusb x y)) (rule (lower (has_type (multi_lane 16 8) - (usub_sat x y))) + (usub_sat _ x y))) (x64_psubusw x y)) ;;;; Rules for `band` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -318,24 +318,24 @@ ;; `{i,b}64` and smaller. ;; And two registers. -(rule 0 (lower (has_type ty (band x y))) +(rule 0 (lower (has_type ty (band _ x y))) (if (ty_int_ref_scalar_64 ty)) (x64_and ty x y)) ;; The above case automatically handles when the rhs is an immediate or a ;; sinkable load, but additionally handle the lhs here. -(rule 1 (lower (has_type ty (band (sinkable_load x) y))) +(rule 1 (lower (has_type ty (band _ (sinkable_load x) y))) (if (ty_int_ref_scalar_64 ty)) (x64_and ty y x)) -(rule 2 (lower (has_type ty (band (simm32_from_value x) y))) +(rule 2 (lower (has_type ty (band _ (simm32_from_value x) y))) (if (ty_int_ref_scalar_64 ty)) (x64_and ty y x)) ;; f32 and f64 -(rule 5 (lower (has_type (ty_scalar_float ty) (band x y))) +(rule 5 (lower (has_type (ty_scalar_float ty) (band _ x y))) (sse_and ty x y)) ;; SSE. @@ -348,7 +348,7 @@ (rule -1 (sse_and (multi_lane _bits _lanes) x y) (x64_pand x y)) (rule 6 (lower (has_type ty @ (multi_lane _bits _lanes) - (band x y))) + (band _ x y))) (sse_and ty x y)) ;; `i128`. @@ -364,7 +364,7 @@ (value_gprs (x64_and $I64 x_lo y_lo) (x64_and $I64 x_hi y_hi)))) -(rule 7 (lower (has_type $I128 (band x y))) +(rule 7 (lower (has_type $I128 (band _ x y))) (and_i128 x y)) ;; Specialized lowerings for `(band x (bnot y))` which is additionally produced @@ -383,17 +383,17 @@ ;; while x86 does ;; ;; pandn(x, y) = and(not(x), y) -(rule 8 (lower (has_type ty @ (multi_lane _bits _lane) (band x (bnot y)))) +(rule 8 (lower (has_type ty @ (multi_lane _bits _lane) (band _ x (bnot _ y)))) (sse_and_not ty y x)) -(rule 9 (lower (has_type ty @ (multi_lane _bits _lane) (band (bnot y) x))) +(rule 9 (lower (has_type ty @ (multi_lane _bits _lane) (band _ (bnot _ y) x))) (sse_and_not ty y x)) -(rule 10 (lower (has_type ty (band x (bnot y)))) +(rule 10 (lower (has_type ty (band _ x (bnot _ y)))) (if (ty_int_ref_scalar_64 ty)) (if-let true (has_bmi1)) ;; the first argument is the one that gets inverted with andn (x64_andn ty y x)) -(rule 11 (lower (has_type ty (band (bnot y) x))) +(rule 11 (lower (has_type ty (band _ (bnot _ y) x))) (if (ty_int_ref_scalar_64 ty)) (if-let true (has_bmi1)) (x64_andn ty y x)) @@ -401,25 +401,25 @@ ;; Specialization of `blsr` for BMI1 (decl pure partial val_minus_one (Value) Value) -(rule 0 (val_minus_one (isub x (u64_from_iconst 1))) x) -(rule 0 (val_minus_one (iadd x (i64_from_iconst -1))) x) -(rule 1 (val_minus_one (iadd (i64_from_iconst -1) x)) x) +(rule 0 (val_minus_one (isub _ x (u64_from_iconst 1))) x) +(rule 0 (val_minus_one (iadd _ x (i64_from_iconst -1))) x) +(rule 1 (val_minus_one (iadd _ (i64_from_iconst -1) x)) x) -(rule 12 (lower (has_type (ty_32_or_64 ty) (band x y))) +(rule 12 (lower (has_type (ty_32_or_64 ty) (band _ x y))) (if-let true (has_bmi1)) (if-let x (val_minus_one y)) (x64_blsr ty x)) -(rule 13 (lower (has_type (ty_32_or_64 ty) (band y x))) +(rule 13 (lower (has_type (ty_32_or_64 ty) (band _ y x))) (if-let true (has_bmi1)) (if-let x (val_minus_one y)) (x64_blsr ty x)) ;; Specialization of `blsi` for BMI1 -(rule 14 (lower (has_type (ty_32_or_64 ty) (band (ineg x) x))) +(rule 14 (lower (has_type (ty_32_or_64 ty) (band _ (ineg _ x) x))) (if-let true (has_bmi1)) (x64_blsi ty x)) -(rule 15 (lower (has_type (ty_32_or_64 ty) (band x (ineg x)))) +(rule 15 (lower (has_type (ty_32_or_64 ty) (band _ x (ineg _ x)))) (if-let true (has_bmi1)) (x64_blsi ty x)) @@ -432,9 +432,9 @@ ;; `ishl`, so an `and` instruction is required to mask the index to match the ;; semantics of Cranelift's `ishl`. -(rule 16 (lower (has_type (ty_32_or_64 ty) (band x y))) +(rule 16 (lower (has_type (ty_32_or_64 ty) (band _ x y))) (if-let true (has_bmi2)) - (if-let (ishl (u64_from_iconst 1) index) (val_minus_one y)) + (if-let (ishl _ (u64_from_iconst 1) index) (val_minus_one y)) (x64_bzhi ty x (x64_and ty index (RegMemImm.Imm (u32_wrapping_sub (ty_bits ty) 1))))) ;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -442,24 +442,24 @@ ;; `{i,b}64` and smaller. ;; Or two registers. -(rule 0 (lower (has_type ty (bor x y))) +(rule 0 (lower (has_type ty (bor _ x y))) (if (ty_int_ref_scalar_64 ty)) (x64_or ty x y)) ;; Handle immediates/sinkable loads on the lhs in addition to the automatic ;; handling of the rhs above -(rule 1 (lower (has_type ty (bor (sinkable_load x) y))) +(rule 1 (lower (has_type ty (bor _ (sinkable_load x) y))) (if (ty_int_ref_scalar_64 ty)) (x64_or ty y x)) -(rule 2 (lower (has_type ty (bor (simm32_from_value x) y))) +(rule 2 (lower (has_type ty (bor _ (simm32_from_value x) y))) (if (ty_int_ref_scalar_64 ty)) (x64_or ty y x)) ;; f32 and f64 -(rule 5 (lower (has_type (ty_scalar_float ty) (bor x y))) +(rule 5 (lower (has_type (ty_scalar_float ty) (bor _ x y))) (sse_or ty x y)) ;; SSE. @@ -472,7 +472,7 @@ (rule -1 (sse_or (multi_lane _bits _lanes) x y) (x64_por x y)) (rule 6 (lower (has_type ty @ (multi_lane _bits _lanes) - (bor x y))) + (bor _ x y))) (sse_or ty x y)) ;; `{i,b}128`. @@ -486,7 +486,7 @@ (value_gprs (x64_or $I64 x_lo y_lo) (x64_or $I64 x_hi y_hi)))) -(rule 7 (lower (has_type $I128 (bor x y))) +(rule 7 (lower (has_type $I128 (bor _ x y))) (or_i128 x y)) ;; Specialized lowerings to generate the `shld` instruction. @@ -495,13 +495,13 @@ ;; different register. Pattern-match doing this with bit-ops and shifts to ;; generate a `shld` instruction. (rule 8 (lower (has_type (ty_int_ref_16_to_64 ty) - (bor (ishl x (u8_from_iconst xs)) (ushr y (u8_from_iconst ys))))) + (bor _ (ishl _ x (u8_from_iconst xs)) (ushr _ y (u8_from_iconst ys))))) (if-let true (u64_eq (ty_bits ty) (u64_wrapping_add xs ys))) (if-let true (u64_gt xs 0)) (if-let true (u64_gt ys 0)) (x64_shld ty x y xs)) (rule 8 (lower (has_type (ty_int_ref_16_to_64 ty) - (bor (ushr y (u8_from_iconst ys)) (ishl x (u8_from_iconst xs))))) + (bor _ (ushr _ y (u8_from_iconst ys)) (ishl _ x (u8_from_iconst xs))))) (if-let true (u64_eq (ty_bits ty) (u64_wrapping_add xs ys))) (if-let true (u64_gt xs 0)) (if-let true (u64_gt ys 0)) @@ -513,34 +513,34 @@ ;; `{i,b}64` and smaller. ;; Xor two registers. -(rule 0 (lower (has_type ty (bxor x y))) +(rule 0 (lower (has_type ty (bxor _ x y))) (if (ty_int_ref_scalar_64 ty)) (x64_xor ty x y)) ;; Handle xor with lhs immediates/sinkable loads in addition to the automatic ;; handling of the rhs above. -(rule 1 (lower (has_type ty (bxor (sinkable_load x) y))) +(rule 1 (lower (has_type ty (bxor _ (sinkable_load x) y))) (if (ty_int_ref_scalar_64 ty)) (x64_xor ty y x)) -(rule 4 (lower (has_type ty (bxor (simm32_from_value x) y))) +(rule 4 (lower (has_type ty (bxor _ (simm32_from_value x) y))) (if (ty_int_ref_scalar_64 ty)) (x64_xor ty y x)) ;; f32 and f64 -(rule 5 (lower (has_type (ty_scalar_float ty) (bxor x y))) +(rule 5 (lower (has_type (ty_scalar_float ty) (bxor _ x y))) (x64_xor_vector ty x y)) ;; SSE. -(rule 6 (lower (has_type ty @ (multi_lane _bits _lanes) (bxor x y))) +(rule 6 (lower (has_type ty @ (multi_lane _bits _lanes) (bxor _ x y))) (x64_xor_vector ty x y)) ;; `{i,b}128`. -(rule 7 (lower (has_type $I128 (bxor x y))) +(rule 7 (lower (has_type $I128 (bxor _ x y))) (let ((x_regs ValueRegs x) (x_lo Gpr (value_regs_get_gpr x_regs 0)) (x_hi Gpr (value_regs_get_gpr x_regs 1)) @@ -552,11 +552,11 @@ ;; Specialization of `blsmsk` for BMI1 -(rule 8 (lower (has_type (ty_32_or_64 ty) (bxor x y))) +(rule 8 (lower (has_type (ty_32_or_64 ty) (bxor _ x y))) (if-let true (has_bmi1)) (if-let x (val_minus_one y)) (x64_blsmsk ty x)) -(rule 9 (lower (has_type (ty_32_or_64 ty) (bxor y x))) +(rule 9 (lower (has_type (ty_32_or_64 ty) (bxor _ y x))) (if-let true (has_bmi1)) (if-let x (val_minus_one y)) (x64_blsmsk ty x)) @@ -565,7 +565,7 @@ ;; `i64` and smaller. -(rule -1 (lower (has_type (fits_in_64 ty) (ishl src amt))) +(rule -1 (lower (has_type (fits_in_64 ty) (ishl _ src amt))) (x64_shl ty src (put_masked_in_imm8_gpr amt ty))) ;; `i128`. @@ -602,7 +602,7 @@ (cmove $I64 (CC.Z) lo_shifted zero) (cmove $I64 (CC.Z) hi_shifted_ lo_shifted))))) -(rule (lower (has_type $I128 (ishl src amt))) +(rule (lower (has_type $I128 (ishl _ src amt))) ;; NB: Only the low bits of `amt` matter since we logically mask the shift ;; amount to the value's bit width. (let ((amt_ Gpr (lo_gpr amt))) @@ -614,7 +614,7 @@ ;; in higher feature sets like AVX), we lower the `ishl.i8x16` to a sequence of ;; instructions. The basic idea, whether the amount to shift by is an immediate ;; or not, is to use a 16x8 shift and then mask off the incorrect bits to 0s. -(rule (lower (has_type ty @ $I8X16 (ishl src amt))) +(rule (lower (has_type ty @ $I8X16 (ishl _ src amt))) (let ( ;; Mask the amount to ensure wrapping behaviour (masked_amt RegMemImm (mask_xmm_shift ty amt)) @@ -623,7 +623,7 @@ ;; the mask below. (unmasked Xmm (x64_psllw src (mov_rmi_to_xmm masked_amt))) (mask_addr SyntheticAmode (ishl_i8x16_mask masked_amt)) - (mask Reg (x64_load $I8X16 mask_addr (ExtKind.None)))) + (mask Reg (x64_movdqu_load mask_addr))) (sse_and $I8X16 unmasked (RegMem.Reg mask)))) ;; Get the address of the mask to use when fixing up the lanes that weren't @@ -656,24 +656,24 @@ (mem_flags_trusted)))) (rule (ishl_i8x16_mask (RegMemImm.Mem amt)) - (ishl_i8x16_mask (RegMemImm.Reg (x64_load $I64 amt (ExtKind.None))))) + (ishl_i8x16_mask (RegMemImm.Reg (x64_movq_rm amt)))) ;; 16x8, 32x4, and 64x2 shifts can each use a single instruction, once the shift amount is masked. -(rule (lower (has_type ty @ $I16X8 (ishl src amt))) +(rule (lower (has_type ty @ $I16X8 (ishl _ src amt))) (x64_psllw src (mov_rmi_to_xmm (mask_xmm_shift ty amt)))) -(rule (lower (has_type ty @ $I32X4 (ishl src amt))) +(rule (lower (has_type ty @ $I32X4 (ishl _ src amt))) (x64_pslld src (mov_rmi_to_xmm (mask_xmm_shift ty amt)))) -(rule (lower (has_type ty @ $I64X2 (ishl src amt))) +(rule (lower (has_type ty @ $I64X2 (ishl _ src amt))) (x64_psllq src (mov_rmi_to_xmm (mask_xmm_shift ty amt)))) ;;;; Rules for `ushr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `i64` and smaller. -(rule -1 (lower (has_type (fits_in_64 ty) (ushr src amt))) +(rule -1 (lower (has_type (fits_in_64 ty) (ushr _ src amt))) (let ((src_ Gpr (extend_to_gpr src ty (ExtendKind.Zero)))) (x64_shr ty src_ (put_masked_in_imm8_gpr amt ty)))) @@ -710,7 +710,7 @@ (cmove $I64 (CC.Z) lo_shifted_ hi_shifted) (cmove $I64 (CC.Z) hi_shifted zero))))) -(rule (lower (has_type $I128 (ushr src amt))) +(rule (lower (has_type $I128 (ushr _ src amt))) ;; NB: Only the low bits of `amt` matter since we logically mask the shift ;; amount to the value's bit width. (let ((amt_ Gpr (lo_gpr amt))) @@ -720,7 +720,7 @@ ;; There are no 8x16 shifts in x64. Do the same 16x8-shift-and-mask thing we do ;; with 8x16 `ishl`. -(rule (lower (has_type ty @ $I8X16 (ushr src amt))) +(rule (lower (has_type ty @ $I8X16 (ushr _ src amt))) (let ( ;; Mask the amount to ensure wrapping behaviour (masked_amt RegMemImm (mask_xmm_shift ty amt)) @@ -762,30 +762,30 @@ (mem_flags_trusted)))) (rule (ushr_i8x16_mask (RegMemImm.Mem amt)) - (ushr_i8x16_mask (RegMemImm.Reg (x64_load $I64 amt (ExtKind.None))))) + (ushr_i8x16_mask (RegMemImm.Reg (x64_movq_rm amt)))) ;; 16x8, 32x4, and 64x2 shifts can each use a single instruction, once the shift amount is masked. -(rule (lower (has_type ty @ $I16X8 (ushr src amt))) +(rule (lower (has_type ty @ $I16X8 (ushr _ src amt))) (x64_psrlw src (mov_rmi_to_xmm (mask_xmm_shift ty amt)))) -(rule (lower (has_type ty @ $I32X4 (ushr src amt))) +(rule (lower (has_type ty @ $I32X4 (ushr _ src amt))) (x64_psrld src (mov_rmi_to_xmm (mask_xmm_shift ty amt)))) -(rule (lower (has_type ty @ $I64X2 (ushr src amt))) +(rule (lower (has_type ty @ $I64X2 (ushr _ src amt))) (x64_psrlq src (mov_rmi_to_xmm (mask_xmm_shift ty amt)))) (decl mask_xmm_shift (Type Value) RegMemImm) (rule (mask_xmm_shift ty amt) (gpr_to_reg (x64_and $I64 amt (RegMemImm.Imm (shift_mask ty))))) -(rule 1 (mask_xmm_shift ty (iconst n)) +(rule 1 (mask_xmm_shift ty (iconst _ n)) (RegMemImm.Imm (shift_amount_masked ty n))) ;;;; Rules for `sshr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `i64` and smaller. -(rule -1 (lower (has_type (fits_in_64 ty) (sshr src amt))) +(rule -1 (lower (has_type (fits_in_64 ty) (sshr _ src amt))) (let ((src_ Gpr (extend_to_gpr src ty (ExtendKind.Sign)))) (x64_sar ty src_ (put_masked_in_imm8_gpr amt ty)))) @@ -822,7 +822,7 @@ (cmove $I64 (CC.Z) lo_shifted_ hi_shifted) (cmove $I64 (CC.Z) hi_shifted sign_bits))))) -(rule (lower (has_type $I128 (sshr src amt))) +(rule (lower (has_type $I128 (sshr _ src amt))) ;; NB: Only the low bits of `amt` matter since we logically mask the shift ;; amount to the value's bit width. (let ((amt_ Gpr (lo_gpr amt))) @@ -843,7 +843,7 @@ ;; hi.i16x8 = [(s8, s8), (s9, s9), ..., (s15, s15)] ;; shifted_hi.i16x8 = shift each lane of `high` ;; result = [s0'', s1'', ..., s15''] -(rule (lower (has_type ty @ $I8X16 (sshr src amt @ (value_type amt_ty)))) +(rule (lower (has_type ty @ $I8X16 (sshr _ src amt @ (value_type amt_ty)))) (let ((src_ Xmm (put_in_xmm src)) ;; Mask the amount to ensure wrapping behaviour (masked_amt RegMemImm (mask_xmm_shift ty amt)) @@ -872,30 +872,30 @@ ;; `sshr.{i16x8,i32x4}` can be a simple `psra{w,d}`, we just have to make sure ;; that if the shift amount is in a register, it is in an XMM register. -(rule (lower (has_type ty @ $I16X8 (sshr src amt))) +(rule (lower (has_type ty @ $I16X8 (sshr _ src amt))) (x64_psraw src (mov_rmi_to_xmm (mask_xmm_shift ty amt)))) -(rule (lower (has_type ty @ $I32X4 (sshr src amt))) +(rule (lower (has_type ty @ $I32X4 (sshr _ src amt))) (x64_psrad src (mov_rmi_to_xmm (mask_xmm_shift ty amt)))) ;; The `sshr.i64x2` CLIF instruction has no single x86 instruction in the older ;; feature sets. To remedy this, a small dance is done with an unsigned right ;; shift plus some extra ops. -(rule 3 (lower (has_type ty @ $I64X2 (sshr src (iconst n)))) +(rule 3 (lower (has_type ty @ $I64X2 (sshr _ src (iconst _ n)))) (if-let true (has_avx512vl)) (if-let true (has_avx512f)) (x64_vpsraq_imm src (shift_amount_masked ty n))) -(rule 2 (lower (has_type ty @ $I64X2 (sshr src amt))) +(rule 2 (lower (has_type ty @ $I64X2 (sshr _ src amt))) (if-let true (has_avx512vl)) (if-let true (has_avx512f)) (let ((masked Gpr (x64_and $I64 amt (RegMemImm.Imm (shift_mask ty))))) (x64_vpsraq src (x64_movd_to_xmm masked)))) -(rule 1 (lower (has_type $I64X2 (sshr src (u32_from_iconst amt)))) +(rule 1 (lower (has_type $I64X2 (sshr _ src (u32_from_iconst amt)))) (lower_i64x2_sshr_imm src (u32_and amt 63))) -(rule (lower (has_type $I64X2 (sshr src amt))) +(rule (lower (has_type $I64X2 (sshr _ src amt))) (lower_i64x2_sshr_gpr src (x64_and $I64 amt (RegMemImm.Imm 63)))) (decl lower_i64x2_sshr_imm (Xmm u32) Xmm) @@ -955,13 +955,13 @@ ;; `i64` and smaller: we can rely on x86's rotate-amount masking since ;; we operate on the whole register. For const's we mask the constant. -(rule -1 (lower (has_type (fits_in_64 ty) (rotl src amt))) +(rule -1 (lower (has_type (fits_in_64 ty) (rotl _ src amt))) (x64_rotl ty src (put_masked_in_imm8_gpr amt ty))) ;; `i128`. -(rule (lower (has_type $I128 (rotl src amt))) +(rule (lower (has_type $I128 (rotl _ src amt))) (let ((src_ ValueRegs src) ;; NB: Only the low bits of `amt` matter since we logically mask the ;; rotation amount to the value's bit width. @@ -976,13 +976,13 @@ ;; `i64` and smaller: we can rely on x86's rotate-amount masking since ;; we operate on the whole register. For const's we mask the constant. -(rule -1 (lower (has_type (fits_in_64 ty) (rotr src amt))) +(rule -1 (lower (has_type (fits_in_64 ty) (rotr _ src amt))) (x64_rotr ty src (put_masked_in_imm8_gpr amt ty))) ;; `i128`. -(rule (lower (has_type $I128 (rotr src amt))) +(rule (lower (has_type $I128 (rotr _ src amt))) (let ((src_ ValueRegs src) ;; NB: Only the low bits of `amt` matter since we logically mask the ;; rotation amount to the value's bit width. @@ -996,10 +996,10 @@ ;; `i64` and smaller. -(rule -1 (lower (has_type (fits_in_64 ty) (ineg x))) +(rule -1 (lower (has_type (fits_in_64 ty) (ineg _ x))) (x64_neg ty x)) -(rule -2 (lower (has_type $I128 (ineg x))) +(rule -2 (lower (has_type $I128 (ineg _ x))) ;; Get the high/low registers for `x`. (let ((regs ValueRegs x) (lo Gpr (value_regs_get_gpr regs 0)) @@ -1010,26 +1010,26 @@ ;; SSE. -(rule (lower (has_type $I8X16 (ineg x))) +(rule (lower (has_type $I8X16 (ineg _ x))) (x64_psubb (imm $I8X16 0) x)) -(rule (lower (has_type $I16X8 (ineg x))) +(rule (lower (has_type $I16X8 (ineg _ x))) (x64_psubw (imm $I16X8 0) x)) -(rule (lower (has_type $I32X4 (ineg x))) +(rule (lower (has_type $I32X4 (ineg _ x))) (x64_psubd (imm $I32X4 0) x)) -(rule (lower (has_type $I64X2 (ineg x))) +(rule (lower (has_type $I64X2 (ineg _ x))) (x64_psubq (imm $I64X2 0) x)) ;;;; Rules for `avg_round` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type (multi_lane 8 16) - (avg_round x y))) + (avg_round _ x y))) (x64_pavgb x y)) (rule (lower (has_type (multi_lane 16 8) - (avg_round x y))) + (avg_round _ x y))) (x64_pavgw x y)) ;;;; Rules for `imul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1038,32 +1038,32 @@ ;; 8-bit base case, needs a special instruction encoding and additionally ;; move sinkable loads to the right. -(rule -8 (lower (has_type $I8 (imul x y))) (x64_mul8 false x y)) -(rule -7 (lower (has_type $I8 (imul (sinkable_load x) y))) (x64_mul8 false y x)) +(rule -8 (lower (has_type $I8 (imul _ x y))) (x64_mul8 false x y)) +(rule -7 (lower (has_type $I8 (imul _ (sinkable_load x) y))) (x64_mul8 false y x)) ;; 16-to-64-bit base cases, same as above by moving sinkable loads to the right. -(rule -6 (lower (has_type (ty_int_ref_16_to_64 ty) (imul x y))) +(rule -6 (lower (has_type (ty_int_ref_16_to_64 ty) (imul _ x y))) (x64_imul ty x y)) -(rule -5 (lower (has_type (ty_int_ref_16_to_64 ty) (imul (sinkable_load x) y))) +(rule -5 (lower (has_type (ty_int_ref_16_to_64 ty) (imul _ (sinkable_load x) y))) (x64_imul ty y x)) ;; Lift out constants to use 3-operand form. -(rule -4 (lower (has_type (ty_int_ref_16_to_64 ty) (imul x (i32_from_iconst y)))) +(rule -4 (lower (has_type (ty_int_ref_16_to_64 ty) (imul _ x (i32_from_iconst y)))) (x64_imul_imm ty x y)) -(rule -3 (lower (has_type (ty_int_ref_16_to_64 ty) (imul (i32_from_iconst x) y))) +(rule -3 (lower (has_type (ty_int_ref_16_to_64 ty) (imul _ (i32_from_iconst x) y))) (x64_imul_imm ty y x)) ;; Special case widening multiplication from 8-to-16-bits with a single ;; instruction since the 8-bit-multiply places both the high and low halves in ;; the same register -(rule -2 (lower (has_type $I16 (imul (sextend x) (sextend y)))) +(rule -2 (lower (has_type $I16 (imul _ (sextend _ x) (sextend _ y)))) (x64_mul8 true x y)) -(rule -2 (lower (has_type $I16 (imul (uextend x) (uextend y)))) +(rule -2 (lower (has_type $I16 (imul _ (uextend _ x) (uextend _ y)))) (x64_mul8 false x y)) ;; `i128`. -(rule 2 (lower (has_type $I128 (imul x y))) +(rule 2 (lower (has_type $I128 (imul _ x y))) (let ((x_regs ValueRegs x) (y_regs ValueRegs y)) (imul128 @@ -1072,7 +1072,7 @@ (value_regs_get_gpr y_regs 0) (value_regs_get_gpr y_regs 1)))) -(rule 4 (lower (has_type $I128 (imul (iconcat x_lo x_hi) (iconcat y_lo y_hi)))) +(rule 4 (lower (has_type $I128 (imul _ (iconcat _ x_lo x_hi) (iconcat _ y_lo y_hi)))) (imul128 x_lo x_hi y_lo y_hi)) ;; Helper for lowering 128-bit multiplication with the 64-bit halves of the @@ -1114,28 +1114,28 @@ ;; operands and producing a 128-bit result, which exactly matches the semantics ;; of widening 64-bit inputs to 128-bit and then multiplying them. That means ;; that these cases can get some some simpler codegen. -(rule 5 (lower (has_type $I128 (imul (uextend x @ (value_type $I64)) - (uextend y @ (value_type $I64))))) +(rule 5 (lower (has_type $I128 (imul _ (uextend _ x @ (value_type $I64)) + (uextend _ y @ (value_type $I64))))) (x64_mul $I64 false x y)) -(rule 5 (lower (has_type $I128 (imul (sextend x @ (value_type $I64)) - (sextend y @ (value_type $I64))))) +(rule 5 (lower (has_type $I128 (imul _ (sextend _ x @ (value_type $I64)) + (sextend _ y @ (value_type $I64))))) (x64_mul $I64 true x y)) ;; SSE. ;; (No i8x16 multiply.) -(rule (lower (has_type (multi_lane 16 8) (imul x y))) +(rule (lower (has_type (multi_lane 16 8) (imul _ x y))) (x64_pmullw x y)) -(rule (lower (has_type (multi_lane 32 4) (imul x y))) +(rule (lower (has_type (multi_lane 32 4) (imul _ x y))) (if-let true (has_sse41)) (x64_pmulld x y)) ;; Without `pmulld` the `pmuludq` instruction is used instead which performs ;; 32-bit multiplication storing the 64-bit result. The 64-bit result is ;; truncated to 32-bits and everything else is woven into place. -(rule -1 (lower (has_type (multi_lane 32 4) (imul x y))) +(rule -1 (lower (has_type (multi_lane 32 4) (imul _ x y))) (let ( (x Xmm x) (y Xmm y) @@ -1148,7 +1148,7 @@ ;; With AVX-512 we can implement `i64x2` multiplication with a single ;; instruction. -(rule 3 (lower (has_type (multi_lane 64 2) (imul x y))) +(rule 3 (lower (has_type (multi_lane 64 2) (imul _ x y))) (if-let true (has_avx512vl)) (if-let true (has_avx512dq)) (x64_vpmullq x y)) @@ -1174,7 +1174,7 @@ ;; the lower 32-bits, however, we will need to use shifts to isolate the high ;; 32-bits when doing calculations, i.e., `Ah == A >> 32`. (rule (lower (has_type (multi_lane 64 2) - (imul a b))) + (imul _ a b))) (let ((a0 Xmm a) (b0 Xmm b) ;; a_hi = A >> 32 @@ -1196,9 +1196,9 @@ ;; Special case for `i32x4.extmul_high_i16x8_s`. (rule 1 (lower (has_type (multi_lane 32 4) - (imul (swiden_high (and (value_type (multi_lane 16 8)) + (imul _ (swiden_high _ (and (value_type (multi_lane 16 8)) x)) - (swiden_high (and (value_type (multi_lane 16 8)) + (swiden_high _ (and (value_type (multi_lane 16 8)) y))))) (let ((x2 Xmm x) (y2 Xmm y) @@ -1208,9 +1208,9 @@ ;; Special case for `i64x2.extmul_high_i32x4_s`. (rule 1 (lower (has_type (multi_lane 64 2) - (imul (swiden_high (and (value_type (multi_lane 32 4)) + (imul _ (swiden_high _ (and (value_type (multi_lane 32 4)) x)) - (swiden_high (and (value_type (multi_lane 32 4)) + (swiden_high _ (and (value_type (multi_lane 32 4)) y))))) (if-let true (has_sse41)) (let ((x2 Xmm (x64_pshufd x 0xFA)) @@ -1219,9 +1219,9 @@ ;; Special case for `i32x4.extmul_low_i16x8_s`. (rule 1 (lower (has_type (multi_lane 32 4) - (imul (swiden_low (and (value_type (multi_lane 16 8)) + (imul _ (swiden_low _ (and (value_type (multi_lane 16 8)) x)) - (swiden_low (and (value_type (multi_lane 16 8)) + (swiden_low _ (and (value_type (multi_lane 16 8)) y))))) (let ((x2 Xmm x) (y2 Xmm y) @@ -1231,9 +1231,9 @@ ;; Special case for `i64x2.extmul_low_i32x4_s`. (rule 1 (lower (has_type (multi_lane 64 2) - (imul (swiden_low (and (value_type (multi_lane 32 4)) + (imul _ (swiden_low _ (and (value_type (multi_lane 32 4)) x)) - (swiden_low (and (value_type (multi_lane 32 4)) + (swiden_low _ (and (value_type (multi_lane 32 4)) y))))) (if-let true (has_sse41)) (let ((x2 Xmm (x64_pshufd x 0x50)) @@ -1242,9 +1242,9 @@ ;; Special case for `i32x4.extmul_high_i16x8_u`. (rule 1 (lower (has_type (multi_lane 32 4) - (imul (uwiden_high (and (value_type (multi_lane 16 8)) + (imul _ (uwiden_high _ (and (value_type (multi_lane 16 8)) x)) - (uwiden_high (and (value_type (multi_lane 16 8)) + (uwiden_high _ (and (value_type (multi_lane 16 8)) y))))) (let ((x2 Xmm x) (y2 Xmm y) @@ -1254,9 +1254,9 @@ ;; Special case for `i64x2.extmul_high_i32x4_u`. (rule 1 (lower (has_type (multi_lane 64 2) - (imul (uwiden_high (and (value_type (multi_lane 32 4)) + (imul _ (uwiden_high _ (and (value_type (multi_lane 32 4)) x)) - (uwiden_high (and (value_type (multi_lane 32 4)) + (uwiden_high _ (and (value_type (multi_lane 32 4)) y))))) (let ((x2 Xmm (x64_pshufd x 0xFA)) (y2 Xmm (x64_pshufd y 0xFA))) @@ -1264,9 +1264,9 @@ ;; Special case for `i32x4.extmul_low_i16x8_u`. (rule 1 (lower (has_type (multi_lane 32 4) - (imul (uwiden_low (and (value_type (multi_lane 16 8)) + (imul _ (uwiden_low _ (and (value_type (multi_lane 16 8)) x)) - (uwiden_low (and (value_type (multi_lane 16 8)) + (uwiden_low _ (and (value_type (multi_lane 16 8)) y))))) (let ((x2 Xmm x) (y2 Xmm y) @@ -1276,9 +1276,9 @@ ;; Special case for `i64x2.extmul_low_i32x4_u`. (rule 1 (lower (has_type (multi_lane 64 2) - (imul (uwiden_low (and (value_type (multi_lane 32 4)) + (imul _ (uwiden_low _ (and (value_type (multi_lane 32 4)) x)) - (uwiden_low (and (value_type (multi_lane 32 4)) + (uwiden_low _ (and (value_type (multi_lane 32 4)) y))))) (let ((x2 Xmm (x64_pshufd x 0x50)) (y2 Xmm (x64_pshufd y 0x50))) @@ -1286,32 +1286,32 @@ ;;;; Rules for `iabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type $I8X16 (iabs x))) +(rule 1 (lower (has_type $I8X16 (iabs _ x))) (if-let true (has_ssse3)) (x64_pabsb_a_or_avx x)) ;; Note the use of `pminub` with signed inputs will produce the positive signed ;; result which is what is desired here. The `pmaxub` isn't available until ;; SSE4.1 in which case the single-instruction above lowering would apply. -(rule (lower (has_type $I8X16 (iabs x))) +(rule (lower (has_type $I8X16 (iabs _ x))) (let ( (x Xmm x) (negated Xmm (x64_psubb (xmm_zero $I8X16) x)) ) (x64_pminub_a x negated))) -(rule 1 (lower (has_type $I16X8 (iabs x))) +(rule 1 (lower (has_type $I16X8 (iabs _ x))) (if-let true (has_ssse3)) (x64_pabsw_a_or_avx x)) -(rule (lower (has_type $I16X8 (iabs x))) +(rule (lower (has_type $I16X8 (iabs _ x))) (let ( (x Xmm x) (negated Xmm (x64_psubw (xmm_zero $I16X8) x)) ) (x64_pmaxsw_a x negated))) -(rule 1 (lower (has_type $I32X4 (iabs x))) +(rule 1 (lower (has_type $I32X4 (iabs _ x))) (if-let true (has_ssse3)) (x64_pabsd_a_or_avx x)) @@ -1321,7 +1321,7 @@ ;; subtracting the mask this subtracts 0 for positive lanes (does nothing) or ;; ends up adding one for negative lanes. This means that for a negative lane ;; `x` the result is `!x + 1` which is the result of negating it. -(rule (lower (has_type $I32X4 (iabs x))) +(rule (lower (has_type $I32X4 (iabs _ x))) (let ( (x Xmm x) (negative_mask Xmm (x64_psrad x (xmi_imm 31))) @@ -1330,7 +1330,7 @@ (x64_psubd flipped_if_negative negative_mask))) ;; When AVX512 is available, we can use a single `vpabsq` instruction. -(rule 2 (lower (has_type $I64X2 (iabs x))) +(rule 2 (lower (has_type $I64X2 (iabs _ x))) (if-let true (has_avx512vl)) (if-let true (has_avx512f)) (x64_vpabsq x)) @@ -1339,7 +1339,7 @@ ;; x` and then blend in those results with `blendvpd` if the MSB of `neg` was ;; set to 1 (i.e. if `neg` was negative or, conversely, if `x` was originally ;; positive). -(rule 1 (lower (has_type $I64X2 (iabs x))) +(rule 1 (lower (has_type $I64X2 (iabs _ x))) (if-let true (has_sse41)) (let ((rx Xmm x) (neg Xmm (x64_psubq (imm $I64X2 0) rx))) @@ -1348,7 +1348,7 @@ ;; and if `blendvpd` isn't available then perform a shift/shuffle to generate a ;; mask of which lanes are negative, followed by flipping bits/sub to make both ;; positive. -(rule (lower (has_type $I64X2 (iabs x))) +(rule (lower (has_type $I64X2 (iabs _ x))) (let ((x Xmm x) (signs Xmm (x64_psrad x (RegMemImm.Imm 31))) (signs Xmm (x64_pshufd signs 0b11_11_01_01)) @@ -1357,7 +1357,7 @@ ;; `i64` and smaller. -(rule -1 (lower (has_type (fits_in_64 ty) (iabs x))) +(rule -1 (lower (has_type (fits_in_64 ty) (iabs _ x))) (let ((src Gpr x) (neg ProducesFlags (x64_neg_paired ty src)) ;; Manually extract the result from the neg, then ignore @@ -1370,7 +1370,7 @@ (with_flags_reg (produces_flags_ignore neg) cmove))) ;; `i128`. Negate the low bits, `adc` to the higher bits, then negate high bits. -(rule (lower (has_type $I128 (iabs x))) +(rule (lower (has_type $I128 (iabs _ x))) ;; Get the high/low registers for `x`. (let ((x_regs ValueRegs x) (x_lo Gpr (value_regs_get_gpr x_regs 0)) @@ -1392,35 +1392,35 @@ ;;;; Rules for `fabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fabs x))) +(rule (lower (has_type $F32 (fabs _ x))) (x64_andps x (imm $F32 0x7fffffff))) -(rule (lower (has_type $F64 (fabs x))) +(rule (lower (has_type $F64 (fabs _ x))) (x64_andpd x (imm $F64 0x7fffffffffffffff))) ;; Special case for `f32x4.abs`. -(rule (lower (has_type $F32X4 (fabs x))) +(rule (lower (has_type $F32X4 (fabs _ x))) (x64_andps x (x64_psrld (vector_all_ones) (xmi_imm 1)))) ;; Special case for `f64x2.abs`. -(rule (lower (has_type $F64X2 (fabs x))) +(rule (lower (has_type $F64X2 (fabs _ x))) (x64_andpd x (x64_psrlq (vector_all_ones) (xmi_imm 1)))) ;;;; Rules for `fneg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fneg x))) +(rule (lower (has_type $F32 (fneg _ x))) (x64_xorps x (imm $F32 0x80000000))) -(rule (lower (has_type $F64 (fneg x))) +(rule (lower (has_type $F64 (fneg _ x))) (x64_xorpd x (imm $F64 0x8000000000000000))) -(rule (lower (has_type $F32X4 (fneg x))) +(rule (lower (has_type $F32X4 (fneg _ x))) (x64_xorps x (x64_pslld (vector_all_ones) (xmi_imm 31)))) -(rule (lower (has_type $F64X2 (fneg x))) +(rule (lower (has_type $F64X2 (fneg _ x))) (x64_xorpd x (x64_psllq (vector_all_ones) (xmi_imm 63)))) @@ -1466,14 +1466,14 @@ ;; Call the lower_bmask rule that does all the procssing -(rule (lower (has_type out_ty (bmask x @ (value_type in_ty)))) +(rule (lower (has_type out_ty (bmask _ x @ (value_type in_ty)))) (lower_bmask out_ty in_ty x)) ;;;; Rules for `bnot` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `i64` and smaller. -(rule -2 (lower (has_type ty (bnot x))) +(rule -2 (lower (has_type ty (bnot _ x))) (if (ty_int_ref_scalar_64 ty)) (x64_not ty x)) @@ -1488,23 +1488,23 @@ (value_gprs (x64_not $I64 x_lo) (x64_not $I64 x_hi)))) -(rule (lower (has_type $I128 (bnot x))) +(rule (lower (has_type $I128 (bnot _ x))) (not_i128 x)) ;; f32 and f64 -(rule -3 (lower (has_type (ty_scalar_float ty) (bnot x))) +(rule -3 (lower (has_type (ty_scalar_float ty) (bnot _ x))) (x64_xor_vector ty x (vector_all_ones))) ;; Special case for vector-types where bit-negation is an xor against an ;; all-one value -(rule -1 (lower (has_type ty @ (multi_lane _bits _lanes) (bnot x))) +(rule -1 (lower (has_type ty @ (multi_lane _bits _lanes) (bnot _ x))) (x64_xor_vector ty x (vector_all_ones))) ;;;; Rules for `bitselect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type ty @ (multi_lane _bits _lanes) - (bitselect condition + (bitselect _ condition if_true if_false))) ;; a = and if_true, condition @@ -1518,7 +1518,7 @@ ;; If every byte of the condition is guaranteed to be all ones or all zeroes, ;; we can use x64_blend. (rule 1 (lower (has_type ty @ (multi_lane _bits _lanes) - (bitselect condition + (bitselect _ condition if_true if_false))) (if-let true (has_sse41)) @@ -1526,10 +1526,10 @@ (x64_pblendvb if_false if_true condition)) (decl pure partial all_ones_or_all_zeros (Value) bool) -(rule (all_ones_or_all_zeros (and (icmp _ _ _) (value_type (multi_lane _ _)))) true) -(rule (all_ones_or_all_zeros (and (fcmp _ _ _) (value_type (multi_lane _ _)))) true) -(rule (all_ones_or_all_zeros (and (bitcast _ (fcmp _ _ _)) (value_type (multi_lane _ _)))) true) -(rule (all_ones_or_all_zeros (vconst (vconst_all_ones_or_all_zeros))) true) +(rule (all_ones_or_all_zeros (and (icmp _ _ _ _) (value_type (multi_lane _ _)))) true) +(rule (all_ones_or_all_zeros (and (fcmp _ _ _ _) (value_type (multi_lane _ _)))) true) +(rule (all_ones_or_all_zeros (and (bitcast _ _ (fcmp _ _ _ _)) (value_type (multi_lane _ _)))) true) +(rule (all_ones_or_all_zeros (vconst _ (vconst_all_ones_or_all_zeros))) true) (decl pure vconst_all_ones_or_all_zeros () Constant) (extern extractor vconst_all_ones_or_all_zeros vconst_all_ones_or_all_zeros) @@ -1539,29 +1539,29 @@ ;; instructions and how they're lowered into CLIF. Note the careful ordering ;; of all the operands here to ensure that the input CLIF matched is implemented ;; by the corresponding x64 instruction. -(rule 2 (lower (has_type $F32X4 (bitselect (bitcast _ (fcmp (FloatCC.LessThan) x y)) x y))) +(rule 2 (lower (has_type $F32X4 (bitselect _ (bitcast _ _ (fcmp _ (FloatCC.LessThan) x y)) x y))) (x64_minps x y)) -(rule 2 (lower (has_type $F64X2 (bitselect (bitcast _ (fcmp (FloatCC.LessThan) x y)) x y))) +(rule 2 (lower (has_type $F64X2 (bitselect _ (bitcast _ _ (fcmp _ (FloatCC.LessThan) x y)) x y))) (x64_minpd x y)) -(rule 3 (lower (has_type $F32X4 (bitselect (bitcast _ (fcmp (FloatCC.LessThan) y x)) x y))) +(rule 3 (lower (has_type $F32X4 (bitselect _ (bitcast _ _ (fcmp _ (FloatCC.LessThan) y x)) x y))) (x64_maxps x y)) -(rule 3 (lower (has_type $F64X2 (bitselect (bitcast _ (fcmp (FloatCC.LessThan) y x)) x y))) +(rule 3 (lower (has_type $F64X2 (bitselect _ (bitcast _ _ (fcmp _ (FloatCC.LessThan) y x)) x y))) (x64_maxpd x y)) ;; Scalar rules -(rule 3 (lower (has_type $I128 (bitselect c t f))) +(rule 3 (lower (has_type $I128 (bitselect _ c t f))) (let ((a ValueRegs (and_i128 c t)) (b ValueRegs (and_i128 (not_i128 c) f))) (or_i128 a b))) -(rule 4 (lower (has_type (ty_int_ref_scalar_64 ty) (bitselect c t f))) +(rule 4 (lower (has_type (ty_int_ref_scalar_64 ty) (bitselect _ c t f))) (let ((a Gpr (x64_and ty c t)) (b Gpr (x64_and ty (x64_not ty c) f))) (x64_or ty a b))) -(rule 5 (lower (has_type (ty_scalar_float ty) (bitselect c t f))) +(rule 5 (lower (has_type (ty_scalar_float ty) (bitselect _ c t f))) (let ((a Xmm (sse_and ty c t)) (c_neg Xmm (x64_xor_vector ty c (vector_all_ones))) (b Xmm (sse_and ty c_neg f))) @@ -1570,26 +1570,26 @@ ;;;; Rules for `blendv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type $I8X16 - (blendv condition if_true if_false))) + (blendv _ condition if_true if_false))) (if-let true (has_sse41)) (x64_pblendvb if_false if_true condition)) (rule (lower (has_type $I32X4 - (blendv condition if_true if_false))) + (blendv _ condition if_true if_false))) (if-let true (has_sse41)) (x64_blendvps if_false if_true condition)) (rule (lower (has_type $I64X2 - (blendv condition if_true if_false))) + (blendv _ condition if_true if_false))) (if-let true (has_sse41)) (x64_blendvpd if_false if_true condition)) ;;;; Rules for `insertlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (insertlane vec @ (value_type $I8X16) val (u8_from_uimm8 idx))) +(rule 1 (lower (insertlane _ vec @ (value_type $I8X16) val (u8_from_uimm8 idx))) (if-let true (has_sse41)) (x64_pinsrb vec val idx)) -(rule 2 (lower (insertlane vec @ (value_type $I8X16) (sinkable_load_exact val) (u8_from_uimm8 idx))) +(rule 2 (lower (insertlane _ vec @ (value_type $I8X16) (sinkable_load_exact val) (u8_from_uimm8 idx))) (if-let true (has_sse41)) (x64_pinsrb vec val idx)) @@ -1617,7 +1617,7 @@ ;; This all, laboriously, gets the `val` into the desired lane so it's then ;; `por`'d with the original vec-with-a-hole to produce the final result of the ;; insertion. -(rule (lower (insertlane vec @ (value_type $I8X16) val (u8_from_uimm8 n))) +(rule (lower (insertlane _ vec @ (value_type $I8X16) val (u8_from_uimm8 n))) (let ((vec_with_hole Xmm (x64_pand vec (insert_i8x16_lane_hole n))) (val Gpr (x64_movzx (ExtMode.BL) val)) (val Gpr (x64_shll_mi val (u8_wrapping_shl (u8_and n 3) 3))) @@ -1635,62 +1635,62 @@ ;; i16x8.replace_lane -(rule (lower (insertlane vec @ (value_type $I16X8) val (u8_from_uimm8 idx))) +(rule (lower (insertlane _ vec @ (value_type $I16X8) val (u8_from_uimm8 idx))) (x64_pinsrw vec val idx)) -(rule 1 (lower (insertlane vec @ (value_type $I16X8) (sinkable_load_exact val) (u8_from_uimm8 idx))) +(rule 1 (lower (insertlane _ vec @ (value_type $I16X8) (sinkable_load_exact val) (u8_from_uimm8 idx))) (x64_pinsrw vec val idx)) ;; i32x4.replace_lane -(rule 1 (lower (insertlane vec @ (value_type $I32X4) val (u8_from_uimm8 idx))) +(rule 1 (lower (insertlane _ vec @ (value_type $I32X4) val (u8_from_uimm8 idx))) (if-let true (has_sse41)) (x64_pinsrd vec val idx)) -(rule (lower (insertlane vec @ (value_type $I32X4) val (u8_from_uimm8 0))) +(rule (lower (insertlane _ vec @ (value_type $I32X4) val (u8_from_uimm8 0))) (x64_movss_regmove vec (x64_movd_to_xmm val))) ;; tmp = [ vec[1] vec[0] val[1] val[0] ] ;; result = [ vec[3] vec[2] tmp[0] tmp[2] ] -(rule (lower (insertlane vec @ (value_type $I32X4) val (u8_from_uimm8 1))) +(rule (lower (insertlane _ vec @ (value_type $I32X4) val (u8_from_uimm8 1))) (let ((val Xmm (x64_movd_to_xmm val)) (vec Xmm vec)) (x64_shufps (x64_punpcklqdq val vec) vec 0b11_10_00_10))) ;; tmp = [ vec[0] vec[3] val[0] val[0] ] ;; result = [ tmp[2] tmp[0] vec[1] vec[0] ] -(rule (lower (insertlane vec @ (value_type $I32X4) val (u8_from_uimm8 2))) +(rule (lower (insertlane _ vec @ (value_type $I32X4) val (u8_from_uimm8 2))) (let ((val Xmm (x64_movd_to_xmm val)) (vec Xmm vec)) (x64_shufps vec (x64_shufps val vec 0b00_11_00_00) 0b10_00_01_00))) ;; tmp = [ vec[3] vec[2] val[1] val[0] ] ;; result = [ tmp[0] tmp[2] vec[1] vec[0] ] -(rule (lower (insertlane vec @ (value_type $I32X4) val (u8_from_uimm8 3))) +(rule (lower (insertlane _ vec @ (value_type $I32X4) val (u8_from_uimm8 3))) (let ((val Xmm (x64_movd_to_xmm val)) (vec Xmm vec)) (x64_shufps vec (x64_shufps val vec 0b11_10_01_00) 0b00_10_01_00))) ;; i64x2.replace_lane -(rule 1 (lower (insertlane vec @ (value_type $I64X2) val (u8_from_uimm8 idx))) +(rule 1 (lower (insertlane _ vec @ (value_type $I64X2) val (u8_from_uimm8 idx))) (if-let true (has_sse41)) (x64_pinsrq vec val idx)) -(rule (lower (insertlane vec @ (value_type $I64X2) val (u8_from_uimm8 0))) +(rule (lower (insertlane _ vec @ (value_type $I64X2) val (u8_from_uimm8 0))) (x64_movsd_regmove vec (x64_movq_to_xmm val))) -(rule (lower (insertlane vec @ (value_type $I64X2) val (u8_from_uimm8 1))) +(rule (lower (insertlane _ vec @ (value_type $I64X2) val (u8_from_uimm8 1))) (x64_punpcklqdq vec (x64_movq_to_xmm val))) ;; (i64x2.replace_lane 1) with a splat as source for lane 0 -- we can elide ;; the splat and just do a move. This turns out to be a common pattern when ;; constructing an i64x2 out of two i64s. -(rule 3 (lower (insertlane (has_type $I64X2 (splat lane0)) +(rule 3 (lower (insertlane _ (has_type $I64X2 (splat _ lane0)) lane1 (u8_from_uimm8 1))) (if-let true (has_sse41)) (x64_pinsrq (bitcast_gpr_to_xmm 64 lane0) lane1 1)) -(rule 1 (lower (insertlane vec @ (value_type $F32X4) (sinkable_load val) (u8_from_uimm8 idx))) +(rule 1 (lower (insertlane _ vec @ (value_type $F32X4) (sinkable_load val) (u8_from_uimm8 idx))) (if-let true (has_sse41)) (x64_insertps vec val (sse_insertps_lane_imm idx))) -(rule (lower (insertlane vec @ (value_type $F32X4) val (u8_from_uimm8 idx))) +(rule (lower (insertlane _ vec @ (value_type $F32X4) val (u8_from_uimm8 idx))) (f32x4_insertlane vec val idx)) ;; Helper function used below for `insertlane` but also here for other @@ -1735,7 +1735,7 @@ ;; Here the `movsd` instruction is used specifically to specialize moving ;; into the fist lane where unlike above cases we're not using the lane ;; immediate as an immediate to the instruction itself. -(rule (lower (insertlane vec @ (value_type $F64X2) val (u8_from_uimm8 0))) +(rule (lower (insertlane _ vec @ (value_type $F64X2) val (u8_from_uimm8 0))) (x64_movsd_regmove vec val)) ;; f64x2.replace_lane 1 @@ -1743,7 +1743,7 @@ ;; Here the `movlhps` instruction is used specifically to specialize moving ;; into the second lane where unlike above cases we're not using the lane ;; immediate as an immediate to the instruction itself. -(rule (lower (insertlane vec @ (value_type $F64X2) val (u8_from_uimm8 1))) +(rule (lower (insertlane _ vec @ (value_type $F64X2) val (u8_from_uimm8 1))) (x64_movlhps vec val)) ;;;; Rules for `smin`, `smax`, `umin`, `umax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1761,16 +1761,16 @@ (with_flags_reg (x64_cmp ty y_reg x_reg) (cmove ty cc y_reg x_reg)))) -(rule -1 (lower (has_type (fits_in_64 ty) (umin x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (umin _ x y))) (cmp_and_choose ty (CC.B) x y)) -(rule -1 (lower (has_type (fits_in_64 ty) (umax x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (umax _ x y))) (cmp_and_choose ty (CC.NB) x y)) -(rule -1 (lower (has_type (fits_in_64 ty) (smin x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (smin _ x y))) (cmp_and_choose ty (CC.L) x y)) -(rule -1 (lower (has_type (fits_in_64 ty) (smax x y))) +(rule -1 (lower (has_type (fits_in_64 ty) (smax _ x y))) (cmp_and_choose ty (CC.NL) x y)) ;; SSE helpers for determining if single-instruction lowerings are available. @@ -1797,7 +1797,7 @@ ;; SSE `smax`. -(rule (lower (has_type (ty_vec128 ty) (smax x y))) +(rule (lower (has_type (ty_vec128 ty) (smax _ x y))) (lower_vec_smax ty x y)) (decl lower_vec_smax (Type Xmm Xmm) Xmm) @@ -1817,11 +1817,11 @@ ;; SSE `smin`. -(rule 1 (lower (has_type (ty_vec128 ty) (smin x y))) +(rule 1 (lower (has_type (ty_vec128 ty) (smin _ x y))) (if-let true (has_pmins ty)) (x64_pmins ty x y)) -(rule (lower (has_type (ty_vec128 ty) (smin x y))) +(rule (lower (has_type (ty_vec128 ty) (smin _ x y))) (let ( (x Xmm x) (y Xmm y) @@ -1833,20 +1833,20 @@ ;; SSE `umax`. -(rule 2 (lower (has_type (ty_vec128 ty) (umax x y))) +(rule 2 (lower (has_type (ty_vec128 ty) (umax _ x y))) (if-let true (has_pmaxu ty)) (x64_pmaxu ty x y)) ;; If y < x then the saturating subtraction will be zero, otherwise when added ;; back to x it'll return y. -(rule 1 (lower (has_type $I16X8 (umax x y))) +(rule 1 (lower (has_type $I16X8 (umax _ x y))) (let ((x Xmm x)) (x64_paddw x (x64_psubusw y x)))) ;; Flip the upper bits of each lane so the signed comparison has the same ;; result as a signed comparison, and then select the results with the output ;; mask. See `pcmpgt` lowering for info on flipping the upper bit. -(rule (lower (has_type (ty_vec128 ty) (umax x y))) +(rule (lower (has_type (ty_vec128 ty) (umax _ x y))) (let ( (x Xmm x) (y Xmm y) @@ -1869,18 +1869,18 @@ ;; SSE `umin`. -(rule 2 (lower (has_type (ty_vec128 ty) (umin x y))) +(rule 2 (lower (has_type (ty_vec128 ty) (umin _ x y))) (if-let true (has_pminu ty)) (x64_pminu ty x y)) ;; If x < y then the saturating subtraction will be 0. Otherwise if x > y then ;; the saturated result, when subtracted again, will go back to `y`. -(rule 1 (lower (has_type $I16X8 (umin x y))) +(rule 1 (lower (has_type $I16X8 (umin _ x y))) (let ((x Xmm x)) (x64_psubw x (x64_psubusw x y)))) ;; Same as `umax`, and see `pcmpgt` for docs on flipping the upper bit. -(rule (lower (has_type (ty_vec128 ty) (umin x y))) +(rule (lower (has_type (ty_vec128 ty) (umin _ x y))) (let ( (x Xmm x) (y Xmm y) @@ -1918,7 +1918,7 @@ ;;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_64 ty) (uadd_overflow_trap a b tc))) +(rule (lower (has_type (fits_in_64 ty) (uadd_overflow_trap _ a b tc))) (with_flags (x64_add_with_flags_paired ty a b) (trap_if (CC.B) tc))) @@ -1927,13 +1927,13 @@ ;; handling of above. (rule 1 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap (simm32_from_value a) b tc))) + (uadd_overflow_trap _ (simm32_from_value a) b tc))) (with_flags (x64_add_with_flags_paired ty b a) (trap_if (CC.B) tc))) (rule 2 (lower (has_type (fits_in_64 ty) - (uadd_overflow_trap (sinkable_load a) b tc))) + (uadd_overflow_trap _ (sinkable_load a) b tc))) (with_flags (x64_add_with_flags_paired ty b a) (trap_if (CC.B) tc))) @@ -1946,74 +1946,74 @@ ;;;; Rules for `icmp` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -2 (lower (icmp cc a @ (value_type (fits_in_64 ty)) b)) +(rule -2 (lower (icmp _ cc a @ (value_type (fits_in_64 ty)) b)) (lower_cond_bool (emit_cmp cc a b))) -(rule -1 (lower (icmp cc a @ (value_type $I128) b)) +(rule -1 (lower (icmp _ cc a @ (value_type $I128) b)) (lower_cond_bool (emit_cmp cc a b))) ;; Peephole optimization for `x < 0`, when x is a signed 64 bit value -(rule 2 (lower (has_type $I8 (icmp (IntCC.SignedLessThan) x @ (value_type $I64) (u64_from_iconst 0)))) +(rule 2 (lower (has_type $I8 (icmp _ (IntCC.SignedLessThan) x @ (value_type $I64) (u64_from_iconst 0)))) (x64_shrq_mi x 63)) ;; Peephole optimization for `0 > x`, when x is a signed 64 bit value -(rule 2 (lower (has_type $I8 (icmp (IntCC.SignedGreaterThan) (u64_from_iconst 0) x @ (value_type $I64)))) +(rule 2 (lower (has_type $I8 (icmp _ (IntCC.SignedGreaterThan) (u64_from_iconst 0) x @ (value_type $I64)))) (x64_shrq_mi x 63)) ;; Peephole optimization for `0 <= x`, when x is a signed 64 bit value -(rule 2 (lower (has_type $I8 (icmp (IntCC.SignedLessThanOrEqual) (u64_from_iconst 0) x @ (value_type $I64)))) +(rule 2 (lower (has_type $I8 (icmp _ (IntCC.SignedLessThanOrEqual) (u64_from_iconst 0) x @ (value_type $I64)))) (x64_shrq_mi (x64_not $I64 x) 63)) ;; Peephole optimization for `x >= 0`, when x is a signed 64 bit value -(rule 2 (lower (has_type $I8 (icmp (IntCC.SignedGreaterThanOrEqual) x @ (value_type $I64) (u64_from_iconst 0)))) +(rule 2 (lower (has_type $I8 (icmp _ (IntCC.SignedGreaterThanOrEqual) x @ (value_type $I64) (u64_from_iconst 0)))) (x64_shrq_mi (x64_not $I64 x) 63)) ;; Peephole optimization for `x < 0`, when x is a signed 32 bit value -(rule 2 (lower (has_type $I8 (icmp (IntCC.SignedLessThan) x @ (value_type $I32) (u64_from_iconst 0)))) +(rule 2 (lower (has_type $I8 (icmp _ (IntCC.SignedLessThan) x @ (value_type $I32) (u64_from_iconst 0)))) (x64_shrl_mi x 31)) ;; Peephole optimization for `0 > x`, when x is a signed 32 bit value -(rule 2 (lower (has_type $I8 (icmp (IntCC.SignedGreaterThan) (u64_from_iconst 0) x @ (value_type $I32)))) +(rule 2 (lower (has_type $I8 (icmp _ (IntCC.SignedGreaterThan) (u64_from_iconst 0) x @ (value_type $I32)))) (x64_shrl_mi x 31)) ;; Peephole optimization for `0 <= x`, when x is a signed 32 bit value -(rule 2 (lower (has_type $I8 (icmp (IntCC.SignedLessThanOrEqual) (u64_from_iconst 0) x @ (value_type $I32)))) - (x64_shrl_mi (x64_not $I64 x) 31)) +(rule 2 (lower (has_type $I8 (icmp _ (IntCC.SignedLessThanOrEqual) (u64_from_iconst 0) x @ (value_type $I32)))) + (x64_shrl_mi (x64_not $I32 x) 31)) ;; Peephole optimization for `x >= 0`, when x is a signed 32 bit value -(rule 2 (lower (has_type $I8 (icmp (IntCC.SignedGreaterThanOrEqual) x @ (value_type $I32) (u64_from_iconst 0)))) - (x64_shrl_mi (x64_not $I64 x) 31)) +(rule 2 (lower (has_type $I8 (icmp _ (IntCC.SignedGreaterThanOrEqual) x @ (value_type $I32) (u64_from_iconst 0)))) + (x64_shrl_mi (x64_not $I32 x) 31)) ;; For XMM-held values, we lower to `PCMP*` instructions, sometimes more than ;; one. To note: what is different here about the output values is that each ;; lane will be filled with all 1s or all 0s according to the comparison, ;; whereas for GPR-held values, the result will be simply 0 or 1 (upper bits ;; unset). -(rule (lower (icmp (IntCC.Equal) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.Equal) a @ (value_type (ty_vec128 ty)) b)) (x64_pcmpeq ty a b)) ;; To lower a not-equals comparison, we perform an equality comparison ;; (PCMPEQ*) and then invert the bits (PXOR with all 1s). -(rule (lower (icmp (IntCC.NotEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.NotEqual) a @ (value_type (ty_vec128 ty)) b)) (let ((checked Xmm (x64_pcmpeq ty a b)) (all_ones Xmm (vector_all_ones))) (x64_pxor checked all_ones))) ;; SSE `sgt` -(rule (lower (icmp (IntCC.SignedGreaterThan) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.SignedGreaterThan) a @ (value_type (ty_vec128 ty)) b)) (x64_pcmpgt ty a b)) ;; SSE `slt` -(rule (lower (icmp (IntCC.SignedLessThan) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.SignedLessThan) a @ (value_type (ty_vec128 ty)) b)) (x64_pcmpgt ty b a)) ;; SSE `ugt` ;; N.B.: we must manually prevent load coalescing operands; the ;; register allocator gets confused otherwise. -(rule 1 (lower (icmp (IntCC.UnsignedGreaterThan) a @ (value_type (ty_vec128 ty)) b)) +(rule 1 (lower (icmp _ (IntCC.UnsignedGreaterThan) a @ (value_type (ty_vec128 ty)) b)) (if-let true (has_pmaxu ty)) (let ((a Xmm a) (b Xmm b) @@ -2023,7 +2023,7 @@ ;; Flip the upper bit of each lane so the result of a signed comparison is the ;; same as the result of an unsigned comparison (see docs on `pcmpgt` for more) -(rule (lower (icmp (IntCC.UnsignedGreaterThan) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.UnsignedGreaterThan) a @ (value_type (ty_vec128 ty)) b)) (let ((mask Xmm (flip_high_bit_mask ty)) (a_masked Xmm (x64_pxor a mask)) (b_masked Xmm (x64_pxor b mask))) @@ -2031,7 +2031,7 @@ ;; SSE `ult` -(rule 1 (lower (icmp (IntCC.UnsignedLessThan) a @ (value_type (ty_vec128 ty)) b)) +(rule 1 (lower (icmp _ (IntCC.UnsignedLessThan) a @ (value_type (ty_vec128 ty)) b)) (if-let true (has_pminu ty)) ;; N.B.: see note above. (let ((a Xmm a) @@ -2042,7 +2042,7 @@ ;; Flip the upper bit of `a` and `b` so the signed comparison result will ;; be the same as the unsigned comparison result (see docs on `pcmpgt` for more). -(rule (lower (icmp (IntCC.UnsignedLessThan) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.UnsignedLessThan) a @ (value_type (ty_vec128 ty)) b)) (let ((mask Xmm (flip_high_bit_mask ty)) (a_masked Xmm (x64_pxor a mask)) (b_masked Xmm (x64_pxor b mask))) @@ -2051,41 +2051,41 @@ ;; SSE `sge` ;; Use `pmaxs*` and compare the result to `a` to see if it's `>= b`. -(rule 1 (lower (icmp (IntCC.SignedGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule 1 (lower (icmp _ (IntCC.SignedGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (if-let true (has_pmaxs ty)) (x64_pcmpeq ty a (x64_pmaxs ty a b))) ;; Without `pmaxs*` use a `pcmpgt*` with reversed operands and invert the ;; result. -(rule (lower (icmp (IntCC.SignedGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.SignedGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (x64_pxor (x64_pcmpgt ty b a) (vector_all_ones))) ;; SSE `sle` ;; With `pmins*` use that and compare the result to `a`. -(rule 1 (lower (icmp (IntCC.SignedLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule 1 (lower (icmp _ (IntCC.SignedLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (if-let true (has_pmins ty)) (x64_pcmpeq ty a (x64_pmins ty a b))) ;; Without `pmins*` perform a greater-than test and invert the result. -(rule (lower (icmp (IntCC.SignedLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.SignedLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (x64_pxor (x64_pcmpgt ty a b) (vector_all_ones))) ;; SSE `uge` -(rule 2 (lower (icmp (IntCC.UnsignedGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule 2 (lower (icmp _ (IntCC.UnsignedGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (if-let true (has_pmaxu ty)) (x64_pcmpeq ty a (x64_pmaxu ty a b))) ;; Perform a saturating subtract of `a` from `b` and if the result is zero then ;; `a` is greater or equal. -(rule 1 (lower (icmp (IntCC.UnsignedGreaterThanOrEqual) a @ (value_type $I16X8) b)) +(rule 1 (lower (icmp _ (IntCC.UnsignedGreaterThanOrEqual) a @ (value_type $I16X8) b)) (x64_pcmpeqw (x64_psubusw b a) (xmm_zero $I16X8))) ;; Flip the upper bit of each lane so the signed comparison is the same as ;; an unsigned one and then invert the result. See docs on `pcmpgt` for why ;; flipping the upper bit works. -(rule (lower (icmp (IntCC.UnsignedGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.UnsignedGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (let ( (mask Xmm (flip_high_bit_mask ty)) (a_masked Xmm (x64_pxor a mask)) @@ -2096,14 +2096,14 @@ ;; SSE `ule` -(rule 2 (lower (icmp (IntCC.UnsignedLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule 2 (lower (icmp _ (IntCC.UnsignedLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (if-let true (has_pminu ty)) (x64_pcmpeq ty a (x64_pminu ty a b))) ;; A saturating subtraction will produce zeros if `a` is less than `b`, so ;; compare that result to an all-zeros result to figure out lanes of `a` that ;; are <= to the lanes in `b` -(rule 1 (lower (icmp (IntCC.UnsignedLessThanOrEqual) a @ (value_type $I16X8) b)) +(rule 1 (lower (icmp _ (IntCC.UnsignedLessThanOrEqual) a @ (value_type $I16X8) b)) (let ((zeros_if_a_is_min Xmm (x64_psubusw a b))) (x64_pcmpeqw zeros_if_a_is_min (xmm_zero $I8X16)))) @@ -2111,7 +2111,7 @@ ;; produces the same result as an unsigned comparison. Then test test for `gt` ;; and invert the result to get the `le` that is desired here. See docs on ;; `pcmpgt` for why flipping the upper bit works. -(rule (lower (icmp (IntCC.UnsignedLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (icmp _ (IntCC.UnsignedLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (let ( (mask Xmm (flip_high_bit_mask ty)) (a_masked Xmm (x64_pxor a mask)) @@ -2139,40 +2139,40 @@ ;; - less than assigns Z = 0, P = 0, C = 1 ;; - equal assigns Z = 1, P = 0, C = 0 -(rule -1 (lower (fcmp cc a @ (value_type (ty_scalar_float ty)) b)) +(rule -1 (lower (fcmp _ cc a @ (value_type (ty_scalar_float ty)) b)) (lower_cond_bool (emit_fcmp cc a b))) ;; For vector lowerings, we use `CMPP*` instructions with a 3-bit operand that ;; determines the comparison to make. Note that comparisons that succeed will ;; fill the lane with 1s; comparisons that do not will fill the lane with 0s. -(rule (lower (fcmp (FloatCC.Equal) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.Equal) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty a b (FcmpImm.Equal))) -(rule (lower (fcmp (FloatCC.NotEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.NotEqual) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty a b (FcmpImm.NotEqual))) -(rule (lower (fcmp (FloatCC.LessThan) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.LessThan) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty a b (FcmpImm.LessThan))) -(rule (lower (fcmp (FloatCC.LessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.LessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty a b (FcmpImm.LessThanOrEqual))) -(rule (lower (fcmp (FloatCC.Ordered) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.Ordered) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty a b (FcmpImm.Ordered))) -(rule (lower (fcmp (FloatCC.Unordered) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.Unordered) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty a b (FcmpImm.Unordered))) -(rule (lower (fcmp (FloatCC.UnorderedOrGreaterThan) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.UnorderedOrGreaterThan) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty a b (FcmpImm.UnorderedOrGreaterThan))) -(rule (lower (fcmp (FloatCC.UnorderedOrGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.UnorderedOrGreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty a b (FcmpImm.UnorderedOrGreaterThanOrEqual))) ;; Some vector lowerings rely on flipping the operands and using a reversed ;; comparison code. -(rule (lower (fcmp (FloatCC.GreaterThan) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.GreaterThan) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty b a (FcmpImm.LessThan))) -(rule (lower (fcmp (FloatCC.GreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.GreaterThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty b a (FcmpImm.LessThanOrEqual))) -(rule (lower (fcmp (FloatCC.UnorderedOrLessThan) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.UnorderedOrLessThan) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty b a (FcmpImm.UnorderedOrGreaterThan))) -(rule (lower (fcmp (FloatCC.UnorderedOrLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) +(rule (lower (fcmp _ (FloatCC.UnorderedOrLessThanOrEqual) a @ (value_type (ty_vec128 ty)) b)) (x64_cmpp ty b a (FcmpImm.UnorderedOrGreaterThanOrEqual))) ;; Some vector lowerings are simply not supported for certain codes: @@ -2181,7 +2181,7 @@ ;;;; Rules for `select` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (select cond x y)) (lower_select (is_nonzero_cmp cond) x y)) +(rule (lower (select _ cond x y)) (lower_select (is_nonzero_cmp cond) x y)) ; Recursion: at most once to swap the And case for an Or. (decl rec lower_select (CondResult Value Value) InstOutput) @@ -2249,21 +2249,21 @@ ;; Specializations for floating-point compares to generate a `mins*` or a ;; `maxs*` instruction. These are equivalent to the "pseudo-m{in,ax}" ;; specializations for vectors. -(rule 3 (lower (has_type $F32 (select (maybe_uextend (fcmp (FloatCC.LessThan) x y)) x y))) +(rule 3 (lower (has_type $F32 (select _ (maybe_uextend (fcmp _ (FloatCC.LessThan) x y)) x y))) (x64_minss x y)) -(rule 3 (lower (has_type $F64 (select (maybe_uextend (fcmp (FloatCC.LessThan) x y)) x y))) +(rule 3 (lower (has_type $F64 (select _ (maybe_uextend (fcmp _ (FloatCC.LessThan) x y)) x y))) (x64_minsd x y)) -(rule 4 (lower (has_type $F32 (select (maybe_uextend (fcmp (FloatCC.LessThan) y x)) x y))) +(rule 4 (lower (has_type $F32 (select _ (maybe_uextend (fcmp _ (FloatCC.LessThan) y x)) x y))) (x64_maxss x y)) -(rule 4 (lower (has_type $F64 (select (maybe_uextend (fcmp (FloatCC.LessThan) y x)) x y))) +(rule 4 (lower (has_type $F64 (select _ (maybe_uextend (fcmp _ (FloatCC.LessThan) y x)) x y))) (x64_maxsd x y)) ;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 2 (lower (has_type (ty_32_or_64 ty) (clz src))) +(rule 2 (lower (has_type (ty_32_or_64 ty) (clz _ src))) (do_clz ty ty src)) -(rule 1 (lower (has_type (ty_8_or_16 ty) (clz src))) +(rule 1 (lower (has_type (ty_8_or_16 ty) (clz _ src))) (let ((extended Gpr (extend_to_gpr src $I64 (ExtendKind.Zero))) (clz Gpr (do_clz $I64 $I64 extended))) (x64_sub $I64 clz (RegMemImm.Imm (u32_wrapping_sub 64 (ty_bits ty)))))) @@ -2271,7 +2271,7 @@ (rule 0 (lower (has_type $I128 - (clz src))) + (clz _ src))) (let ((upper Gpr (do_clz $I64 $I64 (value_regs_get_gpr src 1))) (lower Gpr (x64_add $I64 (do_clz $I64 $I64 (value_regs_get_gpr src 0)) @@ -2300,17 +2300,17 @@ ;; Rules for `ctz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 2 (lower (has_type (ty_32_or_64 ty) (ctz src))) +(rule 2 (lower (has_type (ty_32_or_64 ty) (ctz _ src))) (do_ctz ty ty src)) -(rule 1 (lower (has_type (ty_8_or_16 ty) (ctz src))) +(rule 1 (lower (has_type (ty_8_or_16 ty) (ctz _ src))) (let ((extended Gpr (extend_to_gpr src $I32 (ExtendKind.Zero))) (stopbit Gpr (x64_or $I32 extended (RegMemImm.Imm (u32_wrapping_shl 1 (ty_bits ty)))))) (do_ctz $I32 ty stopbit))) (rule 0 (lower (has_type $I128 - (ctz src))) + (ctz _ src))) (let ((lower Gpr (do_ctz $I64 $I64 (value_regs_get_gpr src 0))) (upper Gpr (x64_add $I64 (do_ctz $I64 $I64 (value_regs_get_gpr src 1)) @@ -2334,17 +2334,17 @@ ;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 2 (lower (has_type (ty_32_or_64 ty) (cls src))) +(rule 2 (lower (has_type (ty_32_or_64 ty) (cls _ src))) (do_cls ty src)) -(rule 1 (lower (has_type (ty_8_or_16 ty) (cls src))) +(rule 1 (lower (has_type (ty_8_or_16 ty) (cls _ src))) (let ((extended Gpr (extend_to_gpr src $I32 (ExtendKind.Sign))) (cls Gpr (do_cls $I32 extended))) (x64_sub $I32 cls (RegMemImm.Imm (u32_wrapping_sub 32 (ty_bits ty)))))) (rule 0 (lower (has_type $I128 - (cls src))) + (cls _ src))) (let ((upper Gpr (do_cls $I64 (value_regs_get_gpr src 1))) (sign_fill Gpr (x64_sarq_mi (value_regs_get_gpr src 1) 63)) (xored Gpr (x64_xor $I64 (value_regs_get_gpr src 0) sign_fill)) @@ -2369,15 +2369,15 @@ ;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 4 (lower (has_type (ty_32_or_64 ty) (popcnt src))) +(rule 4 (lower (has_type (ty_32_or_64 ty) (popcnt _ src))) (if-let true (use_popcnt)) (x64_popcnt ty src)) -(rule 3 (lower (has_type (ty_8_or_16 ty) (popcnt src))) +(rule 3 (lower (has_type (ty_8_or_16 ty) (popcnt _ src))) (if-let true (use_popcnt)) (x64_popcnt $I32 (extend_to_gpr src $I32 (ExtendKind.Zero)))) -(rule 1 (lower (has_type $I128 (popcnt src))) +(rule 1 (lower (has_type $I128 (popcnt _ src))) (if-let true (use_popcnt)) (let ((lo_count Gpr (x64_popcnt $I64 (value_regs_get_gpr src 0))) (hi_count Gpr (x64_popcnt $I64 (value_regs_get_gpr src 1)))) @@ -2385,17 +2385,17 @@ (rule -1 (lower (has_type (ty_32_or_64 ty) - (popcnt src))) + (popcnt _ src))) (do_popcnt ty src)) (rule -2 (lower (has_type (ty_8_or_16 ty) - (popcnt src))) + (popcnt _ src))) (do_popcnt $I32 (extend_to_gpr src $I32 (ExtendKind.Zero)))) (rule (lower (has_type $I128 - (popcnt src))) + (popcnt _ src))) (let ((lo_count Gpr (do_popcnt $I64 (value_regs_get_gpr src 0))) (hi_count Gpr (do_popcnt $I64 (value_regs_get_gpr src 1)))) (value_regs (x64_add $I64 lo_count hi_count) (imm $I64 0)))) @@ -2462,7 +2462,7 @@ final)) -(rule 2 (lower (has_type $I8X16 (popcnt src))) +(rule 2 (lower (has_type $I8X16 (popcnt _ src))) (if-let true (has_avx512vl)) (if-let true (has_avx512bitalg)) (x64_vpopcntb src)) @@ -2488,7 +2488,7 @@ ;; __m128i lookup = _mm_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4); -(rule 1 (lower (has_type $I8X16 (popcnt src))) +(rule 1 (lower (has_type $I8X16 (popcnt _ src))) (if-let true (has_ssse3)) (let ((low_mask XmmMem (emit_u128_le_const 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f)) (low_nibbles Xmm (sse_and $I8X16 src low_mask)) @@ -2504,7 +2504,7 @@ (x64_paddb bit_counts_low bit_counts_high))) ;; A modified version of the popcnt method from Hacker's Delight. -(rule (lower (has_type $I8X16 (popcnt src))) +(rule (lower (has_type $I8X16 (popcnt _ src))) (let ((mask1 XmmMem (emit_u128_le_const 0x77777777777777777777777777777777)) (src Xmm src) (shifted Xmm (x64_pand (x64_psrlq src (xmi_imm 1)) mask1)) @@ -2518,19 +2518,19 @@ ;; Rules for `bitrev` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8 (bitrev src))) +(rule (lower (has_type $I8 (bitrev _ src))) (do_bitrev8 $I32 src)) -(rule (lower (has_type $I16 (bitrev src))) +(rule (lower (has_type $I16 (bitrev _ src))) (do_bitrev16 $I32 src)) -(rule (lower (has_type $I32 (bitrev src))) +(rule (lower (has_type $I32 (bitrev _ src))) (do_bitrev32 $I32 src)) -(rule (lower (has_type $I64 (bitrev src))) +(rule (lower (has_type $I64 (bitrev _ src))) (do_bitrev64 $I64 src)) -(rule (lower (has_type $I128 (bitrev src))) +(rule (lower (has_type $I128 (bitrev _ src))) (value_regs (do_bitrev64 $I64 (value_regs_get_gpr src 1)) (do_bitrev64 $I64 (value_regs_get_gpr src 0)))) @@ -2597,16 +2597,16 @@ ;; x64 bswap instruction is only for 32- or 64-bit swaps ;; implement the 16-bit swap as a rotl by 8 -(rule (lower (has_type $I16 (bswap src))) +(rule (lower (has_type $I16 (bswap _ src))) (x64_rolw_mi src 8)) -(rule (lower (has_type $I32 (bswap src))) +(rule (lower (has_type $I32 (bswap _ src))) (x64_bswap $I32 src)) -(rule (lower (has_type $I64 (bswap src))) +(rule (lower (has_type $I64 (bswap _ src))) (x64_bswap $I64 src)) -(rule (lower (has_type $I128 (bswap src))) +(rule (lower (has_type $I128 (bswap _ src))) (value_regs (x64_bswap $I64 (value_regs_get_gpr src 1)) (x64_bswap $I64 (value_regs_get_gpr src 0)))) @@ -2614,16 +2614,16 @@ ;; Rules for `uextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; I{8,16,32,64} -> I128. -(rule (lower (has_type $I128 (uextend src))) +(rule (lower (has_type $I128 (uextend _ src))) (value_regs (extend_to_gpr src $I64 (ExtendKind.Zero)) (imm $I64 0))) ;; I{8,16,32} -> I64. -(rule (lower (has_type $I64 (uextend src))) +(rule (lower (has_type $I64 (uextend _ src))) (extend_to_gpr src $I64 (ExtendKind.Zero))) ;; I{8,16} -> I32 ;; I8 -> I16 -(rule -1 (lower (has_type (fits_in_32 _) (uextend src))) +(rule -1 (lower (has_type (fits_in_32 _) (uextend _ src))) (extend_to_gpr src $I32 (ExtendKind.Zero))) ;; Rules for `sextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2632,30 +2632,30 @@ ;; ;; Produce upper 64 bits sign-extended from lower 64: shift right by ;; 63 bits to spread the sign bit across the result. -(rule (lower (has_type $I128 (sextend src))) +(rule (lower (has_type $I128 (sextend _ src))) (let ((lo Gpr (extend_to_gpr src $I64 (ExtendKind.Sign))) (hi Gpr (x64_sarq_mi lo 63))) (value_regs lo hi))) ;; I{8,16,32} -> I64. -(rule (lower (has_type $I64 (sextend src))) +(rule (lower (has_type $I64 (sextend _ src))) (extend_to_gpr src $I64 (ExtendKind.Sign))) ;; I{8,16} -> I32 ;; I8 -> I16 -(rule -1 (lower (has_type (fits_in_32 _) (sextend src))) +(rule -1 (lower (has_type (fits_in_32 _) (sextend _ src))) (extend_to_gpr src $I32 (ExtendKind.Sign))) ;; Rules for `ireduce` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; T -> T is always a no-op, even I128 -> I128. -(rule (lower (has_type ty (ireduce src @ (value_type ty)))) +(rule (lower (has_type ty (ireduce _ src @ (value_type ty)))) src) ;; T -> I{64,32,16,8}: We can simply pass through the value: values ;; are always stored with high bits undefined, so we can just leave ;; them be. -(rule 1 (lower (has_type (fits_in_64 ty) (ireduce src))) +(rule 1 (lower (has_type (fits_in_64 ty) (ireduce _ src))) (value_regs_get_gpr src 0)) ;; Rules for `debugtrap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2665,107 +2665,107 @@ ;; Rules for `x86_pmaddubsw` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I16X8 (x86_pmaddubsw x y))) +(rule (lower (has_type $I16X8 (x86_pmaddubsw _ x y))) (if-let true (has_ssse3)) (x64_pmaddubsw y x)) ;; Rules for `fadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fadd x y))) +(rule (lower (has_type $F32 (fadd _ x y))) (x64_addss x y)) -(rule (lower (has_type $F64 (fadd x y))) +(rule (lower (has_type $F64 (fadd _ x y))) (x64_addsd x y)) -(rule (lower (has_type $F32X4 (fadd x y))) +(rule (lower (has_type $F32X4 (fadd _ x y))) (x64_addps x y)) -(rule (lower (has_type $F64X2 (fadd x y))) +(rule (lower (has_type $F64X2 (fadd _ x y))) (x64_addpd x y)) ;; The above rules automatically sink loads for rhs operands, so additionally ;; add rules for sinking loads with lhs operands. -(rule 1 (lower (has_type $F32 (fadd (sinkable_load x) y))) +(rule 1 (lower (has_type $F32 (fadd _ (sinkable_load x) y))) (x64_addss y x)) -(rule 1 (lower (has_type $F64 (fadd (sinkable_load x) y))) +(rule 1 (lower (has_type $F64 (fadd _ (sinkable_load x) y))) (x64_addsd y x)) -(rule 1 (lower (has_type $F32X4 (fadd (sinkable_load x) y))) +(rule 1 (lower (has_type $F32X4 (fadd _ (sinkable_load x) y))) (x64_addps y x)) -(rule 1 (lower (has_type $F64X2 (fadd (sinkable_load x) y))) +(rule 1 (lower (has_type $F64X2 (fadd _ (sinkable_load x) y))) (x64_addpd y x)) ;; Rules for `fsub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fsub x y))) +(rule (lower (has_type $F32 (fsub _ x y))) (x64_subss x y)) -(rule (lower (has_type $F64 (fsub x y))) +(rule (lower (has_type $F64 (fsub _ x y))) (x64_subsd x y)) -(rule (lower (has_type $F32X4 (fsub x y))) +(rule (lower (has_type $F32X4 (fsub _ x y))) (x64_subps x y)) -(rule (lower (has_type $F64X2 (fsub x y))) +(rule (lower (has_type $F64X2 (fsub _ x y))) (x64_subpd x y)) ;; Rules for `fmul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fmul x y))) +(rule (lower (has_type $F32 (fmul _ x y))) (x64_mulss x y)) -(rule (lower (has_type $F64 (fmul x y))) +(rule (lower (has_type $F64 (fmul _ x y))) (x64_mulsd x y)) -(rule (lower (has_type $F32X4 (fmul x y))) +(rule (lower (has_type $F32X4 (fmul _ x y))) (x64_mulps x y)) -(rule (lower (has_type $F64X2 (fmul x y))) +(rule (lower (has_type $F64X2 (fmul _ x y))) (x64_mulpd x y)) ;; The above rules automatically sink loads for rhs operands, so additionally ;; add rules for sinking loads with lhs operands. -(rule 1 (lower (has_type $F32 (fmul (sinkable_load x) y))) +(rule 1 (lower (has_type $F32 (fmul _ (sinkable_load x) y))) (x64_mulss y x)) -(rule 1 (lower (has_type $F64 (fmul (sinkable_load x) y))) +(rule 1 (lower (has_type $F64 (fmul _ (sinkable_load x) y))) (x64_mulsd y x)) -(rule 1 (lower (has_type $F32X4 (fmul (sinkable_load x) y))) +(rule 1 (lower (has_type $F32X4 (fmul _ (sinkable_load x) y))) (x64_mulps y x)) -(rule 1 (lower (has_type $F64X2 (fmul (sinkable_load x) y))) +(rule 1 (lower (has_type $F64X2 (fmul _ (sinkable_load x) y))) (x64_mulpd y x)) ;; Rules for `fdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fdiv x y))) +(rule (lower (has_type $F32 (fdiv _ x y))) (x64_divss x y)) -(rule (lower (has_type $F64 (fdiv x y))) +(rule (lower (has_type $F64 (fdiv _ x y))) (x64_divsd x y)) -(rule (lower (has_type $F32X4 (fdiv x y))) +(rule (lower (has_type $F32X4 (fdiv _ x y))) (x64_divps x y)) -(rule (lower (has_type $F64X2 (fdiv x y))) +(rule (lower (has_type $F64X2 (fdiv _ x y))) (x64_divpd x y)) ;; Rules for `sqrt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (sqrt x))) +(rule (lower (has_type $F32 (sqrt _ x))) (x64_sqrtss (xmm_zero $F32X4) x)) -(rule (lower (has_type $F64 (sqrt x))) +(rule (lower (has_type $F64 (sqrt _ x))) (x64_sqrtsd (xmm_zero $F64X2) x)) -(rule (lower (has_type $F32X4 (sqrt x))) +(rule (lower (has_type $F32X4 (sqrt _ x))) (x64_sqrtps x)) -(rule (lower (has_type $F64X2 (sqrt x))) +(rule (lower (has_type $F64X2 (sqrt _ x))) (x64_sqrtpd x)) ;; Rules for `fpromote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F64 (fpromote x))) +(rule (lower (has_type $F64 (fpromote _ x))) (x64_cvtss2sd (xmm_zero $F64X2) x)) ;; Rules for `fvpromote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F64X2 (fvpromote_low x))) +(rule (lower (has_type $F64X2 (fvpromote_low _ x))) (x64_cvtps2pd (put_in_xmm x))) ;; Rules for `fdemote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fdemote x))) +(rule (lower (has_type $F32 (fdemote _ x))) (x64_cvtsd2ss (xmm_zero $F32X4) x)) ;; Rules for `fvdemote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32X4 (fvdemote x))) +(rule (lower (has_type $F32X4 (fvdemote _ x))) (x64_cvtpd2ps x)) ;; Rules for `fmin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fmin x y))) +(rule (lower (has_type $F32 (fmin _ x y))) (xmm_min_max_seq $F32 true x y)) -(rule (lower (has_type $F64 (fmin x y))) +(rule (lower (has_type $F64 (fmin _ x y))) (xmm_min_max_seq $F64 true x y)) ;; Vector-typed version. We don't use single pseudoinstructions as @@ -2780,7 +2780,7 @@ ;; require that fmin(NaN, _) = fmin(_, NaN) = NaN, and fmin(+0, -0) = ;; fmin(-0, +0) = -0. -(rule (lower (has_type $F32X4 (fmin x y))) +(rule (lower (has_type $F32X4 (fmin _ x y))) ;; Compute min(x, y) and min(y, x) with native ;; instructions. These will differ in one of the edge cases ;; above that we have to handle properly. (Conversely, if they @@ -2825,7 +2825,7 @@ ;; Likewise for F64 lanes, except that the right-shift is by 13 bits ;; (1 sign, 11 exponent, 1 QNaN bit). -(rule (lower (has_type $F64X2 (fmin x y))) +(rule (lower (has_type $F64X2 (fmin _ x y))) (let ((x Xmm x) ;; force x/y into registers and disallow load sinking (y Xmm y) (min1 Xmm (x64_minpd x y)) @@ -2839,15 +2839,15 @@ ;; Rules for `fmax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fmax x y))) +(rule (lower (has_type $F32 (fmax _ x y))) (xmm_min_max_seq $F32 false x y)) -(rule (lower (has_type $F64 (fmax x y))) +(rule (lower (has_type $F64 (fmax _ x y))) (xmm_min_max_seq $F64 false x y)) ;; The vector version of fmax here is a dual to the fmin sequence ;; above, almost, with a few differences. -(rule (lower (has_type $F32X4 (fmax x y))) +(rule (lower (has_type $F32X4 (fmax _ x y))) ;; Compute max(x, y) and max(y, x) with native ;; instructions. These will differ in one of the edge cases ;; above that we have to handle properly. (Conversely, if they @@ -2892,7 +2892,7 @@ (final Xmm (x64_andnps nan_fraction_mask max_blended_nan_positive))) final)) -(rule (lower (has_type $F64X2 (fmax x y))) +(rule (lower (has_type $F64X2 (fmax _ x y))) ;; Compute max(x, y) and max(y, x) with native ;; instructions. These will differ in one of the edge cases ;; above that we have to handle properly. (Conversely, if they @@ -2943,12 +2943,12 @@ ;; Base case for fma is to call out to one of two libcalls. For vectors they ;; need to be decomposed, handle each element individually, and then recomposed. -(rule (lower (has_type $F32 (fma x y z))) +(rule (lower (has_type $F32 (fma _ x y z))) (libcall_3 (LibCall.FmaF32) x y z)) -(rule (lower (has_type $F64 (fma x y z))) +(rule (lower (has_type $F64 (fma _ x y z))) (libcall_3 (LibCall.FmaF64) x y z)) -(rule (lower (has_type $F32X4 (fma x y z))) +(rule (lower (has_type $F32X4 (fma _ x y z))) (let ( (x Xmm (put_in_xmm x)) (y Xmm (put_in_xmm y)) @@ -2972,7 +2972,7 @@ (tmp Xmm (f32x4_insertlane tmp x3 3)) ) tmp)) -(rule (lower (has_type $F64X2 (fma x y z))) +(rule (lower (has_type $F64X2 (fma _ x y z))) (let ( (x Xmm (put_in_xmm x)) (y Xmm (put_in_xmm y)) @@ -2988,7 +2988,7 @@ ;; Special case for when the `fma` feature is active and a native instruction ;; can be used. -(rule 1 (lower (has_type ty (fma x y z))) +(rule 1 (lower (has_type ty (fma _ x y z))) (if-let true (use_fma)) (fmadd ty x y z)) @@ -3007,15 +3007,15 @@ ;; If one of the values being multiplied is negated then use a `vfnmadd*` ;; instruction instead -(rule 3 (fmadd ty (fneg x) y z) (fnmadd ty x y z)) -(rule 4 (fmadd ty x (fneg y) z) (fnmadd ty x y z)) +(rule 3 (fmadd ty (fneg _ x) y z) (fnmadd ty x y z)) +(rule 4 (fmadd ty x (fneg _ y) z) (fnmadd ty x y z)) (rule (fnmadd ty x y z) (x64_vfnmadd213 ty x y z)) (rule 1 (fnmadd ty (sinkable_load x) y z) (x64_vfnmadd132 ty y z x)) (rule 2 (fnmadd ty x (sinkable_load y) z) (x64_vfnmadd132 ty x z y)) -(rule 2 (lower (has_type ty (fma x y (fneg z)))) +(rule 2 (lower (has_type ty (fma _ x y (fneg _ z)))) (if-let true (use_fma)) (fmsub ty x y z)) @@ -3034,8 +3034,8 @@ ;; If one of the values being multiplied is negated then use a `vfnmsub*` ;; instruction instead -(rule 3 (fmsub ty (fneg x) y z) (fnmsub ty x y z)) -(rule 4 (fmsub ty x (fneg y) z) (fnmsub ty x y z)) +(rule 3 (fmsub ty (fneg _ x) y z) (fnmsub ty x y z)) +(rule 4 (fmsub ty x (fneg _ y) z) (fnmsub ty x y z)) (rule (fnmsub ty x y z) (x64_vfnmsub213 ty x y z)) (rule 1 (fnmsub ty (sinkable_load x) y z) (x64_vfnmsub132 ty y z x)) @@ -3051,86 +3051,86 @@ ;; ;; By default, we zero-extend all sub-64-bit loads to a GPR. (rule load_sub64_x64_movzx -4 (lower (has_type (and (fits_in_32 ty) (is_gpr_type _)) - (load (little_or_native_endian flags) address offset))) + (load _ (little_or_native_endian flags) address offset))) (x64_movzx (ext_mode (ty_bits_u16 ty) 64) (to_amode flags address offset))) ;; But if we know that both the `from` and `to` are 64 bits, we simply load with ;; no extension. -(rule load_64_x64_movzx -1 (lower (has_type (ty_int_ref_64 ty) (load (little_or_native_endian flags) address offset))) +(rule load_64_x64_movzx -1 (lower (has_type (ty_int_ref_64 ty) (load _ (little_or_native_endian flags) address offset))) (x64_mov (to_amode flags address offset))) ;; Also, certain scalar loads have a specific `from` width and extension kind ;; (signed -> `sx`, zeroed -> `zx`). We overwrite the high bits of the 64-bit ;; GPR even if the `to` type is smaller (e.g., 16-bits). -(rule (lower (has_type (is_gpr_type ty) (uload8 (little_or_native_endian flags) address offset))) +(rule (lower (has_type (is_gpr_type ty) (uload8 _ (little_or_native_endian flags) address offset))) (x64_movzx (ExtMode.BQ) (to_amode flags address offset))) -(rule (lower (has_type (is_gpr_type ty) (sload8 (little_or_native_endian flags) address offset))) +(rule (lower (has_type (is_gpr_type ty) (sload8 _ (little_or_native_endian flags) address offset))) (x64_movsx (ExtMode.BQ) (to_amode flags address offset))) -(rule (lower (has_type (is_gpr_type ty) (uload16 (little_or_native_endian flags) address offset))) +(rule (lower (has_type (is_gpr_type ty) (uload16 _ (little_or_native_endian flags) address offset))) (x64_movzx (ExtMode.WQ) (to_amode flags address offset))) -(rule (lower (has_type (is_gpr_type ty) (sload16 (little_or_native_endian flags) address offset))) +(rule (lower (has_type (is_gpr_type ty) (sload16 _ (little_or_native_endian flags) address offset))) (x64_movsx (ExtMode.WQ) (to_amode flags address offset))) -(rule (lower (has_type (is_gpr_type ty) (uload32 (little_or_native_endian flags) address offset))) +(rule (lower (has_type (is_gpr_type ty) (uload32 _ (little_or_native_endian flags) address offset))) (x64_movzx (ExtMode.LQ) (to_amode flags address offset))) -(rule (lower (has_type (is_gpr_type ty) (sload32 (little_or_native_endian flags) address offset))) +(rule (lower (has_type (is_gpr_type ty) (sload32 _ (little_or_native_endian flags) address offset))) (x64_movsx (ExtMode.LQ) (to_amode flags address offset))) ;; To load to XMM registers, we use the x64-specific instructions for each type. ;; For `$F32` and `$F64` this is important--we only want to load 32 or 64 bits. ;; But for the 128-bit types, this is not strictly necessary for performance but ;; might help with clarity during disassembly. -(rule 4 (lower (has_type (is_xmm_type (ty_16 _)) (load (little_or_native_endian flags) address offset))) +(rule 4 (lower (has_type (is_xmm_type (ty_16 _)) (load _ (little_or_native_endian flags) address offset))) (x64_pinsrw (xmm_uninit_value) (to_amode flags address offset) 0)) -(rule 3 (lower (has_type (is_xmm_type (ty_32 _)) (load (little_or_native_endian flags) address offset))) +(rule 3 (lower (has_type (is_xmm_type (ty_32 _)) (load _ (little_or_native_endian flags) address offset))) (x64_movss_load (to_amode flags address offset))) -(rule 2 (lower (has_type (is_xmm_type (ty_64 _)) (load (little_or_native_endian flags) address offset))) +(rule 2 (lower (has_type (is_xmm_type (ty_64 _)) (load _ (little_or_native_endian flags) address offset))) (x64_movsd_load (to_amode flags address offset))) -(rule 1 (lower (has_type $F32X4 (load (little_or_native_endian flags) address offset))) +(rule 1 (lower (has_type $F32X4 (load _ (little_or_native_endian flags) address offset))) (x64_movups_load (to_amode flags address offset))) -(rule 1 (lower (has_type $F64X2 (load (little_or_native_endian flags) address offset))) +(rule 1 (lower (has_type $F64X2 (load _ (little_or_native_endian flags) address offset))) (x64_movupd_load (to_amode flags address offset))) -(rule 0 (lower (has_type (is_xmm_type (ty_128 _)) (load (little_or_native_endian flags) address offset))) +(rule 0 (lower (has_type (is_xmm_type (ty_128 _)) (load _ (little_or_native_endian flags) address offset))) (x64_movdqu_load (to_amode flags address offset))) ;; We can load an I128 by doing two 64-bit loads. (rule -3 (lower (has_type $I128 - (load (little_or_native_endian flags) address offset))) + (load _ (little_or_native_endian flags) address offset))) (let ((addr_lo SyntheticAmode (to_amode flags address offset)) - (addr_hi SyntheticAmode (amode_offset addr_lo 8)) + (addr_hi SyntheticAmode (amode_offset addr_lo flags 8)) (value_lo Reg (x64_mov addr_lo)) (value_hi Reg (x64_mov addr_hi))) (value_regs value_lo value_hi))) ;; We also include widening vector loads; these sign- or zero-extend each lane ;; to the next wider width (e.g., 16x4 -> 32x4). -(rule 1 (lower (has_type $I16X8 (sload8x8 (little_or_native_endian flags) address offset))) +(rule 1 (lower (has_type $I16X8 (sload8x8 _ (little_or_native_endian flags) address offset))) (if-let true (has_sse41)) (x64_pmovsxbw (to_amode flags address offset))) -(rule 1 (lower (has_type $I16X8 (uload8x8 (little_or_native_endian flags) address offset))) +(rule 1 (lower (has_type $I16X8 (uload8x8 _ (little_or_native_endian flags) address offset))) (if-let true (has_sse41)) (x64_pmovzxbw (to_amode flags address offset))) -(rule 1 (lower (has_type $I32X4 (sload16x4 (little_or_native_endian flags) address offset))) +(rule 1 (lower (has_type $I32X4 (sload16x4 _ (little_or_native_endian flags) address offset))) (if-let true (has_sse41)) (x64_pmovsxwd (to_amode flags address offset))) -(rule 1 (lower (has_type $I32X4 (uload16x4 (little_or_native_endian flags) address offset))) +(rule 1 (lower (has_type $I32X4 (uload16x4 _ (little_or_native_endian flags) address offset))) (if-let true (has_sse41)) (x64_pmovzxwd (to_amode flags address offset))) -(rule 1 (lower (has_type $I64X2 (sload32x2 (little_or_native_endian flags) address offset))) +(rule 1 (lower (has_type $I64X2 (sload32x2 _ (little_or_native_endian flags) address offset))) (if-let true (has_sse41)) (x64_pmovsxdq (to_amode flags address offset))) -(rule 1 (lower (has_type $I64X2 (uload32x2 (little_or_native_endian flags) address offset))) +(rule 1 (lower (has_type $I64X2 (uload32x2 _ (little_or_native_endian flags) address offset))) (if-let true (has_sse41)) (x64_pmovzxdq (to_amode flags address offset))) -(rule (lower (has_type $I16X8 (sload8x8 (little_or_native_endian flags) address offset))) +(rule (lower (has_type $I16X8 (sload8x8 _ (little_or_native_endian flags) address offset))) (lower_swiden_low $I16X8 (x64_movq_to_xmm (to_amode flags address offset)))) -(rule (lower (has_type $I16X8 (uload8x8 (little_or_native_endian flags) address offset))) +(rule (lower (has_type $I16X8 (uload8x8 _ (little_or_native_endian flags) address offset))) (lower_uwiden_low $I16X8 (x64_movq_to_xmm (to_amode flags address offset)))) -(rule (lower (has_type $I32X4 (sload16x4 (little_or_native_endian flags) address offset))) +(rule (lower (has_type $I32X4 (sload16x4 _ (little_or_native_endian flags) address offset))) (lower_swiden_low $I32X4 (x64_movq_to_xmm (to_amode flags address offset)))) -(rule (lower (has_type $I32X4 (uload16x4 (little_or_native_endian flags) address offset))) +(rule (lower (has_type $I32X4 (uload16x4 _ (little_or_native_endian flags) address offset))) (lower_uwiden_low $I32X4 (x64_movq_to_xmm (to_amode flags address offset)))) -(rule (lower (has_type $I64X2 (sload32x2 (little_or_native_endian flags) address offset))) +(rule (lower (has_type $I64X2 (sload32x2 _ (little_or_native_endian flags) address offset))) (lower_swiden_low $I64X2 (x64_movq_to_xmm (to_amode flags address offset)))) -(rule (lower (has_type $I64X2 (uload32x2 (little_or_native_endian flags) address offset))) +(rule (lower (has_type $I64X2 (uload32x2 _ (little_or_native_endian flags) address offset))) (lower_uwiden_low $I64X2 (x64_movq_to_xmm (to_amode flags address offset)))) ;; Rules for `store*` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3225,7 +3225,7 @@ (value_lo Gpr (value_regs_get_gpr value_reg 0)) (value_hi Gpr (value_regs_get_gpr value_reg 1)) (addr_lo SyntheticAmode (to_amode flags address offset)) - (addr_hi SyntheticAmode (amode_offset addr_lo 8))) + (addr_hi SyntheticAmode (amode_offset addr_lo flags 8))) (side_effect (side_effect_concat (x64_movrm $I64 addr_lo value_lo) @@ -3237,40 +3237,40 @@ ;; f32 or f64 despite the source perhaps being an integer vector since the ;; result of the instruction is the same. (rule 2 (lower (store (little_or_native_endian flags) - (has_type $F32 (extractlane value (u8_from_uimm8 0))) + (has_type $F32 (extractlane _ value (u8_from_uimm8 0))) address offset)) (side_effect (x64_movss_store (to_amode flags address offset) value))) (rule 2 (lower (store (little_or_native_endian flags) - (has_type $F64 (extractlane value (u8_from_uimm8 0))) + (has_type $F64 (extractlane _ value (u8_from_uimm8 0))) address offset)) (side_effect (x64_movsd_store (to_amode flags address offset) value))) (rule 2 (lower (store (little_or_native_endian flags) - (has_type $I8 (extractlane value (u8_from_uimm8 n))) + (has_type $I8 (extractlane _ value (u8_from_uimm8 n))) address offset)) (if-let true (has_sse41)) (side_effect (x64_pextrb_store (to_amode flags address offset) value n))) (rule 2 (lower (store (little_or_native_endian flags) - (has_type $I16 (extractlane value (u8_from_uimm8 n))) + (has_type $I16 (extractlane _ value (u8_from_uimm8 n))) address offset)) (if-let true (has_sse41)) (side_effect (x64_pextrw_store (to_amode flags address offset) value n))) (rule 2 (lower (store (little_or_native_endian flags) - (has_type $I32 (extractlane value (u8_from_uimm8 n))) + (has_type $I32 (extractlane _ value (u8_from_uimm8 n))) address offset)) (if-let true (has_sse41)) (side_effect (x64_pextrd_store (to_amode flags address offset) value n))) (rule 2 (lower (store (little_or_native_endian flags) - (has_type $I64 (extractlane value (u8_from_uimm8 n))) + (has_type $I64 (extractlane _ value (u8_from_uimm8 n))) address offset)) (if-let true (has_sse41)) @@ -3283,9 +3283,9 @@ (rule store_x64_add_mem 3 (lower (store (little_or_native_endian flags) (has_type (ty_32_or_64 ty) - (iadd (and + (iadd _ (and (sinkable_load sink) - (load flags addr offset)) + (load _ flags addr offset)) src2)) addr offset)) @@ -3297,10 +3297,10 @@ (rule 2 (lower (store (little_or_native_endian flags) (has_type (ty_32_or_64 ty) - (iadd src2 + (iadd _ src2 (and (sinkable_load sink) - (load flags addr offset)))) + (load _ flags addr offset)))) addr offset)) (let ((_ RegMemImm sink)) @@ -3311,9 +3311,9 @@ (rule 2 (lower (store (little_or_native_endian flags) (has_type (ty_32_or_64 ty) - (isub (and + (isub _ (and (sinkable_load sink) - (load flags addr offset)) + (load _ flags addr offset)) src2)) addr offset)) @@ -3325,9 +3325,9 @@ (rule 3 (lower (store (little_or_native_endian flags) (has_type (ty_32_or_64 ty) - (band (and + (band _ (and (sinkable_load sink) - (load flags addr offset)) + (load _ flags addr offset)) src2)) addr offset)) @@ -3339,10 +3339,10 @@ (rule 2 (lower (store (little_or_native_endian flags) (has_type (ty_32_or_64 ty) - (band src2 + (band _ src2 (and (sinkable_load sink) - (load flags addr offset)))) + (load _ flags addr offset)))) addr offset)) (let ((_ RegMemImm sink)) @@ -3353,9 +3353,9 @@ (rule 3 (lower (store (little_or_native_endian flags) (has_type (ty_32_or_64 ty) - (bor (and + (bor _ (and (sinkable_load sink) - (load flags addr offset)) + (load _ flags addr offset)) src2)) addr offset)) @@ -3367,10 +3367,10 @@ (rule 2 (lower (store (little_or_native_endian flags) (has_type (ty_32_or_64 ty) - (bor src2 + (bor _ src2 (and (sinkable_load sink) - (load flags addr offset)))) + (load _ flags addr offset)))) addr offset)) (let ((_ RegMemImm sink)) @@ -3381,9 +3381,9 @@ (rule 3 (lower (store (little_or_native_endian flags) (has_type (ty_32_or_64 ty) - (bxor (and + (bxor _ (and (sinkable_load sink) - (load flags addr offset)) + (load _ flags addr offset)) src2)) addr offset)) @@ -3395,10 +3395,10 @@ (rule 2 (lower (store (little_or_native_endian flags) (has_type (ty_32_or_64 ty) - (bxor src2 + (bxor _ src2 (and (sinkable_load sink) - (load flags addr offset)))) + (load _ flags addr offset)))) addr offset)) (let ((_ RegMemImm sink)) @@ -3412,12 +3412,12 @@ ;; Rules for `func_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (func_addr (func_ref_data _ extname dist _))) +(rule (lower (func_addr _ (func_ref_data _ extname dist _))) (load_ext_name extname 0 dist)) ;; Rules for `symbol_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (symbol_value (symbol_value_data extname dist offset))) +(rule (lower (symbol_value _ (symbol_value_data extname dist offset))) (load_ext_name extname offset dist)) ;; Rules for `atomic_load` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3428,12 +3428,12 @@ ;; ;; This lowering is only valid for I8, I16, I32, and I64. The sub-64-bit types ;; are zero extended, as with a normal load. -(rule 1 (lower (has_type $I64 (atomic_load (little_or_native_endian flags) address))) +(rule 1 (lower (has_type $I64 (atomic_load _ (little_or_native_endian flags) address))) (x64_mov (to_amode flags address (zero_offset)))) -(rule (lower (has_type (and (fits_in_32 ty) (ty_int _)) (atomic_load (little_or_native_endian flags) address))) +(rule (lower (has_type (and (fits_in_32 ty) (ty_int _)) (atomic_load _ (little_or_native_endian flags) address))) (x64_movzx (ext_mode (ty_bits_u16 ty) 64) (to_amode flags address (zero_offset)))) ;; Lower 128-bit `atomic_load` using `cmpxchg16b`. -(rule 1 (lower (has_type $I128 (atomic_load (little_or_native_endian flags) address))) +(rule 1 (lower (has_type $I128 (atomic_load _ (little_or_native_endian flags) address))) (if-let true (has_cmpxchg16b)) (x64_cmpxchg16b (value_regs (imm $I64 0) (imm $I64 0)) (value_regs (imm $I64 0) (imm $I64 0)) (to_amode flags address (zero_offset)))) @@ -3450,14 +3450,14 @@ ;; Lower 128-bit `atomic_store` using `cmpxchg16b`. (rule 1 (lower (atomic_store (little_or_native_endian flags) value @ (value_type $I128) address)) (if-let true (has_cmpxchg16b)) - (side_effect (x64_atomic_128_store_seq (to_amode flags address (zero_offset)) value))) + (side_effect (x64_atomic_128_store_seq (to_amode flags address (zero_offset)) flags value))) ;; Rules for `atomic_cas` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type (and (fits_in_64 ty) (ty_int _)) - (atomic_cas (little_or_native_endian flags) address expected replacement))) + (atomic_cas _ (little_or_native_endian flags) address expected replacement))) (x64_cmpxchg ty expected replacement (to_amode flags address (zero_offset)))) -(rule 1 (lower (has_type $I128 (atomic_cas flags address expected replacement))) +(rule 1 (lower (has_type $I128 (atomic_cas _ flags address expected replacement))) (if-let true (has_cmpxchg16b)) (x64_cmpxchg16b expected replacement (to_amode flags address (zero_offset)))) @@ -3466,53 +3466,53 @@ ;; This is a simple, general-case atomic update, based on a loop involving ;; `cmpxchg`. (rule (lower (has_type (and (fits_in_64 ty) (ty_int _)) - (atomic_rmw (little_or_native_endian flags) op address input))) + (atomic_rmw _ (little_or_native_endian flags) op address input))) (x64_atomic_rmw_seq ty (atomic_rmw_seq_op op) (to_amode flags address (zero_offset)) input)) ;; `Add` and `Sub` can use `lock xadd` (rule 1 (lower (has_type (and (fits_in_64 ty) (ty_int _)) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Add) address input))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Add) address input))) (x64_xadd ty (to_amode flags address (zero_offset)) input)) (rule 1 (lower (has_type (and (fits_in_64 ty) (ty_int _)) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Sub) address input))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Sub) address input))) (x64_xadd ty (to_amode flags address (zero_offset)) (x64_neg ty input))) ;; `Xchg` can use `xchg` (rule 1 (lower (has_type (and (fits_in_64 ty) (ty_int _)) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Xchg) address input))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Xchg) address input))) (x64_xchg ty (to_amode flags address (zero_offset)) input)) ;; `Add`, `Sub`, `And`, `Or` and `Xor` can use `lock`-prefixed instructions if ;; the old value is not required. (rule 2 (lower i @ (has_type (fits_in_64 (ty_int ty)) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Add) address input))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Add) address input))) (if-let (first_result res) i) (if-let true (value_is_unused res)) (side_effect_as_invalid (x64_lock_add (raw_operand_size_of_type ty) (to_amode flags address (zero_offset)) input))) (rule 2 (lower i @ (has_type (fits_in_64 (ty_int ty)) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Sub) address input))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Sub) address input))) (if-let (first_result res) i) (if-let true (value_is_unused res)) (side_effect_as_invalid (x64_lock_sub (raw_operand_size_of_type ty) (to_amode flags address (zero_offset)) input))) (rule 2 (lower i @ (has_type (fits_in_64 (ty_int ty)) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.And) address input))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.And) address input))) (if-let (first_result res) i) (if-let true (value_is_unused res)) (side_effect_as_invalid (x64_lock_and (raw_operand_size_of_type ty) (to_amode flags address (zero_offset)) input))) (rule 2 (lower i @ (has_type (fits_in_64 (ty_int ty)) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Or) address input))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Or) address input))) (if-let (first_result res) i) (if-let true (value_is_unused res)) (side_effect_as_invalid (x64_lock_or (raw_operand_size_of_type ty) (to_amode flags address (zero_offset)) input))) (rule 2 (lower i @ (has_type (fits_in_64 (ty_int ty)) - (atomic_rmw (little_or_native_endian flags) (AtomicRmwOp.Xor) address input))) + (atomic_rmw _ (little_or_native_endian flags) (AtomicRmwOp.Xor) address input))) (if-let (first_result res) i) (if-let true (value_is_unused res)) (side_effect_as_invalid (x64_lock_xor (raw_operand_size_of_type ty) (to_amode flags address (zero_offset)) input))) ;; 128-bit integers always use a `lock cmpxchg16b` loop. -(rule 3 (lower (has_type $I128 (atomic_rmw (little_or_native_endian flags) op address input))) +(rule 3 (lower (has_type $I128 (atomic_rmw _ (little_or_native_endian flags) op address input))) (if-let true (has_cmpxchg16b)) - (x64_atomic_128_rmw_seq op (to_amode flags address (zero_offset)) input)) + (x64_atomic_128_rmw_seq op (to_amode flags address (zero_offset)) flags input)) ;; Rules for `call` and `call_indirect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3608,7 +3608,7 @@ ;; Rules for `stack_switch` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; currently, only the Basic model is supported -(rule (lower (stack_switch store_context_ptr load_context_ptr in_payload0)) +(rule (lower (stack_switch _ store_context_ptr load_context_ptr in_payload0)) (if-let (StackSwitchModel.Basic) (stack_switch_model)) (let ((store_context_ptr Gpr (put_in_gpr store_context_ptr)) (load_context_ptr Gpr (put_in_gpr load_context_ptr)) @@ -3617,16 +3617,14 @@ ;;;; Rules for `get_{frame,stack}_pointer` and `get_return_address` ;;;;;;;;;;;; -(rule (lower (get_frame_pointer)) +(rule (lower (get_frame_pointer _)) (x64_rbp)) -(rule (lower (get_stack_pointer)) +(rule (lower (get_stack_pointer _)) (x64_rsp)) -(rule (lower (get_return_address)) - (x64_load $I64 - (Amode.ImmReg 8 (x64_rbp) (mem_flags_trusted)) - (ExtKind.None))) +(rule (lower (get_return_address _)) + (x64_movq_rm (Amode.ImmReg 8 (x64_rbp) (mem_flags_trusted)))) ;; Rules for `jump` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3651,13 +3649,13 @@ ;; Rules for `select_spectre_guard` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (select_spectre_guard cond x y)) +(rule (lower (select_spectre_guard _ cond x y)) (lower_select (is_nonzero_cmp cond) x y)) ;; Note that for GPR-based spectre guards everything is forced into a register ;; not `GprMem`. The `lower_select_spectre_gpr` helper below handles "and" ;; conditions which the `lower_select_gpr` helper does not. -(rule 1 (lower (has_type (is_single_register_gpr_type ty) (select_spectre_guard cond x y))) +(rule 1 (lower (has_type (is_single_register_gpr_type ty) (select_spectre_guard _ cond x y))) (lower_select_spectre_gpr ty (is_nonzero_cmp cond) (put_in_gpr x) y)) (decl lower_select_spectre_gpr (Type CondResult Gpr Gpr) Gpr) @@ -3684,31 +3682,31 @@ ;; `fcvt_from_sint` to an x86-specific opcode using a zero constant which would ;; be subject to normal LICM, but that's not feasible today. -(rule 2 (lower (has_type $F32 (fcvt_from_sint a @ (value_type $I8)))) +(rule 2 (lower (has_type $F32 (fcvt_from_sint _ a @ (value_type $I8)))) (x64_cvtsi2ss $I32 (xmm_zero $F32X4) (extend_to_gpr a $I32 (ExtendKind.Sign)))) -(rule 2 (lower (has_type $F32 (fcvt_from_sint a @ (value_type $I16)))) +(rule 2 (lower (has_type $F32 (fcvt_from_sint _ a @ (value_type $I16)))) (x64_cvtsi2ss $I32 (xmm_zero $F32X4) (extend_to_gpr a $I32 (ExtendKind.Sign)))) -(rule 1 (lower (has_type $F32 (fcvt_from_sint a @ (value_type (ty_int (fits_in_64 ty)))))) +(rule 1 (lower (has_type $F32 (fcvt_from_sint _ a @ (value_type (ty_int (fits_in_64 ty)))))) (x64_cvtsi2ss ty (xmm_zero $F32X4) a)) -(rule 2 (lower (has_type $F64 (fcvt_from_sint a @ (value_type $I8)))) +(rule 2 (lower (has_type $F64 (fcvt_from_sint _ a @ (value_type $I8)))) (x64_cvtsi2sd $I32 (xmm_zero $F64X2) (extend_to_gpr a $I32 (ExtendKind.Sign)))) -(rule 2 (lower (has_type $F64 (fcvt_from_sint a @ (value_type $I16)))) +(rule 2 (lower (has_type $F64 (fcvt_from_sint _ a @ (value_type $I16)))) (x64_cvtsi2sd $I32 (xmm_zero $F64X2) (extend_to_gpr a $I32 (ExtendKind.Sign)))) -(rule 1 (lower (has_type $F64 (fcvt_from_sint a @ (value_type (ty_int (fits_in_64 ty)))))) +(rule 1 (lower (has_type $F64 (fcvt_from_sint _ a @ (value_type (ty_int (fits_in_64 ty)))))) (x64_cvtsi2sd ty (xmm_zero $F64X2) a)) -(rule 0 (lower (fcvt_from_sint a @ (value_type $I32X4))) +(rule 0 (lower (fcvt_from_sint _ a @ (value_type $I32X4))) (x64_cvtdq2ps a)) ;; Base case: decompose the i64x2 input into two scalar registers and convert ;; each of those into a float. Afterwards re-pack the two results into the final ;; destination. -(rule 0 (lower (fcvt_from_sint a @ (value_type $I64X2))) +(rule 0 (lower (fcvt_from_sint _ a @ (value_type $I64X2))) (let ( (a Xmm a) (zero Xmm (xmm_zero $F64X2)) @@ -3717,18 +3715,18 @@ ) (x64_unpcklpd f0 f1))) -(rule 1 (lower (has_type $F64X2 (fcvt_from_sint (swiden_low a @ (value_type $I32X4))))) +(rule 1 (lower (has_type $F64X2 (fcvt_from_sint _ (swiden_low _ a @ (value_type $I32X4))))) (x64_cvtdq2pd a)) ;; Rules for `fcvt_from_uint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (has_type $F32 (fcvt_from_uint val @ (value_type (fits_in_32 (ty_int ty)))))) +(rule 1 (lower (has_type $F32 (fcvt_from_uint _ val @ (value_type (fits_in_32 (ty_int ty)))))) (x64_cvtsi2ss $I64 (xmm_zero $F32X4) (extend_to_gpr val $I64 (ExtendKind.Zero)))) -(rule 1 (lower (has_type $F64 (fcvt_from_uint val @ (value_type (fits_in_32 (ty_int ty)))))) +(rule 1 (lower (has_type $F64 (fcvt_from_uint _ val @ (value_type (fits_in_32 (ty_int ty)))))) (x64_cvtsi2sd $I64 (xmm_zero $F64X2) (extend_to_gpr val $I64 (ExtendKind.Zero)))) -(rule (lower (has_type ty (fcvt_from_uint val @ (value_type $I64)))) +(rule (lower (has_type ty (fcvt_from_uint _ val @ (value_type $I64)))) (cvt_u64_to_float_seq ty val)) ;; Base case of u64x2 being converted to f64x2. No native instruction for this @@ -3759,7 +3757,7 @@ ;; number. The final step in combining via float arithmetic will chop off the ;; leading `1.` at the start of the float that we constructed, one for the low ;; half and one for the upper half. -(rule -1 (lower (has_type $F64X2 (fcvt_from_uint val @ (value_type $I64X2)))) +(rule -1 (lower (has_type $F64X2 (fcvt_from_uint _ val @ (value_type $I64X2)))) (let ((low32_mask XmmMem (emit_u128_le_const 0x00000000ffffffff_00000000ffffffff)) (float_1p52 XmmMem (emit_u128_le_const 0x4330000000000000_4330000000000000)) (float_1p84 XmmMem (emit_u128_le_const 0x4530000000000000_4530000000000000)) @@ -3774,7 +3772,7 @@ ;; 0x1.0p52 + double(src). 0x1.0p52 is unique because at this exponent ;; every value of the mantissa represents a corresponding uint32 number. ;; When we subtract 0x1.0p52 we are left with double(src). -(rule 1 (lower (has_type $F64X2 (fcvt_from_uint (uwiden_low val @ (value_type $I32X4))))) +(rule 1 (lower (has_type $F64X2 (fcvt_from_uint _ (uwiden_low _ val @ (value_type $I32X4))))) (let ((uint_mask XmmMem (emit_u128_le_const 0x43300000_43300000)) (res Xmm (x64_unpcklps val uint_mask)) (uint_mask_high XmmMem (emit_u128_le_const 0x4330000000000000_4330000000000000))) @@ -3782,7 +3780,7 @@ ;; When AVX512VL and AVX512F are available, ;; `fcvt_from_uint` can be lowered to a single instruction. -(rule 2 (lower (has_type $F32X4 (fcvt_from_uint src))) +(rule 2 (lower (has_type $F32X4 (fcvt_from_uint _ src))) (if-let true (has_avx512vl)) (if-let true (has_avx512f)) (x64_vcvtudq2ps src)) @@ -3810,7 +3808,7 @@ ;; -> Convert(Ah) // Convert .. with no loss of significant digits from previous shift ;; -> Ah = Ah + Ah // Double Ah to account for shift right before the conversion. ;; -> dst = Ah + Al // Add the two floats together -(rule 1 (lower (has_type $F32X4 (fcvt_from_uint val))) +(rule 1 (lower (has_type $F32X4 (fcvt_from_uint _ val))) (let ((a Xmm val) ;; get the low 16 bits @@ -3834,20 +3832,20 @@ ;; Rules for `fcvt_to_uint` and `fcvt_to_sint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type out_ty (fcvt_to_uint val @ (value_type (ty_scalar_float _))))) +(rule (lower (has_type out_ty (fcvt_to_uint _ val @ (value_type (ty_scalar_float _))))) (cvt_float_to_uint_seq out_ty val false)) -(rule (lower (has_type out_ty (fcvt_to_uint_sat val @ (value_type (ty_scalar_float _))))) +(rule (lower (has_type out_ty (fcvt_to_uint_sat _ val @ (value_type (ty_scalar_float _))))) (cvt_float_to_uint_seq out_ty val true)) -(rule (lower (has_type out_ty (fcvt_to_sint val @ (value_type (ty_scalar_float _))))) +(rule (lower (has_type out_ty (fcvt_to_sint _ val @ (value_type (ty_scalar_float _))))) (cvt_float_to_sint_seq out_ty val false)) -(rule (lower (has_type out_ty (fcvt_to_sint_sat val @ (value_type (ty_scalar_float _))))) +(rule (lower (has_type out_ty (fcvt_to_sint_sat _ val @ (value_type (ty_scalar_float _))))) (cvt_float_to_sint_seq out_ty val true)) ;; The x64 backend currently only supports these two type combinations. -(rule 1 (lower (has_type $I32X4 (fcvt_to_sint_sat val @ (value_type $F32X4)))) +(rule 1 (lower (has_type $I32X4 (fcvt_to_sint_sat _ val @ (value_type $F32X4)))) (let ((src Xmm val) ;; Sets tmp to zero if float is NaN @@ -3918,7 +3916,7 @@ ;; ;; | Step 6 | Step 7 | ;; | (0-(INT_MAX+1))..(UINT_MAX-(INT_MAX+1))(w/overflow) | ((INT_MAX+1)-(INT_MAX+1))..(INT_MAX+1) | -(rule 1 (lower (has_type $I32X4 (fcvt_to_uint_sat val @ (value_type $F32X4)))) +(rule 1 (lower (has_type $I32X4 (fcvt_to_uint_sat _ val @ (value_type $F32X4)))) (let ((src Xmm val) ;; Converting to unsigned int so if float src is negative or NaN @@ -3965,12 +3963,12 @@ ;; Rules for `x86_cvtt2dq` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I32X4 (x86_cvtt2dq val @ (value_type $F32X4)))) +(rule (lower (has_type $I32X4 (x86_cvtt2dq _ val @ (value_type $F32X4)))) (x64_cvttps2dq val)) ;; Rules for `iadd_pairwise` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8X16 (iadd_pairwise x y))) +(rule (lower (has_type $I8X16 (iadd_pairwise _ x y))) (let ( ;; Shuffle all the even lanes of `x` and `y` into one register (even_lane_mask Xmm (x64_movdqu_load (emit_u128_le_const 0x00ff_00ff_00ff_00ff_00ff_00ff_00ff_00ff))) @@ -3986,11 +3984,11 @@ (x64_paddb evens odds))) -(rule 1 (lower (has_type $I16X8 (iadd_pairwise x y))) +(rule 1 (lower (has_type $I16X8 (iadd_pairwise _ x y))) (if-let true (has_ssse3)) (x64_phaddw x y)) -(rule (lower (has_type $I16X8 (iadd_pairwise x y))) +(rule (lower (has_type $I16X8 (iadd_pairwise _ x y))) (let ( (x Xmm x) (y Xmm y) @@ -4016,11 +4014,11 @@ ) (x64_paddw evens odds))) -(rule 1 (lower (has_type $I32X4 (iadd_pairwise x y))) +(rule 1 (lower (has_type $I32X4 (iadd_pairwise _ x y))) (if-let true (has_ssse3)) (x64_phaddd x y)) -(rule (lower (has_type $I32X4 (iadd_pairwise x y))) +(rule (lower (has_type $I32X4 (iadd_pairwise _ x y))) (let ( (x Xmm x) (y Xmm y) @@ -4033,9 +4031,9 @@ ;; special case for the `i16x8.extadd_pairwise_i8x16_s` wasm instruction (rule 2 (lower - (has_type $I16X8 (iadd_pairwise - (swiden_low val @ (value_type $I8X16)) - (swiden_high val)))) + (has_type $I16X8 (iadd_pairwise _ + (swiden_low _ val @ (value_type $I8X16)) + (swiden_high _ val)))) (if-let true (has_ssse3)) (let ((mul_const Xmm (x64_xmm_load_const $I8X16 (emit_u128_le_const 0x01010101010101010101010101010101)))) @@ -4043,26 +4041,26 @@ ;; special case for the `i32x4.extadd_pairwise_i16x8_s` wasm instruction (rule 2 (lower - (has_type $I32X4 (iadd_pairwise - (swiden_low val @ (value_type $I16X8)) - (swiden_high val)))) + (has_type $I32X4 (iadd_pairwise _ + (swiden_low _ val @ (value_type $I16X8)) + (swiden_high _ val)))) (let ((mul_const XmmMem (emit_u128_le_const 0x0001_0001_0001_0001_0001_0001_0001_0001))) (x64_pmaddwd val mul_const))) ;; special case for the `i16x8.extadd_pairwise_i8x16_u` wasm instruction (rule 2 (lower - (has_type $I16X8 (iadd_pairwise - (uwiden_low val @ (value_type $I8X16)) - (uwiden_high val)))) + (has_type $I16X8 (iadd_pairwise _ + (uwiden_low _ val @ (value_type $I8X16)) + (uwiden_high _ val)))) (if-let true (has_ssse3)) (let ((mul_const XmmMem (emit_u128_le_const 0x01010101010101010101010101010101))) (x64_pmaddubsw val mul_const))) ;; special case for the `i32x4.extadd_pairwise_i16x8_u` wasm instruction (rule 2 (lower - (has_type $I32X4 (iadd_pairwise - (uwiden_low val @ (value_type $I16X8)) - (uwiden_high val)))) + (has_type $I32X4 (iadd_pairwise _ + (uwiden_low _ val @ (value_type $I16X8)) + (uwiden_high _ val)))) (let ((xor_const XmmMem (emit_u128_le_const 0x8000_8000_8000_8000_8000_8000_8000_8000)) (dst Xmm (x64_pxor val xor_const)) @@ -4074,25 +4072,25 @@ ;; special case for the `i32x4.dot_i16x8_s` wasm instruction (rule 2 (lower - (has_type $I32X4 (iadd_pairwise - (imul (swiden_low x) (swiden_low y)) - (imul (swiden_high x) (swiden_high y))))) + (has_type $I32X4 (iadd_pairwise _ + (imul _ (swiden_low _ x) (swiden_low _ y)) + (imul _ (swiden_high _ x) (swiden_high _ y))))) (x64_pmaddwd x y)) ;; Rules for `swiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; With SSE4.1 use the `pmovsx*` instructions for this -(rule 1 (lower (has_type $I16X8 (swiden_low val @ (value_type $I8X16)))) +(rule 1 (lower (has_type $I16X8 (swiden_low _ val @ (value_type $I8X16)))) (if-let true (has_sse41)) (x64_pmovsxbw val)) -(rule 1 (lower (has_type $I32X4 (swiden_low val @ (value_type $I16X8)))) +(rule 1 (lower (has_type $I32X4 (swiden_low _ val @ (value_type $I16X8)))) (if-let true (has_sse41)) (x64_pmovsxwd val)) -(rule 1 (lower (has_type $I64X2 (swiden_low val @ (value_type $I32X4)))) +(rule 1 (lower (has_type $I64X2 (swiden_low _ val @ (value_type $I32X4)))) (if-let true (has_sse41)) (x64_pmovsxdq val)) -(rule (lower (has_type ty (swiden_low val))) (lower_swiden_low ty val)) +(rule (lower (has_type ty (swiden_low _ val))) (lower_swiden_low ty val)) (decl lower_swiden_low (Type Xmm) Xmm) @@ -4115,31 +4113,31 @@ ;; Similar to `swiden_low` with SSE4.1 except that the upper lanes are moved ;; to the lower lanes first. -(rule 1 (lower (has_type $I16X8 (swiden_high val @ (value_type $I8X16)))) +(rule 1 (lower (has_type $I16X8 (swiden_high _ val @ (value_type $I8X16)))) (if-let true (has_sse41)) (if-let true (has_ssse3)) (let ((x Xmm val)) (x64_pmovsxbw (x64_palignr x x 8)))) -(rule 1 (lower (has_type $I32X4 (swiden_high val @ (value_type $I16X8)))) +(rule 1 (lower (has_type $I32X4 (swiden_high _ val @ (value_type $I16X8)))) (if-let true (has_sse41)) (if-let true (has_ssse3)) (let ((x Xmm val)) (x64_pmovsxwd (x64_palignr x x 8)))) -(rule 1 (lower (has_type $I64X2 (swiden_high val @ (value_type $I32X4)))) +(rule 1 (lower (has_type $I64X2 (swiden_high _ val @ (value_type $I32X4)))) (if-let true (has_sse41)) (x64_pmovsxdq (x64_pshufd val 0b11_10_11_10))) ;; Similar to `swiden_low` versions but using `punpckh*` instructions to ;; pair the high lanes next to each other. -(rule (lower (has_type $I16X8 (swiden_high val @ (value_type $I8X16)))) +(rule (lower (has_type $I16X8 (swiden_high _ val @ (value_type $I8X16)))) (let ((val Xmm val)) (x64_psraw (x64_punpckhbw val val) (xmi_imm 8)))) -(rule (lower (has_type $I32X4 (swiden_high val @ (value_type $I16X8)))) +(rule (lower (has_type $I32X4 (swiden_high _ val @ (value_type $I16X8)))) (let ((val Xmm val)) (x64_psrad (x64_punpckhwd val val) (xmi_imm 16)))) ;; Same as `swiden_low`, but `val` has its high lanes moved down. -(rule (lower (has_type $I64X2 (swiden_high val @ (value_type $I32X4)))) +(rule (lower (has_type $I64X2 (swiden_high _ val @ (value_type $I32X4)))) (let ((val Xmm (x64_pshufd val 0b00_00_11_10)) (tmp Xmm (x64_pcmpgtd_a (xmm_zero $I32X4) val))) (x64_punpckldq val tmp))) @@ -4147,17 +4145,17 @@ ;; Rules for `uwiden_low` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; With SSE4.1 use the `pmovzx*` instructions for this -(rule 1 (lower (has_type $I16X8 (uwiden_low val @ (value_type $I8X16)))) +(rule 1 (lower (has_type $I16X8 (uwiden_low _ val @ (value_type $I8X16)))) (if-let true (has_sse41)) (x64_pmovzxbw val)) -(rule 1 (lower (has_type $I32X4 (uwiden_low val @ (value_type $I16X8)))) +(rule 1 (lower (has_type $I32X4 (uwiden_low _ val @ (value_type $I16X8)))) (if-let true (has_sse41)) (x64_pmovzxwd val)) -(rule 1 (lower (has_type $I64X2 (uwiden_low val @ (value_type $I32X4)))) +(rule 1 (lower (has_type $I64X2 (uwiden_low _ val @ (value_type $I32X4)))) (if-let true (has_sse41)) (x64_pmovzxdq val)) -(rule (lower (has_type ty (uwiden_low val))) (lower_uwiden_low ty val)) +(rule (lower (has_type ty (uwiden_low _ val))) (lower_uwiden_low ty val)) ;; Interleave an all-zero register with the low lanes to produce zero-extended ;; results. @@ -4172,19 +4170,19 @@ ;; ;; Note that according to `llvm-mca` at least these instructions are faster ;; than using `pmovzx*` in terms of cycles, even if SSE4.1 is available. -(rule (lower (has_type $I16X8 (uwiden_high val @ (value_type $I8X16)))) +(rule (lower (has_type $I16X8 (uwiden_high _ val @ (value_type $I8X16)))) (x64_punpckhbw val (xmm_zero $I8X16))) -(rule (lower (has_type $I32X4 (uwiden_high val @ (value_type $I16X8)))) +(rule (lower (has_type $I32X4 (uwiden_high _ val @ (value_type $I16X8)))) (x64_punpckhwd val (xmm_zero $I8X16))) -(rule (lower (has_type $I64X2 (uwiden_high val @ (value_type $I32X4)))) +(rule (lower (has_type $I64X2 (uwiden_high _ val @ (value_type $I32X4)))) (x64_unpckhps val (xmm_zero $F32X4))) ;; Rules for `snarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8X16 (snarrow a @ (value_type $I16X8) b))) +(rule (lower (has_type $I8X16 (snarrow _ a @ (value_type $I16X8) b))) (x64_packsswb a b)) -(rule (lower (has_type $I16X8 (snarrow a @ (value_type $I32X4) b))) +(rule (lower (has_type $I16X8 (snarrow _ a @ (value_type $I32X4) b))) (x64_packssdw a b)) ;; We're missing a `snarrow` case for $I64X2 @@ -4193,8 +4191,8 @@ ;; This rule is a special case for handling the translation of the wasm op ;; `i32x4.trunc_sat_f64x2_s_zero`. It can be removed once we have an ;; implementation of `snarrow` for `I64X2`. -(rule (lower (has_type $I32X4 (snarrow (has_type $I64X2 (fcvt_to_sint_sat val)) - (vconst (u128_from_constant 0))))) +(rule (lower (has_type $I32X4 (snarrow _ (has_type $I64X2 (fcvt_to_sint_sat _ val)) + (vconst _ (u128_from_constant 0))))) (let ((a Xmm val) ;; y = i32x4.trunc_sat_f64x2_s_zero(x) is lowered to: @@ -4217,16 +4215,16 @@ ;; This rule is a special case for handling the translation of the wasm op ;; `i32x4.relaxed_trunc_f64x2_s_zero`. -(rule (lower (has_type $I32X4 (snarrow (has_type $I64X2 (x86_cvtt2dq val)) - (vconst (u128_from_constant 0))))) +(rule (lower (has_type $I32X4 (snarrow _ (has_type $I64X2 (x86_cvtt2dq _ val)) + (vconst _ (u128_from_constant 0))))) (x64_cvttpd2dq val)) ;; Rules for `unarrow` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $I8X16 (unarrow a @ (value_type $I16X8) b))) +(rule (lower (has_type $I8X16 (unarrow _ a @ (value_type $I16X8) b))) (x64_packuswb a b)) -(rule 1 (lower (has_type $I16X8 (unarrow a @ (value_type $I32X4) b))) +(rule 1 (lower (has_type $I16X8 (unarrow _ a @ (value_type $I32X4) b))) (if-let true (has_sse41)) (x64_packusdw a b)) @@ -4236,7 +4234,7 @@ ;; ;; If this is performance sensitive then it's probably best to upgrade the CPU ;; to get the above single-instruction lowering. -(rule (lower (has_type $I16X8 (unarrow a @ (value_type $I32X4) b))) +(rule (lower (has_type $I16X8 (unarrow _ a @ (value_type $I32X4) b))) (let ( (a Xmm (unarrow_i32x4_lanes_to_low_u16_lanes a)) (b Xmm (unarrow_i32x4_lanes_to_low_u16_lanes b)) @@ -4273,31 +4271,31 @@ ;; Rules for `bitcast` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule -3 (lower (has_type (is_gpr_type (fits_in_64 ty)) (bitcast _ src @ (value_type (is_xmm_type _))))) +(rule -3 (lower (has_type (is_gpr_type (fits_in_64 ty)) (bitcast _ _ src @ (value_type (is_xmm_type _))))) (bitcast_xmm_to_gpr (ty_bits ty) src)) -(rule -2 (lower (has_type (is_xmm_type (fits_in_64 ty)) (bitcast _ src @ (value_type (is_gpr_type _))))) +(rule -2 (lower (has_type (is_xmm_type (fits_in_64 ty)) (bitcast _ _ src @ (value_type (is_gpr_type _))))) (bitcast_gpr_to_xmm (ty_bits ty) src)) -(rule -1 (lower (has_type $I128 (bitcast _ src @ (value_type (is_xmm_type _))))) +(rule -1 (lower (has_type $I128 (bitcast _ _ src @ (value_type (is_xmm_type _))))) (bitcast_xmm_to_gprs src)) -(rule 0 (lower (has_type (is_xmm_type _) (bitcast _ src @ (value_type $I128)))) +(rule 0 (lower (has_type (is_xmm_type _) (bitcast _ _ src @ (value_type $I128)))) (bitcast_gprs_to_xmm src)) ;; Bitcast between types residing in GPR registers is a no-op. (rule 1 (lower (has_type (is_gpr_type _) - (bitcast _ x @ (value_type (is_gpr_type _))))) + (bitcast _ _ x @ (value_type (is_gpr_type _))))) x) ;; Bitcast between types residing in XMM registers is a no-op. (rule 3 (lower (has_type (is_xmm_type _) - (bitcast _ x @ (value_type (is_xmm_type _))))) + (bitcast _ _ x @ (value_type (is_xmm_type _))))) x) ;; Rules for `fcopysign` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type $F32 (fcopysign a @ (value_type $F32) b))) +(rule (lower (has_type $F32 (fcopysign _ a @ (value_type $F32) b))) (let ((sign_bit Xmm (imm $F32 0x80000000)) (a Xmm a) ;; force into reg so we don't sink a 128-bit load. (b Xmm b)) @@ -4305,7 +4303,7 @@ (x64_andnps sign_bit a) (x64_andps sign_bit b)))) -(rule (lower (has_type $F64 (fcopysign a @ (value_type $F64) b))) +(rule (lower (has_type $F64 (fcopysign _ a @ (value_type $F64) b))) (let ((sign_bit Xmm (imm $F64 0x8000000000000000)) (a Xmm a) ;; force into reg so we don't sink a 128-bit load. (b Xmm b)) @@ -4355,7 +4353,7 @@ ) (x64_movlhps result a1))) (rule (x64_round ty (RegMem.Mem addr) imm) - (x64_round ty (RegMem.Reg (x64_load ty addr (ExtKind.ZeroExtend))) imm)) + (x64_round ty (RegMem.Reg (x64_load_xmm ty addr)) imm)) (decl round_libcall (Type RoundImm) LibCall) (rule (round_libcall $F32 (RoundImm.RoundUp)) (LibCall.CeilF32)) @@ -4369,27 +4367,27 @@ ;; Rules for `ceil` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (ceil a @ (value_type ty))) +(rule (lower (ceil _ a @ (value_type ty))) (x64_round ty a (RoundImm.RoundUp))) ;; Rules for `floor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (floor a @ (value_type ty))) +(rule (lower (floor _ a @ (value_type ty))) (x64_round ty a (RoundImm.RoundDown))) ;; Rules for `nearest` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (nearest a @ (value_type ty))) +(rule (lower (nearest _ a @ (value_type ty))) (x64_round ty a (RoundImm.RoundNearest))) ;; Rules for `trunc` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (trunc a @ (value_type ty))) +(rule (lower (trunc _ a @ (value_type ty))) (x64_round ty a (RoundImm.RoundZero))) ;; Rules for `stack_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (stack_addr stack_slot offset)) +(rule (lower (stack_addr _ stack_slot offset)) (stack_addr_impl stack_slot offset)) ;; Rules for `udiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -4403,7 +4401,7 @@ ;; The inputs to the `div` instruction are different for 8-bit division so ;; it needs a special case here since the instruction being crafted has a ;; different shape. -(rule 2 (lower (udiv a @ (value_type $I8) b)) +(rule 2 (lower (udiv _ a @ (value_type $I8) b)) (x64_divb_m (extend_to_gpr a $I32 (ExtendKind.Zero)) (put_in_gpr b) (TrapCode.INTEGER_DIVISION_BY_ZERO))) @@ -4411,19 +4409,19 @@ ;; 16-to-64-bit division is all done with a similar instruction and the only ;; tricky requirement here is that when div traps are disallowed the divisor ;; must not be zero. -(rule 1 (lower (udiv a @ (value_type (fits_in_64 ty)) b)) +(rule 1 (lower (udiv _ a @ (value_type (fits_in_64 ty)) b)) (value_regs_get (x64_div ty a (imm $I64 0) (put_in_gpr b) (TrapCode.INTEGER_DIVISION_BY_ZERO)) 0)) ;; Rules for `sdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 2 (lower (sdiv a @ (value_type $I8) b)) +(rule 2 (lower (sdiv _ a @ (value_type $I8) b)) (x64_idivb_m (x64_cbtw_zo a) (nonzero_sdiv_divisor $I8 b) (TrapCode.INTEGER_OVERFLOW))) -(rule 1 (lower (sdiv a @ (value_type (fits_in_64 ty)) b)) +(rule 1 (lower (sdiv _ a @ (value_type (fits_in_64 ty)) b)) (let ((a Gpr a)) (value_regs_get (x64_idiv ty a (repeat_sign_bit ty a) (nonzero_sdiv_divisor ty b) (TrapCode.INTEGER_OVERFLOW)) @@ -4445,7 +4443,7 @@ ;; This is required to differentiate the divide-by-zero trap from the ;; integer-overflow trap, the two trapping conditions of signed division. (decl nonzero_sdiv_divisor (Type Value) Reg) -(rule 1 (nonzero_sdiv_divisor ty (iconst imm)) +(rule 1 (nonzero_sdiv_divisor ty (iconst _ imm)) (if-let n (safe_divisor_from_imm64 ty imm)) (imm ty n)) (rule 0 (nonzero_sdiv_divisor ty val) @@ -4461,7 +4459,7 @@ ;; The remainder is in AH, so take the result of the division and right-shift ;; by 8. -(rule 2 (lower (urem a @ (value_type $I8) b)) +(rule 2 (lower (urem _ a @ (value_type $I8) b)) (let ( (result Gpr (x64_divb_m (extend_to_gpr a $I32 (ExtendKind.Zero)) (put_in_gpr b) ;; see `udiv` for why not `gpr_mem` @@ -4469,7 +4467,7 @@ ) (x64_shrq_mi result 8))) -(rule 1 (lower (urem a @ (value_type (fits_in_64 ty)) b)) +(rule 1 (lower (urem _ a @ (value_type (fits_in_64 ty)) b)) (value_regs_get (x64_div ty a (imm $I64 0) (put_in_gpr b) (TrapCode.INTEGER_DIVISION_BY_ZERO)) 1)) @@ -4481,7 +4479,7 @@ ;; ;; Note that like `urem` for i8 types the result is in AH so to get the result ;; it's right-shifted down. -(rule 3 (lower (srem a @ (value_type $I8) (iconst imm))) +(rule 3 (lower (srem _ a @ (value_type $I8) (iconst _ imm))) (if-let n (safe_divisor_from_imm64 $I8 imm)) (let ( (a Gpr (x64_cbtw_zo a)) @@ -4490,7 +4488,7 @@ (x64_shrq_mi result 8))) ;; Same as the above rule but for 16-to-64 bit types. -(rule 2 (lower (srem a @ (value_type ty) (iconst imm))) +(rule 2 (lower (srem _ a @ (value_type ty) (iconst _ imm))) (if-let n (safe_divisor_from_imm64 ty imm)) (let ( (a Gpr a) @@ -4500,10 +4498,10 @@ (x64_idiv ty a (repeat_sign_bit ty a) (imm ty n) (TrapCode.INTEGER_DIVISION_BY_ZERO)) 1))) -(rule 1 (lower (srem a @ (value_type $I8) b)) +(rule 1 (lower (srem _ a @ (value_type $I8) b)) (x64_shrq_mi (x64_checked_srem_seq8 (x64_cbtw_zo a) b) 8)) -(rule (lower (srem a @ (value_type ty) b)) +(rule (lower (srem _ a @ (value_type ty) b)) (let ( (a Gpr a) (size OperandSize (raw_operand_size_of_type ty)) @@ -4514,31 +4512,31 @@ ;; Rules for `umulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (umulhi a @ (value_type $I8) b)) +(rule 0 (lower (umulhi _ a @ (value_type $I8) b)) (x64_shrw_mi (x64_mul8 false a b) 8)) -(rule 1 (lower (umulhi a @ (value_type (ty_int_ref_16_to_64 ty)) b)) +(rule 1 (lower (umulhi _ a @ (value_type (ty_int_ref_16_to_64 ty)) b)) (value_regs_get_gpr (x64_mul ty false a b) 1)) ;; The BMI2 instruction set introduced `mulx` which defines two registers but ;; if the two registers are the same then it only defines the upper bits. This ;; helps slightly reduce register pressure by ensuring only one register here is ;; clobbered. -(rule 2 (lower (umulhi a @ (value_type (ty_32_or_64 ty)) b)) +(rule 2 (lower (umulhi _ a @ (value_type (ty_32_or_64 ty)) b)) (if-let true (has_bmi2)) (x64_mulx_hi ty a b)) ;; Rules for `smulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (smulhi a @ (value_type $I8) b)) +(rule 0 (lower (smulhi _ a @ (value_type $I8) b)) (x64_sarw_mi (x64_mul8 true a b) 8)) -(rule 1 (lower (smulhi a @ (value_type (ty_int_ref_16_to_64 ty)) b)) +(rule 1 (lower (smulhi _ a @ (value_type (ty_int_ref_16_to_64 ty)) b)) (value_regs_get_gpr (x64_mul ty true a b) 1)) ;; Rules for `get_pinned_reg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_pinned_reg)) +(rule (lower (get_pinned_reg _)) (read_pinned_gpr)) ;; Rules for `set_pinned_reg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -4548,13 +4546,13 @@ ;; Rules for `vconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty (vconst const))) +(rule (lower (has_type ty (vconst _ const))) ;; TODO use Inst::gen_constant() instead. (x64_xmm_load_const ty (const_to_vconst const))) ;; Special cases for known constant patterns to skip a 16-byte load. -(rule 1 (lower (has_type ty (vconst (u128_from_constant 0)))) (xmm_zero ty)) -(rule 1 (lower (has_type ty (vconst (u128_from_constant -1)))) (vector_all_ones)) +(rule 1 (lower (has_type ty (vconst _ (u128_from_constant 0)))) (xmm_zero ty)) +(rule 1 (lower (has_type ty (vconst _ (u128_from_constant -1)))) (vector_all_ones)) ;; Rules for `shuffle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -4562,7 +4560,7 @@ ;; indicates which lane of the two operands is chosen for the output. A bit of ;; 0 chooses the corresponding 16-it lane from `a` and a bit of 1 chooses the ;; corresponding 16-bit lane from `b`. -(rule 14 (lower (shuffle a b (pblendw_imm n))) +(rule 14 (lower (shuffle _ a b (pblendw_imm n))) (if-let true (has_sse41)) (x64_pblendw a b n)) (decl pblendw_imm (u8) Immediate) @@ -4573,7 +4571,7 @@ ;; swapped in the instruction here. The `palignr` instruction uses the second ;; operand as the low-order bytes and the first operand as high-order bytes, ;; so put `a` second. -(rule 13 (lower (shuffle a b (palignr_imm_from_immediate n))) +(rule 13 (lower (shuffle _ a b (palignr_imm_from_immediate n))) (if-let true (has_ssse3)) (x64_palignr b a n)) (decl palignr_imm_from_immediate (u8) Immediate) @@ -4585,13 +4583,13 @@ ;; extractors here implemented in Rust and note that there's two cases for each ;; instruction here to match when either the first or second shuffle operand is ;; used. -(rule 12 (lower (shuffle x y (pshuflw_lhs_imm imm))) +(rule 12 (lower (shuffle _ x y (pshuflw_lhs_imm imm))) (x64_pshuflw x imm)) -(rule 11 (lower (shuffle x y (pshuflw_rhs_imm imm))) +(rule 11 (lower (shuffle _ x y (pshuflw_rhs_imm imm))) (x64_pshuflw y imm)) -(rule 10 (lower (shuffle x y (pshufhw_lhs_imm imm))) +(rule 10 (lower (shuffle _ x y (pshufhw_lhs_imm imm))) (x64_pshufhw x imm)) -(rule 9 (lower (shuffle x y (pshufhw_rhs_imm imm))) +(rule 9 (lower (shuffle _ x y (pshufhw_rhs_imm imm))) (x64_pshufhw y imm)) (decl pshuflw_lhs_imm (u8) Immediate) @@ -4608,9 +4606,9 @@ ;; selects 32-bit values from either `x` or `y`, but not both. This means ;; there's one rule for selecting from `x` and another rule for selecting from ;; `y`. -(rule 8 (lower (shuffle x y (pshufd_lhs_imm imm))) +(rule 8 (lower (shuffle _ x y (pshufd_lhs_imm imm))) (x64_pshufd x imm)) -(rule 7 (lower (shuffle x y (pshufd_rhs_imm imm))) +(rule 7 (lower (shuffle _ x y (pshufd_rhs_imm imm))) (x64_pshufd y imm)) (decl pshufd_lhs_imm (u8) Immediate) @@ -4619,27 +4617,27 @@ (extern extractor pshufd_rhs_imm pshufd_rhs_imm) ;; Special case for i8-level interleaving of upper/low bytes. -(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808))) +(rule 6 (lower (shuffle _ a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808))) (x64_punpckhbw a b)) -(rule 6 (lower (shuffle a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000))) +(rule 6 (lower (shuffle _ a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000))) (x64_punpcklbw a b)) ;; Special case for i16-level interleaving of upper/low bytes. -(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f1e_0f0e_1d1c_0d0c_1b1a_0b0a_1918_0908))) +(rule 6 (lower (shuffle _ a b (u128_from_immediate 0x1f1e_0f0e_1d1c_0d0c_1b1a_0b0a_1918_0908))) (x64_punpckhwd a b)) -(rule 6 (lower (shuffle a b (u128_from_immediate 0x1716_0706_1514_0504_1312_0302_1110_0100))) +(rule 6 (lower (shuffle _ a b (u128_from_immediate 0x1716_0706_1514_0504_1312_0302_1110_0100))) (x64_punpcklwd a b)) ;; Special case for i32-level interleaving of upper/low bytes. -(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_1b1a1918_0b0a0908))) +(rule 6 (lower (shuffle _ a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_1b1a1918_0b0a0908))) (x64_punpckhdq a b)) -(rule 6 (lower (shuffle a b (u128_from_immediate 0x17161514_07060504_13121110_03020100))) +(rule 6 (lower (shuffle _ a b (u128_from_immediate 0x17161514_07060504_13121110_03020100))) (x64_punpckldq a b)) ;; Special case for i64-level interleaving of upper/low bytes. -(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c1b1a1918_0f0e0d0c0b0a0908))) +(rule 6 (lower (shuffle _ a b (u128_from_immediate 0x1f1e1d1c1b1a1918_0f0e0d0c0b0a0908))) (x64_punpckhqdq a b)) -(rule 6 (lower (shuffle a b (u128_from_immediate 0x1716151413121110_0706050403020100))) +(rule 6 (lower (shuffle _ a b (u128_from_immediate 0x1716151413121110_0706050403020100))) (x64_punpcklqdq a b)) ;; If the vector shift mask is all 0s then that means the first byte of the @@ -4647,7 +4645,7 @@ ;; all-zeros constant from a rip-relative location but it should be slightly ;; more efficient to execute the `pshufb` here-and-now with an xor'd-to-be-zero ;; register. -(rule 6 (lower (shuffle a _ (u128_from_immediate 0))) +(rule 6 (lower (shuffle _ a _ (u128_from_immediate 0))) (if-let true (has_ssse3)) (x64_pshufb a (xmm_zero $I8X16))) @@ -4661,9 +4659,9 @@ ;; hypothesized that the dedicated instructions are better than `shufps`. ;; Someone with more knowledge about x86 timings should perhaps reorder the ;; rules here eventually though. -(rule 5 (lower (shuffle x y (shufps_imm imm))) +(rule 5 (lower (shuffle _ x y (shufps_imm imm))) (x64_shufps x y imm)) -(rule 4 (lower (shuffle x y (shufps_rev_imm imm))) +(rule 4 (lower (shuffle _ x y (shufps_rev_imm imm))) (x64_shufps y x imm)) (decl shufps_imm(u8) Immediate) @@ -4676,21 +4674,21 @@ ;; register. We statically build `constructed_mask` to zero out any unknown lane ;; indices (may not be completely necessary: verification could fail incorrect ;; mask values) and fix the indexes to all point to the `dst` vector. -(rule 3 (lower (shuffle a a (vec_mask_from_immediate mask))) +(rule 3 (lower (shuffle _ a a (vec_mask_from_immediate mask))) (if-let true (has_ssse3)) (x64_pshufb a (shuffle_0_31_mask mask))) ;; For the case where the shuffle mask contains out-of-bounds values (values ;; greater than 31) we must mask off those resulting values in the result of ;; `vpermi2b`. -(rule 2 (lower (shuffle a b (vec_mask_from_immediate (perm_from_mask_with_zeros mask zeros)))) +(rule 2 (lower (shuffle _ a b (vec_mask_from_immediate (perm_from_mask_with_zeros mask zeros)))) (if-let true (has_avx512vl)) (if-let true (has_avx512vbmi)) (x64_andps (x64_vpermi2b (x64_xmm_load_const $I8X16 mask) a b) zeros)) ;; However, if the shuffle mask contains no out-of-bounds values, we can use ;; `vpermi2b` without any masking. -(rule 1 (lower (shuffle a b (vec_mask_from_immediate mask))) +(rule 1 (lower (shuffle _ a b (vec_mask_from_immediate mask))) (if-let true (has_avx512vl)) (if-let true (has_avx512vbmi)) (x64_vpermi2b (x64_xmm_load_const $I8X16 (perm_from_mask mask)) a b)) @@ -4698,7 +4696,7 @@ ;; If `lhs` and `rhs` are different, we must shuffle each separately and then OR ;; them together. This is necessary due to PSHUFB semantics. As in the case ;; above, we build the `constructed_mask` for each case statically. -(rule (lower (shuffle a b (vec_mask_from_immediate mask))) +(rule (lower (shuffle _ a b (vec_mask_from_immediate mask))) (x64_por (lower_pshufb a (shuffle_0_15_mask mask)) (lower_pshufb b (shuffle_16_31_mask mask)))) @@ -4711,13 +4709,13 @@ ;; https://github.com/WebAssembly/simd/issues/93. The CLIF semantics match the ;; Wasm SIMD semantics for this instruction. The instruction format maps to ;; variables like: %dst = swizzle %src, %mask -(rule (lower (swizzle src mask)) +(rule (lower (swizzle _ src mask)) (let ((mask Xmm (x64_paddusb mask (emit_u128_le_const 0x70707070707070707070707070707070)))) (lower_pshufb src mask))) ;; Rules for `x86_pshufb` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (x86_pshufb src mask)) +(rule (lower (x86_pshufb _ src mask)) (if-let true (has_ssse3)) (x64_pshufb src mask)) @@ -4742,56 +4740,56 @@ ;; Remove the extractlane instruction, leaving the float where it is. The upper ;; bits will remain unchanged; for correctness, this relies on Cranelift type ;; checking to avoid using those bits. -(rule 3 (lower (has_type (ty_scalar_float _) (extractlane val 0))) +(rule 3 (lower (has_type (ty_scalar_float _) (extractlane _ val 0))) val) ;; `f32x4.extract_lane N` where `N != 0` -(rule 1 (lower (extractlane val @ (value_type $F32X4) (u8_from_uimm8 lane))) +(rule 1 (lower (extractlane _ val @ (value_type $F32X4) (u8_from_uimm8 lane))) (x64_pshufd val lane)) ;; `f64x2.extract_lane N` where `N != 0` (aka N == 1) -(rule (lower (extractlane val @ (value_type $F64X2) 1)) +(rule (lower (extractlane _ val @ (value_type $F64X2) 1)) (x64_pshufd val 0b11_10_11_10)) ;; `i8x16.extract_lane N` ;; ;; Note that without SSE4.1 a 16-bit lane extraction is performed and then ;; the result is updated if the desired index is either odd or even. -(rule 2 (lower (extractlane val @ (value_type ty @ $I8X16) (u8_from_uimm8 lane))) +(rule 2 (lower (extractlane _ val @ (value_type ty @ $I8X16) (u8_from_uimm8 lane))) (if-let true (has_sse41)) (x64_pextrb val lane)) ;; extracting an odd lane has an extra shift-right -(rule 1 (lower (extractlane val @ (value_type ty @ $I8X16) (u8_from_uimm8 lane))) +(rule 1 (lower (extractlane _ val @ (value_type ty @ $I8X16) (u8_from_uimm8 lane))) (if-let 1 (u8_and lane 1)) (x64_shrw_mi (x64_pextrw val (u8_wrapping_shr lane 1)) 8)) ;; Extracting an even lane already has the desired lane in the lower bits. Note ;; that having arbitrary upper bits in the returned register should be ok since ;; all operators on the resulting `i8` type should work correctly regardless of ;; the bits in the rest of the register. -(rule (lower (extractlane val @ (value_type ty @ $I8X16) (u8_from_uimm8 lane))) +(rule (lower (extractlane _ val @ (value_type ty @ $I8X16) (u8_from_uimm8 lane))) (if-let 0 (u8_and lane 1)) (x64_pextrw val (u8_wrapping_shr lane 1))) ;; `i16x8.extract_lane N` -(rule (lower (extractlane val @ (value_type ty @ $I16X8) (u8_from_uimm8 lane))) +(rule (lower (extractlane _ val @ (value_type ty @ $I16X8) (u8_from_uimm8 lane))) (x64_pextrw val lane)) ;; `i32x4.extract_lane N` -(rule 2 (lower (extractlane val @ (value_type ty @ $I32X4) (u8_from_uimm8 lane))) +(rule 2 (lower (extractlane _ val @ (value_type ty @ $I32X4) (u8_from_uimm8 lane))) (if-let true (has_sse41)) (x64_pextrd val lane)) -(rule 1 (lower (extractlane val @ (value_type $I32X4) 0)) +(rule 1 (lower (extractlane _ val @ (value_type $I32X4) 0)) (x64_movd_to_gpr val)) -(rule (lower (extractlane val @ (value_type $I32X4) (u8_from_uimm8 n))) +(rule (lower (extractlane _ val @ (value_type $I32X4) (u8_from_uimm8 n))) (x64_movd_to_gpr (x64_pshufd val n))) ;; `i64x2.extract_lane N` -(rule 1 (lower (extractlane val @ (value_type $I64X2) (u8_from_uimm8 lane))) +(rule 1 (lower (extractlane _ val @ (value_type $I64X2) (u8_from_uimm8 lane))) (if-let true (has_sse41)) (x64_pextrq val lane)) -(rule (lower (extractlane val @ (value_type $I64X2) 0)) +(rule (lower (extractlane _ val @ (value_type $I64X2) 0)) (x64_movq_to_gpr val)) -(rule (lower (extractlane val @ (value_type $I64X2) 1)) +(rule (lower (extractlane _ val @ (value_type $I64X2) 1)) (x64_movq_to_gpr (x64_pshufd val 0b00_00_11_10))) ;; Rules for `scalar_to_vector` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -4800,21 +4798,21 @@ ;; xmm-register-to-xmm-register semantics are used to modify only the low bits ;; of a guaranteed-zero register. This ensures that the upper bits are cleared ;; as the upper bits of `src` in a register are otherwise undefined. -(rule 1 (lower (scalar_to_vector src @ (value_type $F32))) +(rule 1 (lower (scalar_to_vector _ src @ (value_type $F32))) (x64_movss_regmove (xmm_zero $F32X4) src)) -(rule 1 (lower (scalar_to_vector src @ (value_type $F64))) +(rule 1 (lower (scalar_to_vector _ src @ (value_type $F64))) (x64_movsd_regmove (xmm_zero $F64X2) src)) ;; Case 2: when moving a scalar value of any other type, use MOVD to zero ;; the upper lanes. -(rule (lower (scalar_to_vector src @ (value_type ty))) +(rule (lower (scalar_to_vector _ src @ (value_type ty))) (bitcast_gpr_to_xmm (ty_bits ty) src)) ;; Case 3: when presented with `load + scalar_to_vector`, coalesce into a single ;; MOVSS/MOVSD instruction. -(rule 2 (lower (scalar_to_vector (and (sinkable_load src) (value_type (ty_32 _))))) +(rule 2 (lower (scalar_to_vector _ (and (sinkable_load src) (value_type (ty_32 _))))) (x64_movss_load src)) -(rule 3 (lower (scalar_to_vector (and (sinkable_load src) (value_type (ty_64 _))))) +(rule 3 (lower (scalar_to_vector _ (and (sinkable_load src) (value_type (ty_64 _))))) (x64_movsd_load src)) ;; Rules for `splat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -4830,20 +4828,20 @@ ;; i8x16 splats: use `vpbroadcastb` on AVX2 and otherwise `pshufb` broadcasts ;; with a mask of zero which is calculated with an xor-against-itself register. -(rule 0 (lower (has_type $I8X16 (splat src))) +(rule 0 (lower (has_type $I8X16 (splat _ src))) (let ((src Xmm (x64_movd_to_xmm src))) (x64_pshufd (x64_pshuflw (x64_punpcklbw src src) 0) 0))) -(rule 1 (lower (has_type $I8X16 (splat src))) +(rule 1 (lower (has_type $I8X16 (splat _ src))) (if-let true (has_ssse3)) (x64_pshufb (bitcast_gpr_to_xmm 32 src) (xmm_zero $I8X16))) -(rule 2 (lower (has_type $I8X16 (splat src))) +(rule 2 (lower (has_type $I8X16 (splat _ src))) (if-let true (use_avx2)) (x64_vpbroadcastb (bitcast_gpr_to_xmm 32 src))) -(rule 3 (lower (has_type $I8X16 (splat (sinkable_load_exact addr)))) +(rule 3 (lower (has_type $I8X16 (splat _ (sinkable_load_exact addr)))) (if-let true (has_sse41)) (if-let true (has_ssse3)) (x64_pshufb (x64_pinsrb (xmm_uninit_value) addr 0) (xmm_zero $I8X16))) -(rule 4 (lower (has_type $I8X16 (splat (sinkable_load_exact addr)))) +(rule 4 (lower (has_type $I8X16 (splat _ (sinkable_load_exact addr)))) (if-let true (use_avx2)) (x64_vpbroadcastb addr)) @@ -4852,14 +4850,14 @@ ;; to the low four lanes, and `pshufd` broadcasts the low 32-bit lane (which ;; at that point is two of the 16-bit values we want to broadcast) to all the ;; lanes. -(rule 0 (lower (has_type $I16X8 (splat src))) +(rule 0 (lower (has_type $I16X8 (splat _ src))) (x64_pshufd (x64_pshuflw (bitcast_gpr_to_xmm 32 src) 0) 0)) -(rule 1 (lower (has_type $I16X8 (splat src))) +(rule 1 (lower (has_type $I16X8 (splat _ src))) (if-let true (use_avx2)) (x64_vpbroadcastw (bitcast_gpr_to_xmm 32 src))) -(rule 2 (lower (has_type $I16X8 (splat (sinkable_load_exact addr)))) +(rule 2 (lower (has_type $I16X8 (splat _ (sinkable_load_exact addr)))) (x64_pshufd (x64_pshuflw (x64_pinsrw (xmm_uninit_value) addr 0) 0) 0)) -(rule 3 (lower (has_type $I16X8 (splat (sinkable_load_exact addr)))) +(rule 3 (lower (has_type $I16X8 (splat _ (sinkable_load_exact addr)))) (if-let true (use_avx2)) (x64_vpbroadcastw addr)) @@ -4867,19 +4865,19 @@ ;; used to broadcast the low lane to all other lanes. ;; ;; Note that sinkable-load cases come later -(rule 0 (lower (has_type $I32X4 (splat src))) +(rule 0 (lower (has_type $I32X4 (splat _ src))) (x64_pshufd (bitcast_gpr_to_xmm 32 src) 0)) -(rule 1 (lower (has_type $I32X4 (splat src))) +(rule 1 (lower (has_type $I32X4 (splat _ src))) (if-let true (use_avx2)) (x64_vpbroadcastd (bitcast_gpr_to_xmm 32 src))) ;; f32x4.splat - the source is already in an xmm register so `shufps` is all ;; that's necessary to complete the splat. This is specialized to `vbroadcastss` ;; on AVX2 to leverage that specific instruction for this operation. -(rule 0 (lower (has_type $F32X4 (splat src))) +(rule 0 (lower (has_type $F32X4 (splat _ src))) (let ((tmp Xmm src)) (x64_shufps src src 0))) -(rule 1 (lower (has_type $F32X4 (splat src))) +(rule 1 (lower (has_type $F32X4 (splat _ src))) (if-let true (use_avx2)) (x64_vbroadcastss src)) @@ -4892,27 +4890,27 @@ ;; the register-based encoding is only available with AVX2. With the ;; `sinkable_load` extractor this should be guaranteed to use the memory-based ;; encoding hence the `has_avx` test. -(rule 5 (lower (has_type (multi_lane 32 4) (splat (sinkable_load addr)))) +(rule 5 (lower (has_type (multi_lane 32 4) (splat _ (sinkable_load addr)))) (let ((tmp Xmm (x64_movss_load addr))) (x64_shufps tmp tmp 0))) -(rule 6 (lower (has_type (multi_lane 32 4) (splat (sinkable_load addr)))) +(rule 6 (lower (has_type (multi_lane 32 4) (splat _ (sinkable_load addr)))) (if-let true (has_avx)) (x64_vbroadcastss addr)) ;; t64x2.splat - use `pshufd` to broadcast the lower 64-bit lane to the upper ;; lane. A minor specialization for sinkable loads to avoid going through a gpr ;; for i64 splats is used as well when `movddup` is available. -(rule 0 (lower (has_type $I64X2 (splat src))) +(rule 0 (lower (has_type $I64X2 (splat _ src))) (x64_pshufd (bitcast_gpr_to_xmm 64 src) 0b01_00_01_00)) -(rule 0 (lower (has_type $F64X2 (splat src))) - (x64_pshufd src 0b01_00_01_00)) -(rule 6 (lower (has_type (multi_lane 64 2) (splat (sinkable_load addr)))) +(rule 0 (lower (has_type $F64X2 (splat _ src))) + (x64_pshufd (put_in_xmm src) 0b01_00_01_00)) +(rule 6 (lower (has_type (multi_lane 64 2) (splat _ (sinkable_load addr)))) (if-let true (has_sse3)) (x64_movddup addr)) ;; Rules for `vany_true` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (vany_true val)) (lower_cond_bool (is_vany_true val))) +(rule (lower (vany_true _ val)) (lower_cond_bool (is_vany_true val))) ;; Any nonzero byte in `val` means that any lane is true. Compare `val` with a ;; zeroed register and extract the high bits to a gpr mask. If the mask is @@ -4932,7 +4930,7 @@ ;; Rules for `vall_true` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (vall_true val)) (lower_cond_bool (is_vall_true val))) +(rule (lower (vall_true _ val)) (lower_cond_bool (is_vall_true val))) (decl is_vall_true (Value) CondResult) (rule 1 (is_vall_true val @ (value_type ty)) @@ -4961,13 +4959,13 @@ ;; for setting/clearing REX.W) as we need at most 16 bits of output for ;; `vhigh_bits`. -(rule (lower (vhigh_bits val @ (value_type (multi_lane 8 16)))) +(rule (lower (vhigh_bits _ val @ (value_type (multi_lane 8 16)))) (x64_pmovmskb val)) -(rule (lower (vhigh_bits val @ (value_type (multi_lane 32 4)))) +(rule (lower (vhigh_bits _ val @ (value_type (multi_lane 32 4)))) (x64_movmskps val)) -(rule (lower (vhigh_bits val @ (value_type (multi_lane 64 2)))) +(rule (lower (vhigh_bits _ val @ (value_type (multi_lane 64 2)))) (x64_movmskpd val)) ;; There is no x86 instruction for extracting the high bit of 16-bit lanes so @@ -4976,7 +4974,7 @@ ;; PACKSSWB([x1, x2, ...], [x1, x2, ...]) = [x1', x2', ..., x1', x2', ...] ;; - use PMOVMSKB to gather the high bits; now we have duplicates, though ;; - shift away the bottom 8 high bits to remove the duplicates. -(rule (lower (vhigh_bits val @ (value_type (multi_lane 16 8)))) +(rule (lower (vhigh_bits _ val @ (value_type (multi_lane 16 8)))) (let ((src Xmm val) (tmp Xmm (x64_packsswb src src)) (tmp Gpr (x64_pmovmskb tmp))) @@ -4984,12 +4982,12 @@ ;; Rules for `iconcat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (iconcat lo @ (value_type $I64) hi)) +(rule (lower (iconcat _ lo @ (value_type $I64) hi)) (value_regs lo hi)) ;; Rules for `isplit` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (isplit val @ (value_type $I128))) +(rule (lower (isplit _ val @ (value_type $I128))) (let ((regs ValueRegs val) (lo Reg (value_regs_get regs 0)) (hi Reg (value_regs_get regs 1))) @@ -4997,18 +4995,18 @@ ;; Rules for `tls_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (tls_model (TlsModel.ElfGd)) (tls_value (symbol_value_data name _ _)))) +(rule (lower (has_type (tls_model (TlsModel.ElfGd)) (tls_value _ (symbol_value_data name _ _)))) (elf_tls_get_addr name)) -(rule (lower (has_type (tls_model (TlsModel.Macho)) (tls_value (symbol_value_data name _ _)))) +(rule (lower (has_type (tls_model (TlsModel.Macho)) (tls_value _ (symbol_value_data name _ _)))) (macho_tls_get_addr name)) -(rule (lower (has_type (tls_model (TlsModel.Coff)) (tls_value (symbol_value_data name _ _)))) +(rule (lower (has_type (tls_model (TlsModel.Coff)) (tls_value _ (symbol_value_data name _ _)))) (coff_tls_get_addr name)) ;; Rules for `sqmul_round_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 1 (lower (sqmul_round_sat qx @ (value_type $I16X8) qy)) +(rule 1 (lower (sqmul_round_sat _ qx @ (value_type $I16X8) qy)) (if-let true (has_ssse3)) (let ((src1 Xmm qx) (src2 Xmm qy) @@ -5024,7 +5022,7 @@ ;; ;; so perform all those operations here manually with a lack of the native ;; instruction. -(rule (lower (sqmul_round_sat qx @ (value_type $I16X8) qy)) +(rule (lower (sqmul_round_sat _ qx @ (value_type $I16X8) qy)) (let ( (qx Xmm qx) (qy Xmm qy) @@ -5050,7 +5048,7 @@ ;; Rules for `x86_pmulhrsw` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (x86_pmulhrsw qx @ (value_type $I16X8) qy)) +(rule (lower (x86_pmulhrsw _ qx @ (value_type $I16X8) qy)) (if-let true (has_ssse3)) (x64_pmulhrsw qx qy)) @@ -5068,8 +5066,8 @@ ;; ROUNDPD xmm_y, xmm_y, 0x0B ;; ADDPD xmm_y, [wasm_f64x2_splat(0x1.0p+52)] ;; SHUFPS xmm_y, xmm_xmp, 0x88 -(rule (lower (uunarrow (fcvt_to_uint_sat src @ (value_type $F64X2)) - (vconst (u128_from_constant 0)))) +(rule (lower (uunarrow _ (fcvt_to_uint_sat _ src @ (value_type $F64X2)) + (vconst _ (u128_from_constant 0)))) (let ((src Xmm src) ;; MOVAPD xmm_y, xmm_x @@ -5096,7 +5094,7 @@ ;; Rules for `get_exception_handler_address` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (get_exception_handler_address (u64_from_imm64 idx) block)) +(rule (lower (get_exception_handler_address _ (u64_from_imm64 idx) block)) (let ((succ_label MachLabel (block_exn_successor_label block idx))) (x64_label_address succ_label))) diff --git a/cranelift/codegen/src/isa/x64/lower.rs b/cranelift/codegen/src/isa/x64/lower.rs index a000f8ece143..346ab7558195 100644 --- a/cranelift/codegen/src/isa/x64/lower.rs +++ b/cranelift/codegen/src/isa/x64/lower.rs @@ -3,14 +3,12 @@ // ISLE integration glue. pub(super) mod isle; -use crate::ir::pcc::{FactContext, PccResult}; use crate::ir::{ Endianness, ExternalName, Inst as IRInst, InstructionData, LibCall, Opcode, Type, types, }; use crate::isa::x64::abi::*; use crate::isa::x64::inst::args::*; use crate::isa::x64::inst::*; -use crate::isa::x64::pcc; use crate::isa::{CallConv, x64::X64Backend}; use crate::machinst::lower::*; use crate::machinst::*; @@ -338,16 +336,4 @@ impl LowerBackend for X64Backend { fn maybe_pinned_reg(&self) -> Option { Some(regs::pinned_reg()) } - - fn check_fact( - &self, - ctx: &FactContext<'_>, - vcode: &mut VCode, - inst: InsnIndex, - state: &mut pcc::FactFlowState, - ) -> PccResult<()> { - pcc::check(ctx, vcode, inst, state) - } - - type FactFlowState = pcc::FactFlowState; } diff --git a/cranelift/codegen/src/isa/x64/lower/isle.rs b/cranelift/codegen/src/isa/x64/lower/isle.rs index 57bfa06ee7fd..60d50df4ea12 100644 --- a/cranelift/codegen/src/isa/x64/lower/isle.rs +++ b/cranelift/codegen/src/isa/x64/lower/isle.rs @@ -36,7 +36,8 @@ type BoxCallIndInfo = Box>; type BoxReturnCallInfo = Box>; type BoxReturnCallIndInfo = Box>; type VecArgPair = Vec; -type BoxSyntheticAmode = Box; +type BoxAtomic128RmwSeqArgs = Box; +type BoxAtomic128XchgSeqArgs = Box; /// When interacting with the external assembler (see `external.rs`), we /// need to fix the types we'll use. @@ -639,7 +640,7 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> { } #[inline] - fn amode_offset(&mut self, addr: &SyntheticAmode, offset: i32) -> SyntheticAmode { + fn amode_try_offset(&mut self, addr: &SyntheticAmode, offset: i32) -> Option { addr.offset(offset) } @@ -970,8 +971,48 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> { WritableGpr::from_reg(reg) } - fn box_synthetic_amode(&mut self, amode: &SyntheticAmode) -> BoxSyntheticAmode { - Box::new(amode.clone()) + fn atomic128_rmw_seq_args( + &mut self, + op: &Atomic128RmwSeqOp, + mem_low: &SyntheticAmode, + mem_high: &SyntheticAmode, + operand_low: Gpr, + operand_high: Gpr, + temp_low: WritableGpr, + temp_high: WritableGpr, + dst_old_low: WritableGpr, + dst_old_high: WritableGpr, + ) -> BoxAtomic128RmwSeqArgs { + Box::new(Atomic128RmwSeqArgs { + op: *op, + mem_low: mem_low.clone(), + mem_high: mem_high.clone(), + operand_low, + operand_high, + temp_low, + temp_high, + dst_old_low, + dst_old_high, + }) + } + + fn atomic128_xchg_seq_args( + &mut self, + mem_low: &SyntheticAmode, + mem_high: &SyntheticAmode, + operand_low: Gpr, + operand_high: Gpr, + dst_old_low: WritableGpr, + dst_old_high: WritableGpr, + ) -> BoxAtomic128XchgSeqArgs { + Box::new(Atomic128XchgSeqArgs { + mem_low: mem_low.clone(), + mem_high: mem_high.clone(), + operand_low, + operand_high, + dst_old_low, + dst_old_high, + }) } //////////////////////////////////////////////////////////////////////////// diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs index a1cdcb3aeaeb..15eb736f479d 100644 --- a/cranelift/codegen/src/isa/x64/mod.rs +++ b/cranelift/codegen/src/isa/x64/mod.rs @@ -25,7 +25,6 @@ use target_lexicon::Triple; mod abi; mod inst; mod lower; -mod pcc; pub mod settings; #[cfg(feature = "unwind")] diff --git a/cranelift/codegen/src/isa/x64/pcc.rs b/cranelift/codegen/src/isa/x64/pcc.rs deleted file mode 100644 index 09b356d69958..000000000000 --- a/cranelift/codegen/src/isa/x64/pcc.rs +++ /dev/null @@ -1,326 +0,0 @@ -//! Proof-carrying-code validation for x64 VCode. - -use crate::ir::pcc::*; -use crate::ir::types::*; -use crate::isa::x64::inst::Inst; -use crate::isa::x64::inst::args::{Amode, Gpr, RegMem, SyntheticAmode, ToWritableReg}; -use crate::machinst::pcc::*; -use crate::machinst::{InsnIndex, VCode}; -use crate::machinst::{Reg, Writable}; -use crate::trace; - -fn undefined_result( - ctx: &FactContext, - vcode: &mut VCode, - dst: Writable, - reg_bits: u16, - result_bits: u16, -) -> PccResult<()> { - check_output(ctx, vcode, dst.to_writable_reg(), &[], |_vcode| { - clamp_range(ctx, reg_bits, result_bits, None) - }) -} - -fn ensure_no_fact(vcode: &VCode, reg: Reg) -> PccResult<()> { - if vcode.vreg_fact(reg.into()).is_some() { - Err(PccError::UnsupportedFact) - } else { - Ok(()) - } -} - -/// Flow-state between facts. -#[derive(Clone, Debug, Default)] -pub(crate) struct FactFlowState { - cmp_flags: Option<(Fact, Fact)>, -} - -pub(crate) fn check( - ctx: &FactContext, - vcode: &mut VCode, - inst_idx: InsnIndex, - state: &mut FactFlowState, -) -> PccResult<()> { - trace!("Checking facts on inst: {:?}", vcode[inst_idx]); - - // We only persist flag state for one instruction, because we - // can't exhaustively enumerate all flags-effecting ops; so take - // the `cmp_state` here and perhaps use it below but don't let it - // remain. - let _cmp_flags = state.cmp_flags.take(); - - match vcode[inst_idx] { - Inst::Args { .. } => { - // Defs on the args have "axiomatic facts": we trust the - // ABI code to pass through the values unharmed, so the - // facts given to us in the CLIF should still be true. - Ok(()) - } - - Inst::CheckedSRemSeq { - dst_quotient, - dst_remainder, - .. - } => { - undefined_result(ctx, vcode, dst_quotient, 64, 64)?; - undefined_result(ctx, vcode, dst_remainder, 64, 64)?; - Ok(()) - } - - Inst::CheckedSRemSeq8 { dst, .. } => undefined_result(ctx, vcode, dst, 64, 64), - - Inst::MovFromPReg { dst, .. } => undefined_result(ctx, vcode, dst, 64, 64), - Inst::MovToPReg { .. } => Ok(()), - - Inst::XmmCmove { dst, .. } => ensure_no_fact(vcode, dst.to_writable_reg().to_reg()), - - Inst::StackProbeLoop { tmp, .. } => ensure_no_fact(vcode, tmp.to_reg()), - - Inst::CvtUint64ToFloatSeq { - dst, - tmp_gpr1, - tmp_gpr2, - .. - } => { - ensure_no_fact(vcode, dst.to_writable_reg().to_reg())?; - ensure_no_fact(vcode, tmp_gpr1.to_writable_reg().to_reg())?; - ensure_no_fact(vcode, tmp_gpr2.to_writable_reg().to_reg())?; - Ok(()) - } - - Inst::CvtFloatToSintSeq { - dst, - tmp_gpr, - tmp_xmm, - .. - } => { - undefined_result(ctx, vcode, dst, 64, 64)?; - ensure_no_fact(vcode, tmp_gpr.to_writable_reg().to_reg())?; - ensure_no_fact(vcode, tmp_xmm.to_writable_reg().to_reg())?; - Ok(()) - } - - Inst::CvtFloatToUintSeq { - dst, - tmp_gpr, - tmp_xmm, - tmp_xmm2, - .. - } => { - undefined_result(ctx, vcode, dst, 64, 64)?; - ensure_no_fact(vcode, tmp_gpr.to_writable_reg().to_reg())?; - ensure_no_fact(vcode, tmp_xmm.to_writable_reg().to_reg())?; - ensure_no_fact(vcode, tmp_xmm2.to_writable_reg().to_reg())?; - Ok(()) - } - - Inst::XmmMinMaxSeq { dst, .. } => ensure_no_fact(vcode, dst.to_writable_reg().to_reg()), - - Inst::CallKnown { .. } - | Inst::ReturnCallKnown { .. } - | Inst::JmpKnown { .. } - | Inst::WinchJmpIf { .. } - | Inst::JmpCond { .. } - | Inst::JmpCondOr { .. } - | Inst::TrapIf { .. } - | Inst::TrapIfAnd { .. } - | Inst::TrapIfOr { .. } => Ok(()), - Inst::Rets { .. } => Ok(()), - - Inst::ReturnCallUnknown { .. } => Ok(()), - - Inst::CallUnknown { ref info } => match &info.dest { - RegMem::Mem { addr } => { - check_load(ctx, None, addr, vcode, I64, 64)?; - Ok(()) - } - RegMem::Reg { .. } => Ok(()), - }, - - Inst::JmpTableSeq { tmp1, tmp2, .. } => { - ensure_no_fact(vcode, tmp1.to_reg())?; - ensure_no_fact(vcode, tmp2.to_reg())?; - Ok(()) - } - - Inst::LoadExtName { dst, .. } => { - ensure_no_fact(vcode, *dst.to_reg())?; - Ok(()) - } - - Inst::AtomicRmwSeq { - ref mem, - temp, - dst_old, - .. - } => { - ensure_no_fact(vcode, *dst_old.to_reg())?; - ensure_no_fact(vcode, *temp.to_reg())?; - check_store(ctx, None, mem, vcode, I64)?; - Ok(()) - } - - Inst::Atomic128RmwSeq { - ref mem, - temp_low, - temp_high, - dst_old_low, - dst_old_high, - .. - } => { - ensure_no_fact(vcode, *dst_old_low.to_reg())?; - ensure_no_fact(vcode, *dst_old_high.to_reg())?; - ensure_no_fact(vcode, *temp_low.to_reg())?; - ensure_no_fact(vcode, *temp_high.to_reg())?; - check_store(ctx, None, mem, vcode, I128)?; - Ok(()) - } - - Inst::Atomic128XchgSeq { - ref mem, - dst_old_low, - dst_old_high, - .. - } => { - ensure_no_fact(vcode, *dst_old_low.to_reg())?; - ensure_no_fact(vcode, *dst_old_high.to_reg())?; - check_store(ctx, None, mem, vcode, I128)?; - Ok(()) - } - - Inst::XmmUninitializedValue { dst } => { - ensure_no_fact(vcode, dst.to_writable_reg().to_reg()) - } - - Inst::GprUninitializedValue { dst } => { - ensure_no_fact(vcode, dst.to_writable_reg().to_reg()) - } - - Inst::ElfTlsGetAddr { dst, .. } | Inst::MachOTlsGetAddr { dst, .. } => { - ensure_no_fact(vcode, dst.to_writable_reg().to_reg()) - } - Inst::CoffTlsGetAddr { dst, tmp, .. } => { - ensure_no_fact(vcode, dst.to_writable_reg().to_reg())?; - ensure_no_fact(vcode, tmp.to_writable_reg().to_reg())?; - Ok(()) - } - - Inst::Unwind { .. } | Inst::DummyUse { .. } => Ok(()), - - Inst::StackSwitchBasic { .. } => Err(PccError::UnimplementedInst), - - Inst::LabelAddress { .. } => Err(PccError::UnimplementedInst), - - Inst::SequencePoint { .. } => Ok(()), - - Inst::External { .. } => Ok(()), // TODO: unsure what to do about this! - } -} - -fn check_load( - ctx: &FactContext, - dst: Option>, - src: &SyntheticAmode, - vcode: &VCode, - ty: Type, - to_bits: u16, -) -> PccResult> { - let result_fact = dst.and_then(|dst| vcode.vreg_fact(dst.to_reg().into())); - let from_bits = u16::try_from(ty.bits()).unwrap(); - check_mem( - ctx, - src, - vcode, - ty, - LoadOrStore::Load { - result_fact, - from_bits, - to_bits, - }, - ) -} - -fn check_store( - ctx: &FactContext, - data: Option, - dst: &SyntheticAmode, - vcode: &VCode, - ty: Type, -) -> PccResult<()> { - let stored_fact = data.and_then(|data| vcode.vreg_fact(data.into())); - check_mem(ctx, dst, vcode, ty, LoadOrStore::Store { stored_fact }).map(|_| ()) -} - -fn check_mem<'a>( - ctx: &FactContext, - amode: &SyntheticAmode, - vcode: &VCode, - ty: Type, - op: LoadOrStore<'a>, -) -> PccResult> { - let addr = match amode { - SyntheticAmode::Real(amode) if amode.get_flags().checked() => { - compute_addr(ctx, vcode, amode, 64).ok_or(PccError::MissingFact)? - } - _ => return Ok(None), - }; - - match op { - LoadOrStore::Load { - result_fact, - from_bits, - to_bits, - } => { - let loaded_fact = clamp_range(ctx, to_bits, from_bits, ctx.load(&addr, ty)?.cloned())?; - trace!( - "loaded_fact = {:?} result_fact = {:?}", - loaded_fact, result_fact - ); - if ctx.subsumes_fact_optionals(loaded_fact.as_ref(), result_fact) { - Ok(loaded_fact.clone()) - } else { - Err(PccError::UnsupportedFact) - } - } - LoadOrStore::Store { stored_fact } => { - ctx.store(&addr, ty, stored_fact)?; - Ok(None) - } - } -} - -fn compute_addr(ctx: &FactContext, vcode: &VCode, amode: &Amode, bits: u16) -> Option { - trace!("compute_addr: {:?}", amode); - match *amode { - Amode::ImmReg { simm32, base, .. } => { - let base = get_fact_or_default(vcode, base, bits); - trace!("base = {:?}", base); - let simm32: i64 = simm32.into(); - let simm32: u64 = simm32 as u64; - let offset = Fact::constant(bits, simm32); - let sum = ctx.add(&base, &offset, bits)?; - trace!("sum = {:?}", sum); - Some(sum) - } - Amode::ImmRegRegShift { - simm32, - base, - index, - shift, - .. - } => { - let base = get_fact_or_default(vcode, base.into(), bits); - let index = get_fact_or_default(vcode, index.into(), bits); - trace!("base = {:?} index = {:?}", base, index); - let shifted = ctx.shl(&index, bits, shift.into())?; - let sum = ctx.add(&base, &shifted, bits)?; - let simm32: i64 = simm32.into(); - let simm32: u64 = simm32 as u64; - let offset = Fact::constant(bits, simm32); - let sum = ctx.add(&sum, &offset, bits)?; - trace!("sum = {:?}", sum); - Some(sum) - } - Amode::RipRelative { .. } => None, - } -} diff --git a/cranelift/codegen/src/isle_prelude.rs b/cranelift/codegen/src/isle_prelude.rs index c10446b75b40..c63309fbd3f9 100644 --- a/cranelift/codegen/src/isle_prelude.rs +++ b/cranelift/codegen/src/isle_prelude.rs @@ -128,6 +128,52 @@ macro_rules! isle_common_prelude_methods { Imm64::new((x >> y) & ty_mask) } + #[inline] + fn imm64_rotl(&mut self, ty: Type, x: Imm64, y: Imm64) -> Imm64 { + let bits = ty.bits(); + assert!(bits <= 64); + // This holds for all Cranelift types ({u/i}{8,16,32,64}) + debug_assert!(bits.is_power_of_two()); + + let ty_mask = self.ty_mask(ty); + let x = (x.bits() as u64) & ty_mask; + + // Mask off any excess rotate bits so the rotate stays within `ty`. + let shift_mask = bits - 1; + let y = ((y.bits() as u64) & u64::from(shift_mask)) as u32; + + // In Rust, x >> 64 or x << 64 panics. + let result = if y == 0 { + x + } else { + (x << y) | (x >> (u32::from(bits) - y)) + }; + + Imm64::new((result & ty_mask) as i64) + } + + #[inline] + fn imm64_rotr(&mut self, ty: Type, x: Imm64, y: Imm64) -> Imm64 { + let bits = ty.bits(); + assert!(bits <= 64); + debug_assert!(bits.is_power_of_two()); + + let ty_mask = self.ty_mask(ty); + let x = (x.bits() as u64) & ty_mask; + + // Mask off any excess rotate bits so the rotate stays within `ty`. + let shift_mask = bits - 1; + let y = ((y.bits() as u64) & u64::from(shift_mask)) as u32; + + let result = if y == 0 { + x + } else { + (x >> y) | (x << (u32::from(bits) - y)) + }; + + Imm64::new((result & ty_mask) as i64) + } + #[inline] fn i64_sextend_u64(&mut self, ty: Type, x: u64) -> i64 { let shift_amt = core::cmp::max(0, 64 - ty.bits()); diff --git a/cranelift/codegen/src/legalizer/globalvalue.rs b/cranelift/codegen/src/legalizer/globalvalue.rs index 45444d161934..ce7801624737 100644 --- a/cranelift/codegen/src/legalizer/globalvalue.rs +++ b/cranelift/codegen/src/legalizer/globalvalue.rs @@ -5,7 +5,8 @@ use super::WalkCommand; use crate::cursor::{Cursor, FuncCursor}; -use crate::ir::{self, InstBuilder, pcc::Fact}; +use crate::ir; +use crate::ir::InstBuilder; use crate::isa::TargetIsa; /// Expand a `global_value` instruction according to the definition of the global value. @@ -61,7 +62,7 @@ fn const_vector_scale( /// Expand a `global_value` instruction for a vmctx global. fn vmctx_addr( - global_value: ir::GlobalValue, + _global_value: ir::GlobalValue, inst: ir::Inst, func: &mut ir::Function, ) -> WalkCommand { @@ -76,15 +77,6 @@ fn vmctx_addr( func.dfg.change_to_alias(result, vmctx); func.layout.remove_inst(inst); - // If there was a fact on the GV, then copy it to the vmctx arg - // blockparam def. - if let Some(fact) = &func.global_value_facts[global_value] { - if func.dfg.facts[vmctx].is_none() { - let fact = fact.clone(); - func.dfg.facts[vmctx] = Some(fact); - } - } - // We removed the instruction, so `cursor.next_inst()` will fail if we // return `WalkCommand::Continue`; instead "revisit" the current // instruction, which will be the next instruction since we removed the @@ -104,18 +96,10 @@ fn iadd_imm_addr( // Get the value for the lhs. let lhs = pos.ins().global_value(global_type, base); - if let Some(fact) = &pos.func.global_value_facts[base] { - pos.func.dfg.facts[lhs] = Some(fact.clone()); - } // Generate the constant and attach a fact to the constant if // there is a fact on the base. let constant = pos.ins().iconst(global_type, offset); - if pos.func.global_value_facts[base].is_some() { - let bits = u16::try_from(global_type.bits()).unwrap(); - let unsigned_offset = offset as u64; // Safety: reinterpret i64 bits as u64. - pos.func.dfg.facts[constant] = Some(Fact::constant(bits, unsigned_offset)); - } // Simply replace the `global_value` instruction with an `iadd_imm`, reusing the result value. pos.func.dfg.replace(inst).iadd(lhs, constant); @@ -144,9 +128,6 @@ fn load_addr( // Get the value for the base. let base_addr = pos.ins().global_value(ptr_ty, base); - if let Some(fact) = &pos.func.global_value_facts[base] { - pos.func.dfg.facts[base_addr] = Some(fact.clone()); - } // Perform the load. pos.func diff --git a/cranelift/codegen/src/machinst/blockorder.rs b/cranelift/codegen/src/machinst/blockorder.rs index 671426e600b2..215b204eeee2 100644 --- a/cranelift/codegen/src/machinst/blockorder.rs +++ b/cranelift/codegen/src/machinst/blockorder.rs @@ -134,6 +134,7 @@ impl LoweredBlock { } /// The associated out-edge successor, if this is a critical edge. + #[cfg(test)] pub fn out_edge(&self) -> Option { match self { &LoweredBlock::CriticalEdge { succ, .. } => Some(succ), diff --git a/cranelift/codegen/src/machinst/buffer.rs b/cranelift/codegen/src/machinst/buffer.rs index abd5396178db..33f836874d51 100644 --- a/cranelift/codegen/src/machinst/buffer.rs +++ b/cranelift/codegen/src/machinst/buffer.rs @@ -378,7 +378,7 @@ pub struct MachBufferFinalized { pub(crate) traps: SmallVec<[MachTrap; 16]>, /// Any call site records referring to this code. pub(crate) call_sites: SmallVec<[MachCallSite; 16]>, - /// Any patchable call site locations refering to this code. + /// Any patchable call site locations referring to this code. pub(crate) patchable_call_sites: SmallVec<[MachPatchableCallSite; 16]>, /// Any exception-handler records referred to at call sites. pub(crate) exception_handlers: SmallVec<[FinalizedMachExceptionHandler; 16]>, @@ -771,7 +771,12 @@ impl MachBuffer { offset, kind, }; - self.pending_fixup_deadline = self.pending_fixup_deadline.min(fixup.deadline()); + self.pending_fixup_deadline = self + .pending_fixup_deadline + // Subtract one alignment here to the deadline to account for + // extra space taken by aligning an island. + .min(fixup.deadline() - I::LabelUse::ALIGN); + trace!("pending_fixup_deadline = {}", self.pending_fixup_deadline); self.pending_fixup_records.push(fixup); // Post-invariant: no mutations to branches/labels data structures. @@ -1280,7 +1285,15 @@ impl MachBuffer { Some(fixup) => fixup.deadline().min(self.pending_fixup_deadline), None => self.pending_fixup_deadline, }; - deadline < u32::MAX && self.worst_case_end_of_island(distance) > deadline + trace!( + "checking island_needed: cur_offset = {} deadline = {} worst_case_end_of_island = {}", + self.cur_offset(), + deadline, + self.worst_case_end_of_island(distance) + ); + let needed = deadline < u32::MAX && self.worst_case_end_of_island(distance) > deadline; + trace!(" -> needed = {needed}"); + needed } /// Returns the maximal offset that islands can reach if `distance` more @@ -1320,6 +1333,11 @@ impl MachBuffer { distance: CodeOffset, ctrl_plane: &mut ControlPlane, ) { + trace!( + "emitting island at {}, distance = {distance}", + self.cur_offset() + ); + // We're going to purge fixups, so no latest-branch editing can happen // anymore. self.latest_branches.clear(); @@ -1336,41 +1354,29 @@ impl MachBuffer { } let forced_threshold = self.worst_case_end_of_island(distance); - - // First flush out all traps/constants so we have more labels in case - // fixups are applied against these labels. + trace!("forced_threshold = {forced_threshold}"); + + // Emit traps/constants after the island: with potentially + // unbounded pending constants/traps and potentially small + // deadlines, it would otherwise be possible to emit a + // small-range jump, have a nearby deadline *before* the end + // of pending constants/traps, and not be able to emit a + // veneer in time. // - // Note that traps are placed first since this typically happens at the - // end of the function and for disassemblers we try to keep all the code - // contiguously together. - for MachLabelTrap { label, code, loc } in mem::take(&mut self.pending_traps) { - // If this trap has source information associated with it then - // emit this information for the trap instruction going out now too. - if let Some(loc) = loc { - self.start_srcloc(loc); - } - self.align_to(I::LabelUse::ALIGN); - self.bind_label(label, ctrl_plane); - self.add_trap(code); - self.put_data(I::TRAP_OPCODE); - if loc.is_some() { - self.end_srcloc(); - } - } - - for constant in mem::take(&mut self.pending_constants) { - let MachBufferConstant { align, size, .. } = self.constants[constant]; - let label = self.constants[constant].upcoming_label.take().unwrap(); - self.align_to(align); - self.bind_label(label, ctrl_plane); - self.used_constants.push((constant, self.cur_offset())); - self.get_appended_space(size); - } + // Fixups whose labels aren't yet defined (e.g. references to + // pending constants/traps) are simply deferred here; they'll + // be resolved in the next island or in the final fixup pass + // at the end of emission. // Either handle all pending fixups because they're ready or move them // onto the `BinaryHeap` tracking all pending fixups if they aren't // ready. assert!(self.latest_branches.is_empty()); + trace!( + "About to handle fixups at offset {}: {:?}", + self.cur_offset(), + self.pending_fixup_records + ); for fixup in mem::take(&mut self.pending_fixup_records) { if self.should_apply_fixup(&fixup, forced_threshold) { self.handle_fixup(fixup, force_veneers, forced_threshold); @@ -1380,7 +1386,11 @@ impl MachBuffer { } self.pending_fixup_deadline = u32::MAX; while let Some(fixup) = self.fixup_records.peek() { - trace!("emit_island: fixup {:?}", fixup); + trace!( + "emit_island: fixup {:?} deadline {}", + fixup, + fixup.deadline() + ); // If this fixup shouldn't be applied, that means its label isn't // defined yet and there'll be remaining space to apply a veneer if @@ -1395,6 +1405,37 @@ impl MachBuffer { self.handle_fixup(fixup, force_veneers, forced_threshold); } + // Now emit pending traps and constants. + // + // Note that traps are placed first since this typically happens at the + // end of the function and for disassemblers we try to keep all the code + // contiguously together. + trace!("emitting pending traps: {:?}", self.pending_traps); + for MachLabelTrap { label, code, loc } in mem::take(&mut self.pending_traps) { + // If this trap has source information associated with it then + // emit this information for the trap instruction going out now too. + if let Some(loc) = loc { + self.start_srcloc(loc); + } + self.align_to(I::LabelUse::ALIGN); + self.bind_label(label, ctrl_plane); + self.add_trap(code); + self.put_data(I::TRAP_OPCODE); + if loc.is_some() { + self.end_srcloc(); + } + } + + trace!("emitting pending constants: {:?}", self.pending_constants); + for constant in mem::take(&mut self.pending_constants) { + let MachBufferConstant { align, size, .. } = self.constants[constant]; + let label = self.constants[constant].upcoming_label.take().unwrap(); + self.align_to(align); + self.bind_label(label, ctrl_plane); + self.used_constants.push((constant, self.cur_offset())); + self.get_appended_space(size); + } + if let Some(loc) = cur_loc { self.start_srcloc(loc); } @@ -1402,7 +1443,20 @@ impl MachBuffer { fn should_apply_fixup(&self, fixup: &MachLabelFixup, forced_threshold: CodeOffset) -> bool { let label_offset = self.resolve_label_offset(fixup.label); - label_offset != UNKNOWN_LABEL_OFFSET || fixup.deadline() < forced_threshold + trace!( + "should_apply_fixup: fixup {fixup:?} label_offset {label_offset} deadline {} forced_threshold {forced_threshold} supports_veneer {}", + fixup.deadline(), + fixup.kind.supports_veneer() + ); + let result = (label_offset != UNKNOWN_LABEL_OFFSET) + || ((fixup.deadline() < forced_threshold) && fixup.kind.supports_veneer()); + trace!( + " -> {}, {}, {} -> {result}", + label_offset != UNKNOWN_LABEL_OFFSET, + fixup.deadline() < forced_threshold, + fixup.kind.supports_veneer() + ); + result } fn handle_fixup( @@ -1525,7 +1579,7 @@ impl MachBuffer { // `emit_island()` will emit any pending veneers and constants, and // as a side-effect, will also take care of any fixups with resolved // labels eagerly. - self.emit_island_maybe_forced(force_veneers, u32::MAX, ctrl_plane); + self.emit_island_maybe_forced(force_veneers, 0, ctrl_plane); } // Ensure that all labels have been fixed up after the last island is emitted. This is a @@ -1865,7 +1919,7 @@ impl MachBufferFinalized { &self.user_stack_maps } - /// Take this buffer's user strack map metadata. + /// Take this buffer's user stack map metadata. pub fn take_user_stack_maps(&mut self) -> SmallVec<[(CodeOffset, u32, ir::UserStackMap); 8]> { mem::take(&mut self.user_stack_maps) } @@ -1991,6 +2045,7 @@ struct MachBufferConstant { /// A trap that is deferred to the next time an island is emitted for either /// traps, constants, or fixups. +#[derive(Debug)] struct MachLabelTrap { /// This label will refer to the trap's offset. label: MachLabel, @@ -2463,6 +2518,7 @@ mod test { use super::*; use crate::ir::UserExternalNameRef; + use crate::isa::aarch64; use crate::isa::aarch64::inst::{BranchTarget, CondBrKind, EmitInfo, Inst}; use crate::isa::aarch64::inst::{OperandSize, xreg}; use crate::machinst::{MachInstEmit, MachInstEmitState}; @@ -2475,9 +2531,15 @@ mod test { BranchTarget::Label(label(n)) } + fn emit_info() -> EmitInfo { + let flags = settings::Flags::new(settings::builder()); + let isa_flags = aarch64::settings::Flags::new(&flags, &aarch64::settings::builder()); + EmitInfo::new(flags, isa_flags) + } + #[test] fn test_elide_jump_to_next() { - let info = EmitInfo::new(settings::Flags::new(settings::builder())); + let info = emit_info(); let mut buf = MachBuffer::new(); let mut state = ::State::default(); let constants = Default::default(); @@ -2493,7 +2555,7 @@ mod test { #[test] fn test_elide_trivial_jump_blocks() { - let info = EmitInfo::new(settings::Flags::new(settings::builder())); + let info = emit_info(); let mut buf = MachBuffer::new(); let mut state = ::State::default(); let constants = Default::default(); @@ -2524,7 +2586,7 @@ mod test { #[test] fn test_flip_cond() { - let info = EmitInfo::new(settings::Flags::new(settings::builder())); + let info = emit_info(); let mut buf = MachBuffer::new(); let mut state = ::State::default(); let constants = Default::default(); @@ -2570,7 +2632,7 @@ mod test { #[test] fn test_island() { - let info = EmitInfo::new(settings::Flags::new(settings::builder())); + let info = emit_info(); let mut buf = MachBuffer::new(); let mut state = ::State::default(); let constants = Default::default(); @@ -2639,7 +2701,7 @@ mod test { #[test] fn test_island_backward() { - let info = EmitInfo::new(settings::Flags::new(settings::builder())); + let info = emit_info(); let mut buf = MachBuffer::new(); let mut state = ::State::default(); let constants = Default::default(); @@ -2725,7 +2787,7 @@ mod test { // label7: // ret - let info = EmitInfo::new(settings::Flags::new(settings::builder())); + let info = emit_info(); let mut buf = MachBuffer::new(); let mut state = ::State::default(); let constants = Default::default(); @@ -2802,7 +2864,7 @@ mod test { // // label0, label1, ..., label4: // b label0 - let info = EmitInfo::new(settings::Flags::new(settings::builder())); + let info = emit_info(); let mut buf = MachBuffer::new(); let mut state = ::State::default(); let constants = Default::default(); diff --git a/cranelift/codegen/src/machinst/compile.rs b/cranelift/codegen/src/machinst/compile.rs index 84c68b337498..7747d804cafe 100644 --- a/cranelift/codegen/src/machinst/compile.rs +++ b/cranelift/codegen/src/machinst/compile.rs @@ -1,9 +1,7 @@ //! Compilation backend pipeline: optimized IR to VCode / binemit. -use crate::CodegenError; use crate::dominator_tree::DominatorTree; use crate::ir::Function; -use crate::ir::pcc; use crate::isa::TargetIsa; use crate::machinst::*; use crate::settings::RegallocAlgorithm; @@ -31,7 +29,7 @@ pub fn compile( crate::machinst::Lower::new(f, abi, emit_info, block_order, sigs, b.flags().clone())?; // Lower the IR. - let mut vcode = { + let vcode = { log::debug!( "Number of CLIF instructions to lower: {}", f.dfg.num_insts() @@ -49,11 +47,6 @@ pub fn compile( log::debug!("Number of lowered vcode blocks: {}", vcode.num_blocks()); trace!("vcode from lowering: \n{:?}", vcode); - // Perform validation of proof-carrying-code facts, if requested. - if b.flags().enable_pcc() { - pcc::check_vcode_facts(f, &mut vcode, b).map_err(CodegenError::Pcc)?; - } - // Perform register allocation. let regalloc_result = { let _tt = timing::regalloc(); diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs index f73ede5b257b..499b807f72f7 100644 --- a/cranelift/codegen/src/machinst/isle.rs +++ b/cranelift/codegen/src/machinst/isle.rs @@ -134,11 +134,6 @@ macro_rules! isle_lower_prelude_methods { .collect() } - #[inline] - fn ensure_in_vreg(&mut self, reg: Reg, ty: Type) -> Reg { - self.lower_ctx.ensure_in_vreg(reg, ty) - } - #[inline] fn value_regs_get(&mut self, regs: ValueRegs, i: usize) -> Reg { regs.regs()[i] @@ -203,8 +198,13 @@ macro_rules! isle_lower_prelude_methods { } #[inline] - fn inst_data_value(&mut self, inst: Inst) -> InstructionData { - self.lower_ctx.dfg().insts[inst] + fn inst_data_value(&mut self, inst: Inst) -> (Type, InstructionData) { + let ty = match self.first_result(inst) { + Some(v) => self.value_type(v), + None => types::INVALID, + }; + let data = self.lower_ctx.dfg().insts[inst]; + (ty, data) } #[inline] @@ -774,11 +774,6 @@ macro_rules! isle_lower_prelude_methods { targets.len() as u32 } - fn add_range_fact(&mut self, reg: Reg, bits: u16, min: u64, max: u64) -> Reg { - self.lower_ctx.add_range_fact(reg, bits, min, max); - reg - } - fn value_is_unused(&mut self, val: Value) -> bool { self.lower_ctx.value_is_unused(val) } diff --git a/cranelift/codegen/src/machinst/lower.rs b/cranelift/codegen/src/machinst/lower.rs index c3ce9fa9c96c..d62e4f882518 100644 --- a/cranelift/codegen/src/machinst/lower.rs +++ b/cranelift/codegen/src/machinst/lower.rs @@ -7,7 +7,6 @@ use crate::entity::SecondaryMap; use crate::inst_predicates::{has_lowering_side_effect, is_constant_64bit}; -use crate::ir::pcc::{Fact, FactContext, PccError, PccResult}; use crate::ir::{ ArgumentPurpose, Block, BlockArg, Constant, ConstantData, DataFlowGraph, ExternalName, Function, GlobalValue, GlobalValueData, Immediate, Inst, InstructionData, MemFlags, @@ -149,22 +148,6 @@ pub trait LowerBackend { fn maybe_pinned_reg(&self) -> Option { None } - - /// The type of state carried between `check_fact` invocations. - type FactFlowState: Default + Clone + Debug; - - /// Check any facts about an instruction, given VCode with facts - /// on VRegs. Takes mutable `VCode` so that it can propagate some - /// kinds of facts automatically. - fn check_fact( - &self, - _ctx: &FactContext<'_>, - _vcode: &mut VCode, - _inst: InsnIndex, - _state: &mut Self::FactFlowState, - ) -> PccResult<()> { - Err(PccError::UnimplementedBackend) - } } /// Machine-independent lowering driver / machine-instruction container. Maintains a correspondence @@ -408,7 +391,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { for ¶m in f.dfg.block_params(bb) { let ty = f.dfg.value_type(param); if value_regs[param].is_invalid() { - let regs = vregs.alloc_with_maybe_fact(ty, f.dfg.facts[param].clone())?; + let regs = vregs.alloc(ty)?; value_regs[param] = regs; trace!("bb {} param {}: regs {:?}", bb, param, regs); } @@ -417,7 +400,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { for &result in f.dfg.inst_results(inst) { let ty = f.dfg.value_type(result); if value_regs[result].is_invalid() && !ty.is_invalid() { - let regs = vregs.alloc_with_maybe_fact(ty, f.dfg.facts[result].clone())?; + let regs = vregs.alloc(ty)?; value_regs[result] = regs; trace!( "bb {} inst {} ({:?}): result {} regs {:?}", @@ -539,10 +522,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> { self.vcode.sigs_mut() } - pub fn vregs_mut(&mut self) -> &mut VRegAllocator { - &mut self.vregs - } - fn gen_arg_setup(&mut self) { if let Some(entry_bb) = self.f.layout.entry_block() { trace!( @@ -802,7 +781,9 @@ impl<'func, I: VCodeInst> Lower<'func, I> { // Value defined by "inst" becomes live after it in normal // order, and therefore **before** in reversed order. - self.emit_value_label_live_range_start_for_inst(inst); + // Only emit value label aliases if the instruction will be lowered + // (otherwise we want to keep using the earlier label instead). + self.emit_value_label_live_range_start_for_inst(inst, has_side_effect || value_needed); // Normal instruction: codegen if the instruction is side-effecting // or any of its outputs is used. @@ -949,14 +930,14 @@ impl<'func, I: VCodeInst> Lower<'func, I> { } } - fn emit_value_label_marks_for_value(&mut self, val: Value) { + fn emit_value_label_marks_for_value(&mut self, val: Value, allow_alias: bool) { let regs = self.value_regs[val]; if regs.len() > 1 { return; } let reg = regs.only_reg().unwrap(); - if let Some(label_starts) = self.get_value_labels(val, 0) { + if let Some(label_starts) = self.get_value_labels(val, if allow_alias { 0 } else { !0 }) { let labels = label_starts .iter() .map(|&ValueLabelStart { label, .. }| label) @@ -971,7 +952,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { } } - fn emit_value_label_live_range_start_for_inst(&mut self, inst: Inst) { + fn emit_value_label_live_range_start_for_inst(&mut self, inst: Inst, allow_alias: bool) { if self.f.dfg.values_labels.is_none() { return; } @@ -982,7 +963,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { inst ); for &val in self.f.dfg.inst_results(inst) { - self.emit_value_label_marks_for_value(val); + self.emit_value_label_marks_for_value(val, allow_alias); } } @@ -993,7 +974,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { trace!("value labeling: block {}", block); for &arg in self.f.dfg.block_params(block) { - self.emit_value_label_marks_for_value(arg); + self.emit_value_label_marks_for_value(arg, true); } self.finish_ir_inst(Default::default()); } @@ -1170,8 +1151,27 @@ impl<'func, I: VCodeInst> Lower<'func, I> { // End branch. if let Some(bb) = lb.orig_block() { if let Some(branch) = self.collect_branch_and_targets(bindex, bb, &mut targets) { + let branch_start = self.vcode.vcode.num_insts(); self.lower_clif_branch(backend, bindex, bb, branch, &targets)?; self.finish_ir_inst(self.srcloc(branch)); + + // Branch instructions like try_call can also be safepoints + // that need stack maps. Forward the stack map from the CLIF + // branch to the VCode safepoint, just like we do for + // non-branch instructions in `lower_clif_block`. + if let Some(entries) = self.f.dfg.user_stack_map_entries(branch) { + let branch_end = self.vcode.vcode.num_insts(); + for i in branch_start..branch_end { + let iix = InsnIndex::new(i); + if self.vcode.vcode[iix].is_safepoint() { + self.vcode.add_user_stack_map( + BackwardsInsnIndex::new(iix.index()), + entries, + ); + break; + } + } + } } } else { // If no orig block, this must be a pure edge block; @@ -1648,18 +1648,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> { regs } - - /// Get the ValueRegs for the edge-defined values for special - /// try-call-return block arguments. - pub fn try_call_return_defs(&mut self, ir_inst: Inst) -> &[ValueRegs>] { - &self.try_call_rets.get(&ir_inst).unwrap()[..] - } - - /// Get the Regs for the edge-defined values for special - /// try-call-return exception payload arguments. - pub fn try_call_exception_defs(&mut self, ir_inst: Inst) -> &[Writable] { - &self.try_call_payloads.get(&ir_inst).unwrap()[..] - } } /// Codegen primitives: allocate temps, emit instructions, set result registers, @@ -1670,11 +1658,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> { writable_value_regs(self.vregs.alloc_with_deferred_error(ty)) } - /// Get the current root instruction that we are lowering. - pub fn cur_inst(&self) -> Inst { - self.cur_inst.unwrap() - } - /// Emit a machine instruction. pub fn emit(&mut self, mach_inst: I) { trace!("emit: {:?}", mach_inst); @@ -1719,33 +1702,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> { pub fn use_constant(&mut self, constant: VCodeConstantData) -> VCodeConstant { self.vcode.constants().insert(constant) } - - /// Cause the value in `reg` to be in a virtual reg, by copying it into a - /// new virtual reg if `reg` is a real reg. `ty` describes the type of the - /// value in `reg`. - pub fn ensure_in_vreg(&mut self, reg: Reg, ty: Type) -> Reg { - if reg.to_virtual_reg().is_some() { - reg - } else { - let new_reg = self.alloc_tmp(ty).only_reg().unwrap(); - self.emit(I::gen_move(new_reg, reg, ty)); - new_reg.to_reg() - } - } - - /// Add a range fact to a register, if no other fact is present. - pub fn add_range_fact(&mut self, reg: Reg, bit_width: u16, min: u64, max: u64) { - if self.flags.enable_pcc() { - self.vregs.set_fact_if_missing( - reg.to_virtual_reg().unwrap(), - Fact::Range { - bit_width, - min, - max, - }, - ); - } - } } #[cfg(test)] diff --git a/cranelift/codegen/src/machinst/mod.rs b/cranelift/codegen/src/machinst/mod.rs index c327803d5fb3..850b93f29fc3 100644 --- a/cranelift/codegen/src/machinst/mod.rs +++ b/cranelift/codegen/src/machinst/mod.rs @@ -84,7 +84,6 @@ pub use helpers::*; pub mod valueregs; pub use reg::*; pub use valueregs::*; -pub mod pcc; pub mod reg; /// A machine instruction. diff --git a/cranelift/codegen/src/machinst/pcc.rs b/cranelift/codegen/src/machinst/pcc.rs deleted file mode 100644 index 046b2ca3b3e2..000000000000 --- a/cranelift/codegen/src/machinst/pcc.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! Common helpers for ISA-specific proof-carrying-code implementations. - -use crate::ir::pcc::{Fact, FactContext, PccError, PccResult}; -use crate::machinst::{Reg, VCode, VCodeInst, Writable}; -use crate::trace; - -pub(crate) fn get_fact_or_default(vcode: &VCode, reg: Reg, width: u16) -> Fact { - trace!( - "get_fact_or_default: reg {reg:?} -> {:?}", - vcode.vreg_fact(reg.into()) - ); - vcode - .vreg_fact(reg.into()) - .cloned() - .unwrap_or_else(|| Fact::max_range_for_width(width)) -} - -pub(crate) fn has_fact(vcode: &VCode, reg: Reg) -> bool { - vcode.vreg_fact(reg.into()).is_some() -} - -pub(crate) fn fail_if_missing(fact: Option) -> PccResult { - fact.ok_or(PccError::UnsupportedFact) -} - -pub(crate) fn clamp_range( - ctx: &FactContext, - to_bits: u16, - from_bits: u16, - fact: Option, -) -> PccResult> { - let max = if from_bits > 64 { - return Ok(None); - } else if from_bits == 64 { - u64::MAX - } else { - (1u64 << from_bits) - 1 - }; - trace!( - "clamp_range: fact {:?} from {} to {}", - fact, from_bits, to_bits - ); - Ok(fact - .and_then(|f| ctx.uextend(&f, from_bits, to_bits)) - .or_else(|| { - let result = Fact::Range { - bit_width: to_bits, - min: 0, - max, - }; - trace!(" -> clamping to {:?}", result); - Some(result) - })) -} - -pub(crate) fn check_subsumes(ctx: &FactContext, subsumer: &Fact, subsumee: &Fact) -> PccResult<()> { - check_subsumes_optionals(ctx, Some(subsumer), Some(subsumee)) -} - -pub(crate) fn check_subsumes_optionals( - ctx: &FactContext, - subsumer: Option<&Fact>, - subsumee: Option<&Fact>, -) -> PccResult<()> { - trace!( - "checking if derived fact {:?} subsumes stated fact {:?}", - subsumer, subsumee - ); - - if ctx.subsumes_fact_optionals(subsumer, subsumee) { - Ok(()) - } else { - Err(PccError::UnsupportedFact) - } -} - -pub(crate) fn check_output) -> PccResult>>( - ctx: &FactContext, - vcode: &mut VCode, - out: Writable, - ins: &[Reg], - f: F, -) -> PccResult<()> { - if let Some(fact) = vcode.vreg_fact(out.to_reg().into()) { - let result = f(vcode)?; - check_subsumes_optionals(ctx, result.as_ref(), Some(fact)) - } else if ins.iter().any(|r| { - vcode - .vreg_fact(r.into()) - .map(|fact| fact.propagates()) - .unwrap_or(false) - }) { - if let Ok(Some(fact)) = f(vcode) { - trace!("setting vreg {:?} to {:?}", out, fact); - vcode.set_vreg_fact(out.to_reg().into(), fact); - } - Ok(()) - } else { - Ok(()) - } -} - -pub(crate) fn check_unop PccResult>>( - ctx: &FactContext, - vcode: &mut VCode, - reg_width: u16, - out: Writable, - ra: Reg, - f: F, -) -> PccResult<()> { - check_output(ctx, vcode, out, &[ra], |vcode| { - let ra = get_fact_or_default(vcode, ra, reg_width); - f(&ra) - }) -} - -pub(crate) fn check_binop PccResult>>( - ctx: &FactContext, - vcode: &mut VCode, - reg_width: u16, - out: Writable, - ra: Reg, - rb: Reg, - f: F, -) -> PccResult<()> { - check_output(ctx, vcode, out, &[ra, rb], |vcode| { - let ra = get_fact_or_default(vcode, ra, reg_width); - let rb = get_fact_or_default(vcode, rb, reg_width); - f(&ra, &rb) - }) -} - -pub(crate) fn check_constant( - ctx: &FactContext, - vcode: &mut VCode, - out: Writable, - bit_width: u16, - value: u64, -) -> PccResult<()> { - let result = Fact::constant(bit_width, value); - if let Some(fact) = vcode.vreg_fact(out.to_reg().into()) { - check_subsumes(ctx, &result, fact) - } else { - trace!("setting vreg {:?} to {:?}", out, result); - vcode.set_vreg_fact(out.to_reg().into(), result); - Ok(()) - } -} - -/// The operation we're checking against an amode: either -/// -/// - a *load*, and we need to validate that the field's fact subsumes -/// the load result's fact, OR -/// -/// - a *store*, and we need to validate that the stored data's fact -/// subsumes the field's fact. -pub(crate) enum LoadOrStore<'a> { - Load { - result_fact: Option<&'a Fact>, - from_bits: u16, - to_bits: u16, - }, - Store { - stored_fact: Option<&'a Fact>, - }, -} diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs index 2e8120a1999a..ff93f89aae69 100644 --- a/cranelift/codegen/src/machinst/vcode.rs +++ b/cranelift/codegen/src/machinst/vcode.rs @@ -19,7 +19,6 @@ use crate::CodegenError; use crate::FxHashMap; -use crate::ir::pcc::*; use crate::ir::{self, Constant, ConstantData, ValueLabel, types}; use crate::ranges::Ranges; use crate::timing; @@ -204,9 +203,6 @@ pub struct VCode { pub(crate) sigs: SigSet, - /// Facts on VRegs, for proof-carrying code verification. - facts: Vec>, - log2_min_function_alignment: u8, } @@ -566,7 +562,6 @@ impl VCodeBuilder { /// Build the final VCode. pub fn build(mut self, mut vregs: VRegAllocator) -> VCode { self.vcode.vreg_types = take(&mut vregs.vreg_types); - self.vcode.facts = take(&mut vregs.facts); if self.direction == VCodeBuildDirection::Backward { self.reverse_and_finalize(&vregs); @@ -591,19 +586,6 @@ impl VCodeBuilder { vregs.debug_assert_no_vreg_aliases( self.vcode.debug_value_labels.iter().map(|&(vreg, ..)| vreg), ); - // Facts are resolved eagerly during set_vreg_alias. - vregs.debug_assert_no_vreg_aliases( - self.vcode - .facts - .iter() - .zip(&vregs.vreg_types) - .enumerate() - .filter(|(_, (fact, _))| fact.is_some()) - .map(|(vreg, (_, &ty))| { - let (regclasses, _) = I::rc_for_type(ty).unwrap(); - VReg::new(vreg, regclasses[0]) - }), - ); self.vcode } @@ -668,7 +650,6 @@ impl VCode { emit_info, constants, debug_value_labels: vec![], - facts: vec![], log2_min_function_alignment, } } @@ -1528,31 +1509,6 @@ impl VCode { self.block_order.lowered_order()[block.index()].orig_block() } - /// Get the type of a VReg. - pub fn vreg_type(&self, vreg: VReg) -> Type { - self.vreg_types[vreg.vreg()] - } - - /// Get the fact, if any, for a given VReg. - pub fn vreg_fact(&self, vreg: VReg) -> Option<&Fact> { - self.facts[vreg.vreg()].as_ref() - } - - /// Set the fact for a given VReg. - pub fn set_vreg_fact(&mut self, vreg: VReg, fact: Fact) { - trace!("set fact on {}: {:?}", vreg, fact); - self.facts[vreg.vreg()] = Some(fact); - } - - /// Does a given instruction define any facts? - pub fn inst_defines_facts(&self, inst: InsnIndex) -> bool { - self.inst_operands(inst) - .iter() - .filter(|o| o.kind() == OperandKind::Def) - .map(|o| o.vreg()) - .any(|vreg| self.facts[vreg.vreg()].is_some()) - } - /// Get the user stack map associated with the given forward instruction index. pub fn get_user_stack_map(&self, inst: InsnIndex) -> Option<&ir::UserStackMap> { let index = inst.to_backwards_insn_index(self.num_insts()); @@ -1669,12 +1625,6 @@ impl Debug for VRegAllocator { writeln!(f, " {:?} := {:?}", Reg::from(key), Reg::from(*dest))?; } - for (vreg, fact) in self.facts.iter().enumerate() { - if let Some(fact) = fact { - writeln!(f, " v{vreg} ! {fact}")?; - } - } - writeln!(f, "}}") } } @@ -1712,15 +1662,6 @@ impl fmt::Debug for VCode { inst, self.insts[inst].pretty_print_inst(&mut state) )?; - if !self.operands.is_empty() { - for operand in self.inst_operands(InsnIndex::new(inst)) { - if operand.kind() == OperandKind::Def { - if let Some(fact) = &self.facts[operand.vreg().vreg()] { - writeln!(f, " v{} ! {}", operand.vreg().vreg(), fact)?; - } - } - } - } if let Some(user_stack_map) = self.get_user_stack_map(InsnIndex::new(inst)) { writeln!(f, " {user_stack_map:?}")?; } @@ -1751,9 +1692,6 @@ pub struct VRegAllocator { /// lowering rules) or some ABI code. deferred_error: Option, - /// Facts on VRegs, for proof-carrying code. - facts: Vec>, - /// The type of instruction that this allocator makes registers for. _inst: core::marker::PhantomData, } @@ -1768,7 +1706,6 @@ impl VRegAllocator { vreg_types, vreg_aliases: FxHashMap::with_capacity_and_hasher(capacity, Default::default()), deferred_error: None, - facts: Vec::with_capacity(capacity), _inst: core::marker::PhantomData::default(), } } @@ -1808,9 +1745,6 @@ impl VRegAllocator { self.vreg_types.push(reg_ty); } - // Create empty facts for each allocated vreg. - self.facts.resize(self.vreg_types.len(), None); - Ok(regs) } @@ -1853,13 +1787,6 @@ impl VRegAllocator { // Disallow cycles (see below). assert_ne!(resolved_to, from); - // Maintain the invariant that PCC facts only exist on vregs - // which aren't aliases. We want to preserve whatever was - // stated about the vreg before its producer was lowered. - if let Some(fact) = self.facts[from.vreg()].take() { - self.set_fact(resolved_to, fact); - } - let old_alias = self.vreg_aliases.insert(from, resolved_to); debug_assert_eq!(old_alias, None); } @@ -1884,42 +1811,6 @@ impl VRegAllocator { fn debug_assert_no_vreg_aliases(&self, mut list: impl Iterator) { debug_assert!(list.all(|vreg| !self.vreg_aliases.contains_key(&vreg))); } - - /// Set the proof-carrying code fact on a given virtual register. - /// - /// Returns the old fact, if any (only one fact can be stored). - fn set_fact(&mut self, vreg: regalloc2::VReg, fact: Fact) -> Option { - trace!("vreg {:?} has fact: {:?}", vreg, fact); - debug_assert!(!self.vreg_aliases.contains_key(&vreg)); - self.facts[vreg.vreg()].replace(fact) - } - - /// Set a fact only if one doesn't already exist. - pub fn set_fact_if_missing(&mut self, vreg: VirtualReg, fact: Fact) { - let vreg = self.resolve_vreg_alias(vreg.into()); - if self.facts[vreg.vreg()].is_none() { - self.set_fact(vreg, fact); - } - } - - /// Allocate a fresh ValueRegs, with a given fact to apply if - /// the value fits in one VReg. - pub fn alloc_with_maybe_fact( - &mut self, - ty: Type, - fact: Option, - ) -> CodegenResult> { - let result = self.alloc(ty)?; - - // Ensure that we don't lose a fact on a value that splits - // into multiple VRegs. - assert!(result.len() == 1 || fact.is_none()); - if let Some(fact) = fact { - self.set_fact(result.regs()[0].into(), fact); - } - - Ok(result) - } } /// This structure tracks the large constants used in VCode that will be emitted separately by the diff --git a/cranelift/codegen/src/opts/arithmetic.isle b/cranelift/codegen/src/opts/arithmetic.isle index 81184532a5f3..ba41c5a1454e 100644 --- a/cranelift/codegen/src/opts/arithmetic.isle +++ b/cranelift/codegen/src/opts/arithmetic.isle @@ -291,3 +291,100 @@ (if-let 0 (u64_checked_rem z x)) (if-let 1 (u64_rem x 2)) (eq ty y (iconst ty1 (imm64 (u64_div z x))))) + +;; (x + y) + (-y) ==> x +;; and equivalent operand-order variants. +(rule (simplify (iadd ty (iadd ty x y) (ineg ty y))) x) +(rule (simplify (iadd ty (ineg ty y) (iadd ty x y))) x) +(rule (simplify (iadd ty (iadd ty y x) (ineg ty y))) x) +(rule (simplify (iadd ty (ineg ty y) (iadd ty y x))) x) + +;; (x | y) - (x & y) ==> (x ^ y) +(rule (simplify (isub ty (bor ty x y) (band ty x y))) (bxor ty x y)) +(rule (simplify (isub ty (bor ty x y) (band ty y x))) (bxor ty x y)) +(rule (simplify (isub ty (bor ty y x) (band ty x y))) (bxor ty x y)) +(rule (simplify (isub ty (bor ty y x) (band ty y x))) (bxor ty x y)) + +;; (x + y) - (x & y) ==> (x | y) +(rule (simplify (isub ty (iadd ty x y) (band ty x y))) (bor ty x y)) +(rule (simplify (isub ty (iadd ty x y) (band ty y x))) (bor ty x y)) +(rule (simplify (isub ty (iadd ty y x) (band ty x y))) (bor ty x y)) +(rule (simplify (isub ty (iadd ty y x) (band ty y x))) (bor ty x y)) + +;; (x | y) - (x ^ y) ==> (x & y) +(rule (simplify (isub ty (bor ty x y) (bxor ty x y))) (band ty x y)) +(rule (simplify (isub ty (bor ty x y) (bxor ty y x))) (band ty x y)) +(rule (simplify (isub ty (bor ty y x) (bxor ty x y))) (band ty x y)) +(rule (simplify (isub ty (bor ty y x) (bxor ty y x))) (band ty x y)) + +;; (~x) + x == -1 +;; Keep the generic fold for <=64-bit types, and handle i128 explicitly. +(rule (simplify (iadd (fits_in_64 ty) (bnot ty x) x)) (iconst_s ty -1)) +(rule (simplify (iadd (fits_in_64 ty) x (bnot ty x))) (iconst_s ty -1)) + +(rule (simplify (iadd $I128 (bnot $I128 x) x)) (sextend $I128 (iconst_s $I64 -1))) +(rule (simplify (iadd $I128 x (bnot $I128 x))) (sextend $I128 (iconst_s $I64 -1))) + +;; ((x + y) - (x + z)) --> (y - z) +(rule (simplify (isub ty (iadd ty x y) (iadd ty x z))) (isub ty y z)) +(rule (simplify (isub ty (iadd ty x y) (iadd ty z x))) (isub ty y z)) +(rule (simplify (isub ty (iadd ty y x) (iadd ty x z))) (isub ty y z)) +(rule (simplify (isub ty (iadd ty y x) (iadd ty z x))) (isub ty y z)) + +;; ((x - z) - (y - z)) --> (x - y) +(rule (simplify (isub ty (isub ty x z) (isub ty y z))) (isub ty x y)) + +;; ((x - y) - (x - z)) --> (z - y) +(rule (simplify (isub ty (isub ty x y) (isub ty x z))) (isub ty z y)) + +;; umin(x, y) + umax(x, y) --> x + y +(rule (simplify (iadd ty (umin ty x y) (umax ty x y))) (iadd ty x y)) +(rule (simplify (iadd ty (umax ty x y) (umin ty x y))) (iadd ty x y)) +(rule (simplify (iadd ty (umin ty x y) (umax ty y x))) (iadd ty x y)) +(rule (simplify (iadd ty (umax ty y x) (umin ty x y))) (iadd ty x y)) +(rule (simplify (iadd ty (umin ty y x) (umax ty x y))) (iadd ty x y)) +(rule (simplify (iadd ty (umax ty x y) (umin ty y x))) (iadd ty x y)) +(rule (simplify (iadd ty (umin ty y x) (umax ty y x))) (iadd ty x y)) +(rule (simplify (iadd ty (umax ty y x) (umin ty y x))) (iadd ty x y)) + +;; smin(x, y) + smax(x, y) --> x + y +(rule (simplify (iadd ty (smin ty x y) (smax ty x y))) (iadd ty x y)) +(rule (simplify (iadd ty (smax ty x y) (smin ty x y))) (iadd ty x y)) +(rule (simplify (iadd ty (smin ty x y) (smax ty y x))) (iadd ty x y)) +(rule (simplify (iadd ty (smax ty y x) (smin ty x y))) (iadd ty x y)) +(rule (simplify (iadd ty (smin ty y x) (smax ty x y))) (iadd ty x y)) +(rule (simplify (iadd ty (smax ty x y) (smin ty y x))) (iadd ty x y)) +(rule (simplify (iadd ty (smin ty y x) (smax ty y x))) (iadd ty x y)) +(rule (simplify (iadd ty (smax ty y x) (smin ty y x))) (iadd ty x y)) + +;; ((x + z) - (y + z)) --> (x - y) +(rule (simplify (isub ty (iadd ty x z) (iadd ty y z))) (isub ty x y)) +(rule (simplify (isub ty (iadd ty x z) (iadd ty z y))) (isub ty x y)) +(rule (simplify (isub ty (iadd ty z x) (iadd ty y z))) (isub ty x y)) +(rule (simplify (isub ty (iadd ty z x) (iadd ty z y))) (isub ty x y)) + +;; ((x - y) + (y + z)) --> (x + z) +(rule (simplify (iadd ty (isub ty x y) (iadd ty y z))) (iadd ty x z)) +(rule (simplify (iadd ty (iadd ty y z) (isub ty x y))) (iadd ty x z)) +(rule (simplify (iadd ty (isub ty x y) (iadd ty z y))) (iadd ty x z)) +(rule (simplify (iadd ty (iadd ty z y) (isub ty x y))) (iadd ty x z)) + +;; (x - (x + y)) --> -y +(rule (simplify (isub ty x (iadd ty x y))) (ineg ty y)) +(rule (simplify (isub ty x (iadd ty y x))) (ineg ty y)) + +;; (x + (y + (z - x))) --> (y + z) +(rule (simplify (iadd ty x (iadd ty y (isub ty z x)))) (iadd ty y z)) +(rule (simplify (iadd ty (iadd ty y (isub ty z x)) x)) (iadd ty y z)) +(rule (simplify (iadd ty x (iadd ty (isub ty z x) y))) (iadd ty y z)) +(rule (simplify (iadd ty (iadd ty (isub ty z x) y) x)) (iadd ty y z)) + +;; (x + y) == (y + x) --> true +(rule (simplify (eq (ty_int ty) (iadd cty x y) (iadd cty y x))) (iconst_u ty 1)) +(rule (simplify (eq (ty_int ty) (iadd cty y x) (iadd cty x y))) (iconst_u ty 1)) +(rule (simplify (eq (ty_int ty) (iadd cty x y) (iadd cty x y))) (iconst_u ty 1)) +(rule (simplify (eq (ty_int ty) (iadd cty y x) (iadd cty y x))) (iconst_u ty 1)) + +;; (x - y) != x --> y != 0 +(rule (simplify (ne cty (isub (ty_int ty) x y) x)) (ne cty y (iconst_u ty 0))) +(rule (simplify (ne cty x (isub (ty_int ty) x y))) (ne cty y (iconst_u ty 0))) diff --git a/cranelift/codegen/src/opts/bitops.isle b/cranelift/codegen/src/opts/bitops.isle index ee8af0f4ac02..646e03f01895 100644 --- a/cranelift/codegen/src/opts/bitops.isle +++ b/cranelift/codegen/src/opts/bitops.isle @@ -246,4 +246,454 @@ (rule (simplify (iadd ty (ineg ty y) (bor ty y x))) (band ty x (bnot ty y))) - +; x & (x | y) --> x +(rule (simplify (band ty (bor ty x y) x)) x) +(rule (simplify (band ty x (bor ty x y))) x) +(rule (simplify (band ty (bor ty y x) x)) x) +(rule (simplify (band ty x (bor ty y x))) x) + +; (x | z) & (y | z) --> (x & y) | z +(rule (simplify (band ty (bor ty x z) (bor ty y z))) (bor ty (band ty x y) z)) +(rule (simplify (band ty (bor ty y z) (bor ty x z))) (bor ty (band ty x y) z)) +(rule (simplify (band ty (bor ty x z) (bor ty z y))) (bor ty (band ty x y) z)) +(rule (simplify (band ty (bor ty z y) (bor ty x z))) (bor ty (band ty x y) z)) +(rule (simplify (band ty (bor ty z x) (bor ty y z))) (bor ty (band ty x y) z)) +(rule (simplify (band ty (bor ty y z) (bor ty z x))) (bor ty (band ty x y) z)) +(rule (simplify (band ty (bor ty z x) (bor ty z y))) (bor ty (band ty x y) z)) +(rule (simplify (band ty (bor ty z y) (bor ty z x))) (bor ty (band ty x y) z)) + +; (x & z) | (y & z) --> (x | y) & z +(rule (simplify (bor ty (band ty x z) (band ty y z))) (band ty (bor ty x y) z)) +(rule (simplify (bor ty (band ty y z) (band ty x z))) (band ty (bor ty x y) z)) +(rule (simplify (bor ty (band ty x z) (band ty z y))) (band ty (bor ty x y) z)) +(rule (simplify (bor ty (band ty z y) (band ty x z))) (band ty (bor ty x y) z)) +(rule (simplify (bor ty (band ty z x) (band ty y z))) (band ty (bor ty x y) z)) +(rule (simplify (bor ty (band ty y z) (band ty z x))) (band ty (bor ty x y) z)) +(rule (simplify (bor ty (band ty z x) (band ty z y))) (band ty (bor ty x y) z)) +(rule (simplify (bor ty (band ty z y) (band ty z x))) (band ty (bor ty x y) z)) + +; (x | y) ^ (x | ~y) --> ~x +(rule (simplify (bxor ty (bor ty x y) (bor ty x (bnot ty y)))) (bnot ty x)) +(rule (simplify (bxor ty (bor ty x (bnot ty y)) (bor ty x y))) (bnot ty x)) +(rule (simplify (bxor ty (bor ty x y) (bor ty (bnot ty y) x))) (bnot ty x)) +(rule (simplify (bxor ty (bor ty (bnot ty y) x) (bor ty x y))) (bnot ty x)) +(rule (simplify (bxor ty (bor ty y x) (bor ty x (bnot ty y)))) (bnot ty x)) +(rule (simplify (bxor ty (bor ty x (bnot ty y)) (bor ty y x))) (bnot ty x)) +(rule (simplify (bxor ty (bor ty y x) (bor ty (bnot ty y) x))) (bnot ty x)) +(rule (simplify (bxor ty (bor ty (bnot ty y) x) (bor ty y x))) (bnot ty x)) + +; (~x) & (~y) --> ~(x | y) +(rule (simplify (band ty (bnot ty x) (bnot ty y))) (bnot ty (bor ty x y))) +(rule (simplify (band ty (bnot ty y) (bnot ty x))) (bnot ty (bor ty x y))) + +; (~x) | (~y) --> ~(x & y) +(rule (simplify (bor ty (bnot ty x) (bnot ty y))) (bnot ty (band ty x y))) +(rule (simplify (bor ty (bnot ty y) (bnot ty x))) (bnot ty (band ty x y))) + +; x | ((x ^ y) ^ z) --> x | (y ^ z) +(rule (simplify (bor ty (bxor ty (bxor ty x y) z) x)) (bor ty (bxor ty y z) x)) +(rule (simplify (bor ty x (bxor ty (bxor ty x y) z))) (bor ty (bxor ty y z) x)) +(rule (simplify (bor ty (bxor ty z (bxor ty x y)) x)) (bor ty (bxor ty y z) x)) +(rule (simplify (bor ty x (bxor ty z (bxor ty x y)))) (bor ty (bxor ty y z) x)) +(rule (simplify (bor ty (bxor ty (bxor ty y x) z) x)) (bor ty (bxor ty y z) x)) +(rule (simplify (bor ty x (bxor ty (bxor ty y x) z))) (bor ty (bxor ty y z) x)) +(rule (simplify (bor ty (bxor ty z (bxor ty y x)) x)) (bor ty (bxor ty y z) x)) +(rule (simplify (bor ty x (bxor ty z (bxor ty y x)))) (bor ty (bxor ty y z) x)) + +; (x ^ z) == (y ^ z) --> x == y +(rule (simplify (eq ty (bxor cty x z) (bxor cty y z))) (eq ty x y)) +(rule (simplify (eq ty (bxor cty y z) (bxor cty x z))) (eq ty x y)) +(rule (simplify (eq ty (bxor cty x z) (bxor cty z y))) (eq ty x y)) +(rule (simplify (eq ty (bxor cty z y) (bxor cty x z))) (eq ty x y)) +(rule (simplify (eq ty (bxor cty z x) (bxor cty y z))) (eq ty x y)) +(rule (simplify (eq ty (bxor cty y z) (bxor cty z x))) (eq ty x y)) +(rule (simplify (eq ty (bxor cty z x) (bxor cty z y))) (eq ty x y)) +(rule (simplify (eq ty (bxor cty z y) (bxor cty z x))) (eq ty x y)) + +; y | ~(x | y) --> y | ~x +(rule (simplify (bor ty y (bnot ty (bor ty x y)))) (bor ty y (bnot ty x))) +(rule (simplify (bor ty (bnot ty (bor ty x y)) y)) (bor ty y (bnot ty x))) +(rule (simplify (bor ty y (bnot ty (bor ty y x)))) (bor ty y (bnot ty x))) +(rule (simplify (bor ty (bnot ty (bor ty y x)) y)) (bor ty y (bnot ty x))) + +; y & ~(x & y) --> y & ~x +(rule (simplify (band ty y (bnot ty (band ty x y)))) (band ty y (bnot ty x))) +(rule (simplify (band ty (bnot ty (band ty x y)) y)) (band ty y (bnot ty x))) +(rule (simplify (band ty y (bnot ty (band ty y x)))) (band ty y (bnot ty x))) +(rule (simplify (band ty (bnot ty (band ty y x)) y)) (band ty y (bnot ty x))) + +; (~x) ^ (x | y) --> x | ~y +(rule (simplify (bxor ty (bnot ty x) (bor ty x y))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bor ty x y) (bnot ty x))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bnot ty x) (bor ty y x))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bor ty y x) (bnot ty x))) (bor ty x (bnot ty y))) + +; (x < y) | (x > y) --> x != y +(rule (simplify (bor ty (slt ty x y) (sgt ty x y))) (ne ty x y)) +(rule (simplify (bor ty (sgt ty x y) (slt ty x y))) (ne ty x y)) +(rule (simplify (bor ty (slt ty x y) (slt ty y x))) (ne ty x y)) +(rule (simplify (bor ty (slt ty y x) (slt ty x y))) (ne ty x y)) +(rule (simplify (bor ty (sgt ty y x) (sgt ty x y))) (ne ty x y)) +(rule (simplify (bor ty (sgt ty x y) (sgt ty y x))) (ne ty x y)) +(rule (simplify (bor ty (sgt ty y x) (slt ty y x))) (ne ty x y)) +(rule (simplify (bor ty (slt ty y x) (sgt ty y x))) (ne ty x y)) + +; (x <_u y) | (x >_u y) --> x != y +(rule (simplify (bor ty (ult ty x y) (ugt ty x y))) (ne ty x y)) +(rule (simplify (bor ty (ugt ty x y) (ult ty x y))) (ne ty x y)) +(rule (simplify (bor ty (ult ty x y) (ult ty y x))) (ne ty x y)) +(rule (simplify (bor ty (ult ty y x) (ult ty x y))) (ne ty x y)) +(rule (simplify (bor ty (ugt ty y x) (ugt ty x y))) (ne ty x y)) +(rule (simplify (bor ty (ugt ty x y) (ugt ty y x))) (ne ty x y)) +(rule (simplify (bor ty (ugt ty y x) (ult ty y x))) (ne ty x y)) +(rule (simplify (bor ty (ult ty y x) (ugt ty y x))) (ne ty x y)) + +; ~((~x) & y) --> x | ~y +(rule (simplify (bnot ty (band ty (bnot ty x) y))) (bor ty x (bnot ty y))) +(rule (simplify (bnot ty (band ty y (bnot ty x)))) (bor ty x (bnot ty y))) + +; ~((~x) | y) --> x & ~y +(rule (simplify (bnot ty (bor ty (bnot ty x) y))) (band ty x (bnot ty y))) +(rule (simplify (bnot ty (bor ty y (bnot ty x)))) (band ty x (bnot ty y))) + +; (x <_u y) | (x == y) --> (x <=_u y) +(rule (simplify (bor ty (ult ty x y) (eq ty x y))) (ule ty x y)) +(rule (simplify (bor ty (eq ty x y) (ult ty x y))) (ule ty x y)) +(rule (simplify (bor ty (ult ty x y) (eq ty y x))) (ule ty x y)) +(rule (simplify (bor ty (eq ty y x) (ult ty x y))) (ule ty x y)) + +; (y >_u x) | (x == y) --> (x <=_u y) +(rule (simplify (bor ty (ugt ty y x) (eq ty x y))) (ule ty x y)) +(rule (simplify (bor ty (eq ty x y) (ugt ty y x))) (ule ty x y)) +(rule (simplify (bor ty (ugt ty y x) (eq ty y x))) (ule ty x y)) +(rule (simplify (bor ty (eq ty y x) (ugt ty y x))) (ule ty x y)) + +; (x < y) | (x == y) --> (x <= y) +(rule (simplify (bor ty (slt ty x y) (eq ty x y))) (sle ty x y)) +(rule (simplify (bor ty (eq ty x y) (slt ty x y))) (sle ty x y)) +(rule (simplify (bor ty (slt ty x y) (eq ty y x))) (sle ty x y)) +(rule (simplify (bor ty (eq ty y x) (slt ty x y))) (sle ty x y)) + +; (y > x) | (x == y) --> (x <= y) +(rule (simplify (bor ty (sgt ty y x) (eq ty x y))) (sle ty x y)) +(rule (simplify (bor ty (eq ty x y) (sgt ty y x))) (sle ty x y)) +(rule (simplify (bor ty (sgt ty y x) (eq ty y x))) (sle ty x y)) +(rule (simplify (bor ty (eq ty y x) (sgt ty y x))) (sle ty x y)) + +; (x ^ z) | (((x ^ z) ^ y)) --> (x ^ z) | y +(rule (simplify (bor ty (bxor ty x z) (bxor ty (bxor ty x z) y))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty (bxor ty x z) y) (bxor ty x z))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty x z) (bxor ty y (bxor ty x z)))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty y (bxor ty x z)) (bxor ty x z))) (bor ty (bxor ty x z) y)) + +(rule (simplify (bor ty (bxor ty x z) (bxor ty (bxor ty z x) y))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty (bxor ty z x) y) (bxor ty x z))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty x z) (bxor ty y (bxor ty z x)))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty y (bxor ty z x)) (bxor ty x z))) (bor ty (bxor ty x z) y)) + +(rule (simplify (bor ty (bxor ty z x) (bxor ty (bxor ty x z) y))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty (bxor ty x z) y) (bxor ty z x))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty z x) (bxor ty y (bxor ty x z)))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty y (bxor ty x z)) (bxor ty z x))) (bor ty (bxor ty x z) y)) + +(rule (simplify (bor ty (bxor ty z x) (bxor ty (bxor ty z x) y))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty (bxor ty z x) y) (bxor ty z x))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty z x) (bxor ty y (bxor ty z x)))) (bor ty (bxor ty x z) y)) +(rule (simplify (bor ty (bxor ty y (bxor ty z x)) (bxor ty z x))) (bor ty (bxor ty x z) y)) + +; x | (x ^ y) --> x | y +(rule (simplify (bor ty x (bxor ty x y))) (bor ty x y)) +(rule (simplify (bor ty (bxor ty x y) x)) (bor ty x y)) +(rule (simplify (bor ty x (bxor ty y x))) (bor ty x y)) +(rule (simplify (bor ty (bxor ty y x) x)) (bor ty x y)) + +; ~((~x) + y) --> x - y +(rule (simplify (bnot ty (iadd ty (bnot ty x) y))) (isub ty x y)) +(rule (simplify (bnot ty (iadd ty y (bnot ty x)))) (isub ty x y)) + +; (x ^ z) | (y | x) --> (y | x) | z +(rule (simplify (bor ty (bxor ty x z) (bor ty y x))) (bor ty (bor ty y x) z)) +(rule (simplify (bor ty (bor ty y x) (bxor ty x z))) (bor ty (bor ty y x) z)) +(rule (simplify (bor ty (bxor ty x z) (bor ty x y))) (bor ty (bor ty y x) z)) +(rule (simplify (bor ty (bor ty x y) (bxor ty x z))) (bor ty (bor ty y x) z)) +(rule (simplify (bor ty (bxor ty z x) (bor ty y x))) (bor ty (bor ty y x) z)) +(rule (simplify (bor ty (bor ty y x) (bxor ty z x))) (bor ty (bor ty y x) z)) +(rule (simplify (bor ty (bxor ty z x) (bor ty x y))) (bor ty (bor ty y x) z)) +(rule (simplify (bor ty (bor ty x y) (bxor ty z x))) (bor ty (bor ty y x) z)) + +; (x | y) ^ (x ^ y) --> x & y +(rule (simplify (bxor ty (bor ty y x) (bxor ty y x))) (band ty x y)) +(rule (simplify (bxor ty (bxor ty y x) (bor ty y x))) (band ty x y)) +(rule (simplify (bxor ty (bor ty y x) (bxor ty x y))) (band ty x y)) +(rule (simplify (bxor ty (bxor ty x y) (bor ty y x))) (band ty x y)) +(rule (simplify (bxor ty (bor ty x y) (bxor ty y x))) (band ty x y)) +(rule (simplify (bxor ty (bxor ty y x) (bor ty x y))) (band ty x y)) +(rule (simplify (bxor ty (bor ty x y) (bxor ty x y))) (band ty x y)) +(rule (simplify (bxor ty (bxor ty x y) (bor ty x y))) (band ty x y)) + +; x | (z & (x ^ y)) --> x | (z & y) +(rule (simplify (bor ty (band ty (bxor ty y x) z) x)) (bor ty (band ty y z) x)) +(rule (simplify (bor ty x (band ty (bxor ty y x) z))) (bor ty (band ty y z) x)) +(rule (simplify (bor ty (band ty z (bxor ty y x)) x)) (bor ty (band ty y z) x)) +(rule (simplify (bor ty x (band ty z (bxor ty y x)))) (bor ty (band ty y z) x)) +(rule (simplify (bor ty (band ty (bxor ty x y) z) x)) (bor ty (band ty y z) x)) +(rule (simplify (bor ty x (band ty (bxor ty x y) z))) (bor ty (band ty y z) x)) +(rule (simplify (bor ty (band ty z (bxor ty x y)) x)) (bor ty (band ty y z) x)) +(rule (simplify (bor ty x (band ty z (bxor ty x y)))) (bor ty (band ty y z) x)) + +; (x | y) | (x ^ z) --> (x | y) | z +(rule (simplify (bor ty (bor ty x y) (bxor ty x z))) (bor ty (bor ty x y) z)) +(rule (simplify (bor ty (bxor ty x z) (bor ty x y))) (bor ty (bor ty x y) z)) +(rule (simplify (bor ty (bor ty x y) (bxor ty z x))) (bor ty (bor ty x y) z)) +(rule (simplify (bor ty (bxor ty z x) (bor ty x y))) (bor ty (bor ty x y) z)) +(rule (simplify (bor ty (bor ty y x) (bxor ty x z))) (bor ty (bor ty x y) z)) +(rule (simplify (bor ty (bxor ty x z) (bor ty y x))) (bor ty (bor ty x y) z)) +(rule (simplify (bor ty (bor ty y x) (bxor ty z x))) (bor ty (bor ty x y) z)) +(rule (simplify (bor ty (bxor ty z x) (bor ty y x))) (bor ty (bor ty x y) z)) + +; (x ^ z) != (y ^ z) --> x != y +(rule (simplify (ne ty (bxor cty x z) (bxor cty y z))) (ne ty x y)) +(rule (simplify (ne ty (bxor cty y z) (bxor cty x z))) (ne ty x y)) +(rule (simplify (ne ty (bxor cty x z) (bxor cty z y))) (ne ty x y)) +(rule (simplify (ne ty (bxor cty z y) (bxor cty x z))) (ne ty x y)) +(rule (simplify (ne ty (bxor cty z x) (bxor cty y z))) (ne ty x y)) +(rule (simplify (ne ty (bxor cty y z) (bxor cty z x))) (ne ty x y)) +(rule (simplify (ne ty (bxor cty z x) (bxor cty z y))) (ne ty x y)) +(rule (simplify (ne ty (bxor cty z y) (bxor cty z x))) (ne ty x y)) + +; (x & y) | (x & ~y) --> x +(rule (simplify (bor ty (band ty x y) (band ty x (bnot ty y)))) x) +(rule (simplify (bor ty (band ty x (bnot ty y)) (band ty x y))) x) +(rule (simplify (bor ty (band ty x y) (band ty (bnot ty y) x))) x) +(rule (simplify (bor ty (band ty (bnot ty y) x) (band ty x y))) x) +(rule (simplify (bor ty (band ty y x) (band ty x (bnot ty y)))) x) +(rule (simplify (bor ty (band ty x (bnot ty y)) (band ty y x))) x) +(rule (simplify (bor ty (band ty y x) (band ty (bnot ty y) x))) x) +(rule (simplify (bor ty (band ty (bnot ty y) x) (band ty y x))) x) + +; (x | y) & (x | ~y) --> x +(rule (simplify (band ty (bor ty x y) (bor ty x (bnot ty y)))) x) +(rule (simplify (band ty (bor ty x (bnot ty y)) (bor ty x y))) x) +(rule (simplify (band ty (bor ty x y) (bor ty (bnot ty y) x))) x) +(rule (simplify (band ty (bor ty (bnot ty y) x) (bor ty x y))) x) +(rule (simplify (band ty (bor ty y x) (bor ty x (bnot ty y)))) x) +(rule (simplify (band ty (bor ty x (bnot ty y)) (bor ty y x))) x) +(rule (simplify (band ty (bor ty y x) (bor ty (bnot ty y) x))) x) +(rule (simplify (band ty (bor ty (bnot ty y) x) (bor ty y x))) x) + +; (x ^ y) | ~x --> ~(x & y) +(rule (simplify (bor ty (bxor ty y x) (bnot ty x))) (bnot ty (band ty y x))) +(rule (simplify (bor ty (bnot ty x) (bxor ty y x))) (bnot ty (band ty y x))) +(rule (simplify (bor ty (bxor ty x y) (bnot ty x))) (bnot ty (band ty y x))) +(rule (simplify (bor ty (bnot ty x) (bxor ty x y))) (bnot ty (band ty y x))) + +; (x ^ y) ^ x --> y +(rule (simplify (bxor ty (bxor ty x y) x)) y) +(rule (simplify (bxor ty x (bxor ty x y))) y) +(rule (simplify (bxor ty (bxor ty y x) x)) y) +(rule (simplify (bxor ty x (bxor ty y x))) y) + +; y | (x & (y | z)) --> y | (x & z) +(rule (simplify (bor ty (band ty x (bor ty y z)) y)) (bor ty (band ty x z) y)) +(rule (simplify (bor ty y (band ty x (bor ty y z)))) (bor ty (band ty x z) y)) +(rule (simplify (bor ty (band ty (bor ty y z) x) y)) (bor ty (band ty x z) y)) +(rule (simplify (bor ty y (band ty (bor ty y z) x))) (bor ty (band ty x z) y)) +(rule (simplify (bor ty (band ty x (bor ty z y)) y)) (bor ty (band ty x z) y)) +(rule (simplify (bor ty y (band ty x (bor ty z y)))) (bor ty (band ty x z) y)) +(rule (simplify (bor ty (band ty (bor ty z y) x) y)) (bor ty (band ty x z) y)) +(rule (simplify (bor ty y (band ty (bor ty z y) x))) (bor ty (band ty x z) y)) + +; y & (x | (y & z)) --> y & (x | z) +(rule (simplify (band ty (bor ty x (band ty y z)) y)) (band ty (bor ty x z) y)) +(rule (simplify (band ty y (bor ty x (band ty y z)))) (band ty (bor ty x z) y)) +(rule (simplify (band ty (bor ty (band ty y z) x) y)) (band ty (bor ty x z) y)) +(rule (simplify (band ty y (bor ty (band ty y z) x))) (band ty (bor ty x z) y)) +(rule (simplify (band ty (bor ty x (band ty z y)) y)) (band ty (bor ty x z) y)) +(rule (simplify (band ty y (bor ty x (band ty z y)))) (band ty (bor ty x z) y)) +(rule (simplify (band ty (bor ty (band ty z y) x) y)) (band ty (bor ty x z) y)) +(rule (simplify (band ty y (bor ty (band ty z y) x))) (band ty (bor ty x z) y)) + +; y | (x ^ (y & z)) --> x | y +(rule (simplify (bor ty (bxor ty x (band ty y z)) y)) (bor ty x y)) +(rule (simplify (bor ty y (bxor ty x (band ty y z)))) (bor ty x y)) +(rule (simplify (bor ty (bxor ty (band ty y z) x) y)) (bor ty x y)) +(rule (simplify (bor ty y (bxor ty (band ty y z) x))) (bor ty x y)) +(rule (simplify (bor ty (bxor ty x (band ty z y)) y)) (bor ty x y)) +(rule (simplify (bor ty y (bxor ty x (band ty z y)))) (bor ty x y)) +(rule (simplify (bor ty (bxor ty (band ty z y) x) y)) (bor ty x y)) +(rule (simplify (bor ty y (bxor ty (band ty z y) x))) (bor ty x y)) + +; ((x & y) ^ y) + z --> (y + z) - (x & y) +(rule (simplify (iadd ty (bxor ty (band ty x y) y) z)) (isub ty (iadd ty y z) (band ty x y))) +(rule (simplify (iadd ty z (bxor ty (band ty x y) y))) (isub ty (iadd ty y z) (band ty x y))) +(rule (simplify (iadd ty (bxor ty y (band ty x y)) z)) (isub ty (iadd ty y z) (band ty x y))) +(rule (simplify (iadd ty z (bxor ty y (band ty x y)))) (isub ty (iadd ty y z) (band ty x y))) +(rule (simplify (iadd ty (bxor ty (band ty y x) y) z)) (isub ty (iadd ty y z) (band ty x y))) +(rule (simplify (iadd ty z (bxor ty (band ty y x) y))) (isub ty (iadd ty y z) (band ty x y))) +(rule (simplify (iadd ty (bxor ty y (band ty y x)) z)) (isub ty (iadd ty y z) (band ty x y))) +(rule (simplify (iadd ty z (bxor ty y (band ty y x)))) (isub ty (iadd ty y z) (band ty x y))) + +; y & (~y | x) --> y & x +(rule (simplify (band ty y (bor ty (bnot ty y) x))) (band ty y x)) +(rule (simplify (band ty (bor ty (bnot ty y) x) y)) (band ty y x)) +(rule (simplify (band ty y (bor ty x (bnot ty y)))) (band ty y x)) +(rule (simplify (band ty (bor ty x (bnot ty y)) y)) (band ty y x)) + +; y | (~y & x) --> y | x +(rule (simplify (bor ty y (band ty (bnot ty y) x))) (bor ty y x)) +(rule (simplify (bor ty (band ty (bnot ty y) x) y)) (bor ty y x)) +(rule (simplify (bor ty y (band ty x (bnot ty y)))) (bor ty y x)) +(rule (simplify (bor ty (band ty x (bnot ty y)) y)) (bor ty y x)) + +;; ((x & ~y) - (x & y)) --> ((x ^ y) - y) +(rule (simplify (isub ty (band ty x (bnot ty y)) (band ty x y))) (isub ty (bxor ty x y) y)) +(rule (simplify (isub ty (band ty x (bnot ty y)) (band ty y x))) (isub ty (bxor ty x y) y)) +(rule (simplify (isub ty (band ty (bnot ty y) x) (band ty x y))) (isub ty (bxor ty x y) y)) +(rule (simplify (isub ty (band ty (bnot ty y) x) (band ty y x))) (isub ty (bxor ty x y) y)) +(rule (simplify (isub ty (band ty x y) (band ty x (bnot ty y)))) (isub ty y (bxor ty x y))) +(rule (simplify (isub ty (band ty x y) (band ty (bnot ty y) x))) (isub ty y (bxor ty x y))) +(rule (simplify (isub ty (band ty y x) (band ty x (bnot ty y)))) (isub ty y (bxor ty x y))) +(rule (simplify (isub ty (band ty y x) (band ty (bnot ty y) x))) (isub ty y (bxor ty x y))) + +;; (x & ~y) | (~x & y) --> (x ^ y) +(rule (simplify (bor ty (band ty x (bnot ty y)) (band ty (bnot ty x) y))) (bxor ty x y)) +(rule (simplify (bor ty (band ty (bnot ty x) y) (band ty x (bnot ty y)))) (bxor ty x y)) +(rule (simplify (bor ty (band ty x (bnot ty y)) (band ty y (bnot ty x)))) (bxor ty x y)) +(rule (simplify (bor ty (band ty y (bnot ty x)) (band ty x (bnot ty y)))) (bxor ty x y)) +(rule (simplify (bor ty (band ty (bnot ty y) x) (band ty (bnot ty x) y))) (bxor ty x y)) +(rule (simplify (bor ty (band ty (bnot ty x) y) (band ty (bnot ty y) x))) (bxor ty x y)) +(rule (simplify (bor ty (band ty (bnot ty y) x) (band ty y (bnot ty x)))) (bxor ty x y)) +(rule (simplify (bor ty (band ty y (bnot ty x)) (band ty (bnot ty y) x))) (bxor ty x y)) + +;; (x & ~y) | (x ^ y) --> (x ^ y) +(rule (simplify (bor ty (band ty x (bnot ty y)) (bxor ty x y))) (bxor ty x y)) +(rule (simplify (bor ty (bxor ty x y) (band ty x (bnot ty y)))) (bxor ty x y)) +(rule (simplify (bor ty (band ty x (bnot ty y)) (bxor ty y x))) (bxor ty x y)) +(rule (simplify (bor ty (bxor ty y x) (band ty x (bnot ty y)))) (bxor ty x y)) +(rule (simplify (bor ty (band ty (bnot ty y) x) (bxor ty x y))) (bxor ty x y)) +(rule (simplify (bor ty (bxor ty x y) (band ty (bnot ty y) x))) (bxor ty x y)) +(rule (simplify (bor ty (band ty (bnot ty y) x) (bxor ty y x))) (bxor ty x y)) +(rule (simplify (bor ty (bxor ty y x) (band ty (bnot ty y) x))) (bxor ty x y)) + +;; (x & ~y) ^ ~x --> ~(x & y) +(rule (simplify (bxor ty (band ty x (bnot ty y)) (bnot ty x))) (bnot ty (band ty x y))) +(rule (simplify (bxor ty (bnot ty x) (band ty x (bnot ty y)))) (bnot ty (band ty x y))) +(rule (simplify (bxor ty (band ty (bnot ty y) x) (bnot ty x))) (bnot ty (band ty x y))) +(rule (simplify (bxor ty (bnot ty x) (band ty (bnot ty y) x))) (bnot ty (band ty x y))) + +;; (~x & y) ^ x --> x | y +(rule (simplify (bxor ty (band ty (bnot ty x) y) x)) (bor ty x y)) +(rule (simplify (bxor ty x (band ty (bnot ty x) y))) (bor ty x y)) +(rule (simplify (bxor ty (band ty y (bnot ty x)) x)) (bor ty x y)) +(rule (simplify (bxor ty x (band ty y (bnot ty x)))) (bor ty x y)) + +;; (x | y) & ~(x ^ y) --> x & y +(rule (simplify (band ty (bor ty x y) (bnot ty (bxor ty x y)))) (band ty x y)) +(rule (simplify (band ty (bnot ty (bxor ty x y)) (bor ty x y))) (band ty x y)) +(rule (simplify (band ty (bor ty x y) (bnot ty (bxor ty y x)))) (band ty x y)) +(rule (simplify (band ty (bnot ty (bxor ty y x)) (bor ty x y))) (band ty x y)) +(rule (simplify (band ty (bor ty y x) (bnot ty (bxor ty x y)))) (band ty x y)) +(rule (simplify (band ty (bnot ty (bxor ty x y)) (bor ty y x))) (band ty x y)) +(rule (simplify (band ty (bor ty y x) (bnot ty (bxor ty y x)))) (band ty x y)) +(rule (simplify (band ty (bnot ty (bxor ty y x)) (bor ty y x))) (band ty x y)) + +;; x | ~(x ^ y) --> x | ~y +(rule (simplify (bor ty x (bnot ty (bxor ty x y)))) (bor ty x (bnot ty y))) +(rule (simplify (bor ty (bnot ty (bxor ty x y)) x)) (bor ty x (bnot ty y))) +(rule (simplify (bor ty x (bnot ty (bxor ty y x)))) (bor ty x (bnot ty y))) +(rule (simplify (bor ty (bnot ty (bxor ty y x)) x)) (bor ty x (bnot ty y))) + +;; x | ((~x) ^ y) --> x | ~y +(rule (simplify (bor ty x (bxor ty (bnot ty x) y))) (bor ty x (bnot ty y))) +(rule (simplify (bor ty (bxor ty (bnot ty x) y) x)) (bor ty x (bnot ty y))) +(rule (simplify (bor ty x (bxor ty y (bnot ty x)))) (bor ty x (bnot ty y))) +(rule (simplify (bor ty (bxor ty y (bnot ty x)) x)) (bor ty x (bnot ty y))) + +;; x & ~(x ^ y) --> x & y +(rule (simplify (band ty x (bnot ty (bxor ty x y)))) (band ty x y)) +(rule (simplify (band ty (bnot ty (bxor ty x y)) x)) (band ty x y)) +(rule (simplify (band ty x (bnot ty (bxor ty y x)))) (band ty x y)) +(rule (simplify (band ty (bnot ty (bxor ty y x)) x)) (band ty x y)) + +;; x & ((~x) ^ y) --> x & y +(rule (simplify (band ty x (bxor ty (bnot ty x) y))) (band ty x y)) +(rule (simplify (band ty (bxor ty (bnot ty x) y) x)) (band ty x y)) +(rule (simplify (band ty x (bxor ty y (bnot ty x)))) (band ty x y)) +(rule (simplify (band ty (bxor ty y (bnot ty x)) x)) (band ty x y)) + +;; (x | y) | (x ^ y) --> (x | y) +(rule (simplify (bor ty (bor ty x y) (bxor ty x y))) (bor ty x y)) +(rule (simplify (bor ty (bxor ty x y) (bor ty x y))) (bor ty x y)) +(rule (simplify (bor ty (bor ty x y) (bxor ty y x))) (bor ty x y)) +(rule (simplify (bor ty (bxor ty y x) (bor ty x y))) (bor ty x y)) +(rule (simplify (bor ty (bor ty y x) (bxor ty x y))) (bor ty x y)) +(rule (simplify (bor ty (bxor ty x y) (bor ty y x))) (bor ty x y)) +(rule (simplify (bor ty (bor ty y x) (bxor ty y x))) (bor ty x y)) +(rule (simplify (bor ty (bxor ty y x) (bor ty y x))) (bor ty x y)) + +;; (x ^ y) & (x ^ (y ^ z)) --> (x ^ y) & ~z +(rule (simplify (band ty (bxor ty x y) (bxor ty (bxor ty y z) x))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty (bxor ty y z) x) (bxor ty x y))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty x y) (bxor ty x (bxor ty y z)))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty x (bxor ty y z)) (bxor ty x y))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty x y) (bxor ty (bxor ty z y) x))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty (bxor ty z y) x) (bxor ty x y))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty x y) (bxor ty x (bxor ty z y)))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty x (bxor ty z y)) (bxor ty x y))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty y x) (bxor ty (bxor ty y z) x))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty (bxor ty y z) x) (bxor ty y x))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty y x) (bxor ty x (bxor ty y z)))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty x (bxor ty y z)) (bxor ty y x))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty y x) (bxor ty (bxor ty z y) x))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty (bxor ty z y) x) (bxor ty y x))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty y x) (bxor ty x (bxor ty z y)))) (band ty (bxor ty x y) (bnot ty z))) +(rule (simplify (band ty (bxor ty x (bxor ty z y)) (bxor ty y x))) (band ty (bxor ty x y) (bnot ty z))) + +;; (~x & y) ^ z --> (x & y) ^ (z ^ y) +(rule (simplify (bxor ty (band ty (bnot ty x) y) z)) (bxor ty (band ty x y) (bxor ty z y))) +(rule (simplify (bxor ty z (band ty (bnot ty x) y))) (bxor ty (band ty x y) (bxor ty z y))) +(rule (simplify (bxor ty (band ty y (bnot ty x)) z)) (bxor ty (band ty x y) (bxor ty z y))) +(rule (simplify (bxor ty z (band ty y (bnot ty x)))) (bxor ty (band ty x y) (bxor ty z y))) + +;; ~x - ~y --> y - x +(rule (simplify (isub ty (bnot ty x) (bnot ty y))) (isub ty y x)) + +;; (~x & y) | ~(x | y) --> ~x +(rule (simplify (bor ty (band ty (bnot ty x) y) (bnot ty (bor ty x y)))) (bnot ty x)) +(rule (simplify (bor ty (bnot ty (bor ty x y)) (band ty (bnot ty x) y))) (bnot ty x)) +(rule (simplify (bor ty (band ty (bnot ty x) y) (bnot ty (bor ty y x)))) (bnot ty x)) +(rule (simplify (bor ty (bnot ty (bor ty y x)) (band ty (bnot ty x) y))) (bnot ty x)) +(rule (simplify (bor ty (band ty y (bnot ty x)) (bnot ty (bor ty x y)))) (bnot ty x)) +(rule (simplify (bor ty (bnot ty (bor ty x y)) (band ty y (bnot ty x)))) (bnot ty x)) +(rule (simplify (bor ty (band ty y (bnot ty x)) (bnot ty (bor ty y x)))) (bnot ty x)) +(rule (simplify (bor ty (bnot ty (bor ty y x)) (band ty y (bnot ty x)))) (bnot ty x)) + +;; (~x | y) & ~(x & y) --> ~x +(rule (simplify (band ty (bor ty (bnot ty x) y) (bnot ty (band ty x y)))) (bnot ty x)) +(rule (simplify (band ty (bnot ty (band ty x y)) (bor ty (bnot ty x) y))) (bnot ty x)) +(rule (simplify (band ty (bor ty (bnot ty x) y) (bnot ty (band ty y x)))) (bnot ty x)) +(rule (simplify (band ty (bnot ty (band ty y x)) (bor ty (bnot ty x) y))) (bnot ty x)) +(rule (simplify (band ty (bor ty y (bnot ty x)) (bnot ty (band ty x y)))) (bnot ty x)) +(rule (simplify (band ty (bnot ty (band ty x y)) (bor ty y (bnot ty x)))) (bnot ty x)) +(rule (simplify (band ty (bor ty y (bnot ty x)) (bnot ty (band ty y x)))) (bnot ty x)) +(rule (simplify (band ty (bnot ty (band ty y x)) (bor ty y (bnot ty x)))) (bnot ty x)) + +;; (x & y) | ~(x | y) --> ~(x ^ y) +(rule (simplify (bor ty (band ty x y) (bnot ty (bor ty x y)))) (bnot ty (bxor ty x y))) +(rule (simplify (bor ty (bnot ty (bor ty x y)) (band ty x y))) (bnot ty (bxor ty x y))) +(rule (simplify (bor ty (band ty x y) (bnot ty (bor ty y x)))) (bnot ty (bxor ty x y))) +(rule (simplify (bor ty (bnot ty (bor ty y x)) (band ty x y))) (bnot ty (bxor ty x y))) +(rule (simplify (bor ty (band ty y x) (bnot ty (bor ty x y)))) (bnot ty (bxor ty x y))) +(rule (simplify (bor ty (bnot ty (bor ty x y)) (band ty y x))) (bnot ty (bxor ty x y))) +(rule (simplify (bor ty (band ty y x) (bnot ty (bor ty y x)))) (bnot ty (bxor ty x y))) +(rule (simplify (bor ty (bnot ty (bor ty y x)) (band ty y x))) (bnot ty (bxor ty x y))) + +;; (~x | y) ^ (x ^ y) --> x | ~y +(rule (simplify (bxor ty (bor ty (bnot ty x) y) (bxor ty x y))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bxor ty x y) (bor ty (bnot ty x) y))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bor ty (bnot ty x) y) (bxor ty y x))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bxor ty y x) (bor ty (bnot ty x) y))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bor ty y (bnot ty x)) (bxor ty x y))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bxor ty x y) (bor ty y (bnot ty x)))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bor ty y (bnot ty x)) (bxor ty y x))) (bor ty x (bnot ty y))) +(rule (simplify (bxor ty (bxor ty y x) (bor ty y (bnot ty x)))) (bor ty x (bnot ty y))) \ No newline at end of file diff --git a/cranelift/codegen/src/opts/cprop.isle b/cranelift/codegen/src/opts/cprop.isle index e6ec5c84db40..81a6273a08b8 100644 --- a/cranelift/codegen/src/opts/cprop.isle +++ b/cranelift/codegen/src/opts/cprop.isle @@ -91,6 +91,17 @@ (iconst _ k2))) (subsume (iconst ty (imm64_sshr ty k1 k2)))) +(rule (simplify (rotr (fits_in_64 ty) + (iconst ty k1) + (iconst _ k2))) + (subsume (iconst ty (imm64_rotr ty k1 k2)))) + +(rule (simplify (rotl (fits_in_64 ty) + (iconst ty k1) + (iconst _ k2))) + (subsume (iconst ty (imm64_rotl ty k1 k2)))) + + (rule (simplify (ireduce narrow (iconst (fits_in_64 _) (u64_from_imm64 imm)))) (subsume (iconst narrow (imm64_masked narrow imm)))) @@ -392,7 +403,7 @@ (rule (simplify (fmin $F16 (f16const $F16 n) (f16const $F16 m))) (if-let r (f16_min n m)) - (subsume (f16const $F32 r))) + (subsume (f16const $F16 r))) (rule (simplify (fmin $F32 (f32const $F32 n) (f32const $F32 m))) (if-let r (f32_min n m)) (subsume (f32const $F32 r))) diff --git a/cranelift/codegen/src/opts/selects.isle b/cranelift/codegen/src/opts/selects.isle index 1772adb4f903..77b2b8b943cc 100644 --- a/cranelift/codegen/src/opts/selects.isle +++ b/cranelift/codegen/src/opts/selects.isle @@ -112,5 +112,84 @@ (rule (simplify (ult (fits_in_64 ty) x (umin _ x z))) (iconst_u ty 0)) (rule (simplify (ult (fits_in_64 ty) x (umin _ y x))) (iconst_u ty 0)) - - +;; (x == y) ? x : y => y +;; select(eq/ne) patterns that always collapse to y. +(rule (simplify (select ty (eq cty x y) x y)) (subsume y)) +(rule (simplify (select ty (ne cty x y) y x)) (subsume y)) +(rule (simplify (select ty (eq cty y x) x y)) (subsume y)) +(rule (simplify (select ty (ne cty y x) y x)) (subsume y)) + +;; (x | (y != z)) ? y : z ==> y +(rule (simplify (select ty (bor cty x (ne cty y z)) y z)) (subsume y)) +(rule (simplify (select ty (bor cty (ne cty y z) x) y z)) (subsume y)) +(rule (simplify (select ty (bor cty x (ne cty z y)) y z)) (subsume y)) +(rule (simplify (select ty (bor cty (ne cty z y) x) y z)) (subsume y)) + +;; (x == y) ? x : (x | y) ==> (x | y) +;; and equivalent operand-order variants. +(rule (simplify (select ty (eq cty x y) x (bor ty x y))) (bor ty x y)) +(rule (simplify (select ty (ne cty x y) (bor ty x y) x)) (bor ty x y)) +(rule (simplify (select ty (eq cty x y) x (bor ty y x))) (bor ty x y)) +(rule (simplify (select ty (ne cty x y) (bor ty y x) x)) (bor ty x y)) +(rule (simplify (select ty (eq cty y x) x (bor ty x y))) (bor ty x y)) +(rule (simplify (select ty (ne cty y x) (bor ty x y) x)) (bor ty x y)) +(rule (simplify (select ty (eq cty y x) x (bor ty y x))) (bor ty x y)) +(rule (simplify (select ty (ne cty y x) (bor ty y x) x)) (bor ty x y)) + +;; (x != y) ? (x & y) : y ==> (x & y) +;; and equivalent operand-order variants. +(rule (simplify (select ty (ne cty x y) (band ty x y) y)) (band ty x y)) +(rule (simplify (select ty (eq cty x y) y (band ty x y))) (band ty x y)) +(rule (simplify (select ty (ne cty x y) (band ty y x) y)) (band ty x y)) +(rule (simplify (select ty (eq cty x y) y (band ty y x))) (band ty x y)) +(rule (simplify (select ty (ne cty y x) (band ty x y) y)) (band ty x y)) +(rule (simplify (select ty (eq cty y x) y (band ty x y))) (band ty x y)) +(rule (simplify (select ty (ne cty y x) (band ty y x) y)) (band ty x y)) +(rule (simplify (select ty (eq cty y x) y (band ty y x))) (band ty x y)) + +;; (x <_u y) ? 1 : (x == y) ==> (x <=_u y) +;; and equivalent operand-order variants. +(rule (simplify (select ty (ult cty x y) (iconst_u ty 1) (eq ty x y))) (ule ty x y)) +(rule (simplify (select ty (uge cty x y) (eq ty x y) (iconst_u ty 1))) (ule ty x y)) +(rule (simplify (select ty (ugt cty y x) (iconst_u ty 1) (eq ty x y))) (ule ty x y)) +(rule (simplify (select ty (ule cty y x) (eq ty x y) (iconst_u ty 1))) (ule ty x y)) +(rule (simplify (select ty (ult cty x y) (iconst_u ty 1) (eq ty y x))) (ule ty x y)) +(rule (simplify (select ty (uge cty x y) (eq ty y x) (iconst_u ty 1))) (ule ty x y)) +(rule (simplify (select ty (ugt cty y x) (iconst_u ty 1) (eq ty y x))) (ule ty x y)) +(rule (simplify (select ty (ule cty y x) (eq ty y x) (iconst_u ty 1))) (ule ty x y)) + +;; (x < y) ? 1 : (x > y) ==> (x != y) +;; and equivalent operand-order variants. +(rule (simplify (select ty (slt cty x y) (iconst_u ty 1) (sgt ty x y))) (ne ty x y)) +(rule (simplify (select ty (sge cty x y) (sgt ty x y) (iconst_u ty 1))) (ne ty x y)) +(rule (simplify (select ty (sgt cty y x) (iconst_u ty 1) (sgt ty x y))) (ne ty x y)) +(rule (simplify (select ty (sle cty y x) (sgt ty x y) (iconst_u ty 1))) (ne ty x y)) +(rule (simplify (select ty (slt cty x y) (iconst_u ty 1) (slt ty y x))) (ne ty x y)) +(rule (simplify (select ty (sge cty x y) (slt ty y x) (iconst_u ty 1))) (ne ty x y)) +(rule (simplify (select ty (sgt cty y x) (iconst_u ty 1) (slt ty y x))) (ne ty x y)) +(rule (simplify (select ty (sle cty y x) (slt ty y x) (iconst_u ty 1))) (ne ty x y)) +(rule (simplify (select ty (ult cty x y) (iconst_u ty 1) (ugt ty x y))) (ne ty x y)) +(rule (simplify (select ty (uge cty x y) (ugt ty x y) (iconst_u ty 1))) (ne ty x y)) +(rule (simplify (select ty (ugt cty y x) (iconst_u ty 1) (ugt ty x y))) (ne ty x y)) +(rule (simplify (select ty (ule cty y x) (ugt ty x y) (iconst_u ty 1))) (ne ty x y)) +(rule (simplify (select ty (ult cty x y) (iconst_u ty 1) (ult ty y x))) (ne ty x y)) +(rule (simplify (select ty (uge cty x y) (ult ty y x) (iconst_u ty 1))) (ne ty x y)) +(rule (simplify (select ty (ugt cty y x) (iconst_u ty 1) (ult ty y x))) (ne ty x y)) +(rule (simplify (select ty (ule cty y x) (ult ty y x) (iconst_u ty 1))) (ne ty x y)) + +;; (x ? 1 : (x | y)) ==> (x ? 1 : y) +(rule (simplify (select ty x (iconst_u ty 1) (bor ty x y))) (select ty x (iconst_u ty 1) y)) +(rule (simplify (select ty x (iconst_u ty 1) (bor ty y x))) (select ty x (iconst_u ty 1) y)) + +;; if x == n then (y - n) else (y - x) ==> (y - x) +;; and equivalent predicate/operand-order variants. +(rule (simplify (select ty (eq cty x (iconst_u ty n)) (isub ty y (iconst_u ty n)) (isub ty y x))) (isub ty y x)) +(rule (simplify (select ty (ne cty x (iconst_u ty n)) (isub ty y x) (isub ty y (iconst_u ty n)))) (isub ty y x)) +(rule (simplify (select ty (eq cty (iconst_u ty n) x) (isub ty y (iconst_u ty n)) (isub ty y x))) (isub ty y x)) +(rule (simplify (select ty (ne cty (iconst_u ty n) x) (isub ty y x) (isub ty y (iconst_u ty n)))) (isub ty y x)) + +;; select(cond, eq(z, y), eq(p, y)) ==> eq(y, select(cond, z, p)) +(rule (simplify (select ty x (eq ty z y) (eq ty (iconst_u cty p) y))) (eq ty y (select cty x z (iconst_u cty p)))) +(rule (simplify (select ty x (eq ty z y) (eq ty y (iconst_u cty p)))) (eq ty y (select cty x z (iconst_u cty p)))) +(rule (simplify (select ty x (eq ty y z) (eq ty (iconst_u cty p) y))) (eq ty y (select cty x z (iconst_u cty p)))) +(rule (simplify (select ty x (eq ty y z) (eq ty y (iconst_u cty p)))) (eq ty y (select cty x z (iconst_u cty p)))) diff --git a/cranelift/codegen/src/opts/shifts.isle b/cranelift/codegen/src/opts/shifts.isle index 82992c26c6cf..f3cfe7700c86 100644 --- a/cranelift/codegen/src/opts/shifts.isle +++ b/cranelift/codegen/src/opts/shifts.isle @@ -266,6 +266,10 @@ (rule (simplify (rotl ty (rotr ty x y @ (value_type kty)) z @ (value_type kty))) (rotr ty x (isub_uextend y z))) +(rule + (simplify (rotr ty (iconst ty p) (select ty x (iconst ty y) (iconst ty z)))) + (select ty x (iconst ty (imm64_rotr ty p y)) (iconst ty (imm64_rotr ty p z)))) + ;; Convert shifts into rotates. We always normalize into a rotate left. ;; ;; (bor (ishl x k1) (ushr x k2)) == (rotl x k1) if k2 == ty_bits - k1 @@ -311,4 +315,3 @@ (rule (simplify (iadd ty (ishl ty x z) (ishl ty y z))) (ishl ty (iadd ty x y) z)) (rule (simplify (ushr ty (band ty (ishl ty x y) z) y)) (band ty x (ushr ty z y))) - diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle index f6493c7987f7..76a9773f4299 100644 --- a/cranelift/codegen/src/prelude.isle +++ b/cranelift/codegen/src/prelude.isle @@ -85,6 +85,12 @@ (decl pure imm64_sshr (Type Imm64 Imm64) Imm64) (extern constructor imm64_sshr imm64_sshr) +(decl pure imm64_rotl (Type Imm64 Imm64) Imm64) +(extern constructor imm64_rotl imm64_rotl) + +(decl pure imm64_rotr (Type Imm64 Imm64) Imm64) +(extern constructor imm64_rotr imm64_rotr) + ;; Sign extends a u64 from ty bits up to 64bits (decl pure i64_sextend_u64 (Type u64) i64) (extern constructor i64_sextend_u64 i64_sextend_u64) diff --git a/cranelift/codegen/src/prelude_lower.isle b/cranelift/codegen/src/prelude_lower.isle index f1f492bddb36..8200c814c9ad 100644 --- a/cranelift/codegen/src/prelude_lower.isle +++ b/cranelift/codegen/src/prelude_lower.isle @@ -136,11 +136,6 @@ (decl put_in_regs_vec (ValueSlice) ValueRegsVec) (extern constructor put_in_regs_vec put_in_regs_vec) -;; If the given reg is a real register, cause the value in reg to be in a virtual -;; reg, by copying it into a new virtual reg. -(decl ensure_in_vreg (Reg Type) Reg) -(extern constructor ensure_in_vreg ensure_in_vreg) - ;; Get the `n`th register inside a `ValueRegs`. (spec (value_regs_get arg i) (provide (= arg result) (= (widthof i) 1))) @@ -184,11 +179,6 @@ (rule (multi_reg_to_single (MultiReg.One a)) (value_reg a)) -;; Add a range fact to a register, when compiling with -;; proof-carrying-code enabled. -(decl add_range_fact (Reg u16 u64 u64) Reg) -(extern constructor add_range_fact add_range_fact) - ;;;; Common Mach Types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (type MachLabel (primitive MachLabel)) @@ -272,7 +262,7 @@ (extern extractor first_result first_result) ;; Extract the `InstructionData` for an `Inst`. -(decl inst_data_value (InstructionData) Inst) +(decl inst_data_value (Type InstructionData) Inst) (extern extractor infallible inst_data_value inst_data_value) ;; Extract the type of the instruction's first result. @@ -306,7 +296,7 @@ (spec (u64_from_iconst arg) (provide (= arg (zero_ext 64 result)))) (decl u64_from_iconst (u64) Value) (extractor (u64_from_iconst x) - (def_inst (iconst (u64_from_imm64 x)))) + (def_inst (iconst _ (u64_from_imm64 x)))) (decl i8_from_iconst (i8) Value) (extractor (i8_from_iconst x) @@ -358,13 +348,13 @@ (extractor (ne x y) (icmp (IntCC.NotEqual) x y)) (decl ult (Value Value) Value) -(extractor (ult x y) (icmp (IntCC.UnsignedLessThan) x y)) +(extractor (ult x y) (icmp _ (IntCC.UnsignedLessThan) x y)) (decl ule (Value Value) Value) (extractor (ule x y) (icmp (IntCC.UnsignedLessThanOrEqual) x y)) (decl ugt (Value Value) Value) -(extractor (ugt x y) (icmp (IntCC.UnsignedGreaterThan) x y)) +(extractor (ugt x y) (icmp _ (IntCC.UnsignedGreaterThan) x y)) (decl uge (Value Value) Value) (extractor (uge x y) (icmp (IntCC.UnsignedGreaterThanOrEqual) x y)) diff --git a/cranelift/codegen/src/prelude_opt.isle b/cranelift/codegen/src/prelude_opt.isle index 5508f3b352e0..509e2edb5376 100644 --- a/cranelift/codegen/src/prelude_opt.isle +++ b/cranelift/codegen/src/prelude_opt.isle @@ -216,7 +216,7 @@ (decl sge (Type Value Value) Value) (extractor (sge ty x y) (icmp ty (IntCC.SignedGreaterThanOrEqual) x y)) -;;;;;; Divison-By-Constant Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;; Division-By-Constant Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (decl pure i64_is_negative_power_of_two (i64) bool) (rule (i64_is_negative_power_of_two x) diff --git a/cranelift/codegen/src/print_errors.rs b/cranelift/codegen/src/print_errors.rs index 21a2767267e8..ee9bc788385e 100644 --- a/cranelift/codegen/src/print_errors.rs +++ b/cranelift/codegen/src/print_errors.rs @@ -4,7 +4,6 @@ use crate::entity::SecondaryMap; use crate::ir; use crate::ir::entities::{AnyEntity, Block, Inst, Value}; use crate::ir::function::Function; -use crate::ir::pcc::Fact; use crate::result::CodegenError; use crate::verifier::{VerifierError, VerifierErrors}; use crate::write::{FuncWriter, PlainWriter, decorate_function}; @@ -72,9 +71,8 @@ impl<'a> FuncWriter for PrettyVerifierError<'a> { func: &Function, entity: AnyEntity, value: &dyn fmt::Display, - maybe_fact: Option<&Fact>, ) -> fmt::Result { - pretty_preamble_error(w, func, entity, value, maybe_fact, &mut *self.0, self.1) + pretty_preamble_error(w, func, entity, value, &mut *self.0, self.1) } } @@ -158,12 +156,11 @@ fn pretty_preamble_error( func: &Function, entity: AnyEntity, value: &dyn fmt::Display, - maybe_fact: Option<&Fact>, func_w: &mut dyn FuncWriter, errors: &mut Vec, ) -> fmt::Result { let mut s = String::new(); - func_w.write_entity_definition(&mut s, func, entity, value, maybe_fact)?; + func_w.write_entity_definition(&mut s, func, entity, value)?; write!(w, "{s}")?; // TODO: Use drain_filter here when it gets stabilized diff --git a/cranelift/codegen/src/result.rs b/cranelift/codegen/src/result.rs index c62572cd9958..da16cb6d3d05 100644 --- a/cranelift/codegen/src/result.rs +++ b/cranelift/codegen/src/result.rs @@ -1,10 +1,8 @@ //! Result and error types representing the outcome of compiling a function. -use regalloc2::checker::CheckerErrors; - -use crate::ir::pcc::PccError; use crate::{ir::Function, verifier::VerifierErrors}; use alloc::string::String; +use regalloc2::checker::CheckerErrors; /// A compilation error. /// @@ -42,9 +40,6 @@ pub enum CodegenError { /// Register allocator internal error discovered by the symbolic checker. Regalloc(CheckerErrors), - - /// Proof-carrying-code validation error. - Pcc(PccError), } /// A convenient alias for a `Result` that uses `CodegenError` as the error type. @@ -62,7 +57,6 @@ impl core::error::Error for CodegenError { #[cfg(feature = "unwind")] CodegenError::RegisterMappingError { .. } => None, CodegenError::Regalloc(..) => None, - CodegenError::Pcc(..) => None, } } } @@ -77,10 +71,6 @@ impl core::fmt::Display for CodegenError { #[cfg(feature = "unwind")] CodegenError::RegisterMappingError(_0) => write!(f, "Register mapping error"), CodegenError::Regalloc(errors) => write!(f, "Regalloc validation errors: {errors:?}"), - - // NOTE: if this is changed, please update the `is_pcc_error` function defined in - // `wasmtime/crates/fuzzing/src/oracles.rs` - CodegenError::Pcc(e) => write!(f, "Proof-carrying-code validation error: {e:?}"), } } } diff --git a/cranelift/codegen/src/settings.rs b/cranelift/codegen/src/settings.rs index 158ba139bd42..2c74fa9c994d 100644 --- a/cranelift/codegen/src/settings.rs +++ b/cranelift/codegen/src/settings.rs @@ -500,7 +500,6 @@ regalloc_checker = false regalloc_verbose_logs = false enable_alias_analysis = true enable_verifier = true -enable_pcc = false is_pic = false use_colocated_libcalls = false enable_nan_canonicalization = false diff --git a/cranelift/codegen/src/verifier/mod.rs b/cranelift/codegen/src/verifier/mod.rs index 71dc457d8b85..5fca9a68d94d 100644 --- a/cranelift/codegen/src/verifier/mod.rs +++ b/cranelift/codegen/src/verifier/mod.rs @@ -72,8 +72,7 @@ use crate::ir::instructions::{CallInfo, InstructionFormat, ResolvedConstraint}; use crate::ir::{self, ArgumentExtension, BlockArg, ExceptionTable}; use crate::ir::{ ArgumentPurpose, Block, Constant, DynamicStackSlot, FuncRef, Function, GlobalValue, Inst, - JumpTable, MemFlags, MemoryTypeData, Opcode, SigRef, StackSlot, Type, Value, ValueDef, - ValueList, types, + JumpTable, MemFlags, Opcode, SigRef, StackSlot, Type, Value, ValueDef, ValueList, types, }; use crate::ir::{ExceptionTableItem, Signature}; use crate::isa::{CallConv, TargetIsa}; @@ -411,53 +410,6 @@ impl<'a> Verifier<'a> { Ok(()) } - fn verify_memory_types(&self, errors: &mut VerifierErrors) -> VerifierStepResult { - // Verify that all fields are statically-sized and lie within - // the struct, do not overlap, and are in offset order - for (mt, mt_data) in &self.func.memory_types { - match mt_data { - MemoryTypeData::Struct { size, fields } => { - let mut last_offset = 0; - for field in fields { - if field.offset < last_offset { - errors.report(( - mt, - format!( - "memory type {} has a field at offset {}, which is out-of-order", - mt, field.offset - ), - )); - } - last_offset = match field.offset.checked_add(u64::from(field.ty.bytes())) { - Some(o) => o, - None => { - errors.report(( - mt, - format!( - "memory type {} has a field at offset {} of size {}; offset plus size overflows a u64", - mt, field.offset, field.ty.bytes()), - )); - break; - } - }; - - if last_offset > *size { - errors.report(( - mt, - format!( - "memory type {} has a field at offset {} of size {} that overflows the struct size {}", - mt, field.offset, field.ty.bytes(), *size), - )); - } - } - } - _ => {} - } - } - - Ok(()) - } - /// Check that the given block can be encoded as a BB, by checking that only /// branching instructions are ending the block. fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult { @@ -2128,7 +2080,6 @@ impl<'a> Verifier<'a> { pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult { self.verify_global_values(errors)?; - self.verify_memory_types(errors)?; self.typecheck_entry_block_params(errors)?; self.check_entry_not_cold(errors)?; self.typecheck_function_signature(errors)?; diff --git a/cranelift/codegen/src/write.rs b/cranelift/codegen/src/write.rs index c0025aa6fca3..ab15294a6086 100644 --- a/cranelift/codegen/src/write.rs +++ b/cranelift/codegen/src/write.rs @@ -6,7 +6,6 @@ use crate::entity::SecondaryMap; use crate::ir::entities::AnyEntity; use crate::ir::immediates::Ieee128; -use crate::ir::pcc::Fact; use crate::ir::{Block, DataFlowGraph, Function, Inst, Opcode, SigRef, Type, Value, ValueDef}; use crate::packed_option::ReservedValue; use alloc::string::{String, ToString}; @@ -45,30 +44,24 @@ pub trait FuncWriter { for (ss, slot) in func.dynamic_stack_slots.iter() { any = true; - self.write_entity_definition(w, func, ss.into(), slot, None)?; + self.write_entity_definition(w, func, ss.into(), slot)?; } for (ss, slot) in func.sized_stack_slots.iter() { any = true; - self.write_entity_definition(w, func, ss.into(), slot, None)?; + self.write_entity_definition(w, func, ss.into(), slot)?; } for (gv, gv_data) in &func.global_values { any = true; - let maybe_fact = func.global_value_facts[gv].as_ref(); - self.write_entity_definition(w, func, gv.into(), gv_data, maybe_fact)?; - } - - for (mt, mt_data) in &func.memory_types { - any = true; - self.write_entity_definition(w, func, mt.into(), mt_data, None)?; + self.write_entity_definition(w, func, gv.into(), gv_data)?; } // Write out all signatures before functions since function declarations can refer to // signatures. for (sig, sig_data) in &func.dfg.signatures { any = true; - self.write_entity_definition(w, func, sig.into(), &sig_data, None)?; + self.write_entity_definition(w, func, sig.into(), &sig_data)?; } for (fnref, ext_func) in &func.dfg.ext_funcs { @@ -79,19 +72,18 @@ pub trait FuncWriter { func, fnref.into(), &ext_func.display(Some(&func.params)), - None, )?; } } for (&cref, cval) in func.dfg.constants.iter() { any = true; - self.write_entity_definition(w, func, cref.into(), cval, None)?; + self.write_entity_definition(w, func, cref.into(), cval)?; } if let Some(limit) = func.stack_limit { any = true; - self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit, None)?; + self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit)?; } Ok(any) @@ -104,9 +96,8 @@ pub trait FuncWriter { func: &Function, entity: AnyEntity, value: &dyn fmt::Display, - maybe_fact: Option<&Fact>, ) -> fmt::Result { - self.super_entity_definition(w, func, entity, value, maybe_fact) + self.super_entity_definition(w, func, entity, value) } /// Default impl of `write_entity_definition` @@ -116,13 +107,8 @@ pub trait FuncWriter { _func: &Function, entity: AnyEntity, value: &dyn fmt::Display, - maybe_fact: Option<&Fact>, ) -> fmt::Result { - if let Some(fact) = maybe_fact { - writeln!(w, " {entity} ! {fact} = {value}") - } else { - writeln!(w, " {entity} = {value}") - } + writeln!(w, " {entity} = {value}") } } @@ -208,11 +194,7 @@ pub fn write_function_spec(w: &mut dyn Write, func: &Function) -> fmt::Result { fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result { let ty = func.dfg.value_type(arg); - if let Some(f) = &func.dfg.facts[arg] { - write!(w, "{arg} ! {f}: {ty}") - } else { - write!(w, "{arg}: {ty}") - } + write!(w, "{arg}: {ty}") } /// Write out the basic block header, outdented: @@ -366,9 +348,6 @@ fn write_instruction( } else { write!(w, ", {r}")?; } - if let Some(f) = &func.dfg.facts[*r] { - write!(w, " ! {f}")?; - } } if has_results { write!(w, " = ")?; @@ -499,7 +478,8 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt func_ref, DisplayValues(args.as_slice(pool)), exception_tables[exception].display(pool), - ) + )?; + write_user_stack_map_entries(w, dfg, inst) } TryCallIndirect { ref args, @@ -513,7 +493,8 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt args[0], DisplayValues(&args[1..]), exception_tables[exception].display(pool), - ) + )?; + write_user_stack_map_entries(w, dfg, inst) } FuncAddr { func_ref, .. } => write!(w, " {func_ref}"), StackLoad { diff --git a/cranelift/control/Cargo.toml b/cranelift/control/Cargo.toml index dbf29a83d268..5e4975567be6 100644 --- a/cranelift/control/Cargo.toml +++ b/cranelift/control/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-control" -version = "0.130.0" +version = "0.131.2" description = "White-box fuzz testing framework" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/entity/Cargo.toml b/cranelift/entity/Cargo.toml index bad028fcccf2..0dd69b6767c1 100644 --- a/cranelift/entity/Cargo.toml +++ b/cranelift/entity/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-entity" -version = "0.130.0" +version = "0.131.2" description = "Data structures using entity references as mapping keys" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-entity" diff --git a/cranelift/filetests/filetests/egraph/arithmetic-precise.clif b/cranelift/filetests/filetests/egraph/arithmetic-precise.clif new file mode 100644 index 000000000000..e25f33984058 --- /dev/null +++ b/cranelift/filetests/filetests/egraph/arithmetic-precise.clif @@ -0,0 +1,280 @@ +test optimize precise-output +set opt_level=speed +target x86_64 + +function %fold_iadd_iadd_ineg_i32(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + v3 = ineg v1 + v4 = iadd v2, v3 + return v4 +} + +; function %fold_iadd_iadd_ineg_i32(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; return v0 +; } + +;; (x | y) - (x & y) ==> (x ^ y) +function %test_isub_bor_band_to_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = band v0, v1 + v4 = isub v2, v3 + return v4 +} + +; function %test_isub_bor_band_to_bxor(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v6 = bxor v1, v0 +; return v6 +; } + +function %fold_isub_iadd_band_to_bor_i32(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + v3 = band v0, v1 + v4 = isub v2, v3 + return v4 +} + +; function %fold_isub_iadd_band_to_bor_i32(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v6 = bor v1, v0 +; return v6 +; } + +function %fold_isub_bor_bxor_to_band_i32(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bxor v0, v1 + v4 = isub v2, v3 + return v4 +} + +; function %fold_isub_bor_bxor_to_band_i32(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v6 = band v1, v0 +; return v6 +; } + +function %fold_iadd_bnot_x_to_allones_i32(i32) -> i32 { +block0(v0: i32): + v1 = bnot v0 + v2 = iadd v1, v0 + return v2 +} + +; function %fold_iadd_bnot_x_to_allones_i32(i32) -> i32 fast { +; block0(v0: i32): +; v3 = iconst.i32 -1 +; return v3 ; v3 = -1 +; } + +function %fold_iadd_bnot_x_to_allones_i128(i128) -> i128 { +block0(v0: i128): + v1 = bnot v0 + v2 = iadd v1, v0 + return v2 +} + +; function %fold_iadd_bnot_x_to_allones_i128(i128) -> i128 fast { +; block0(v0: i128): +; v3 = iconst.i64 -1 +; v4 = sextend.i128 v3 ; v3 = -1 +; return v4 +; } + +;; ((x + y) - (x + z)) --> (y - z) +function %test_isub_iadd_iadd_cancel(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = iadd v0, v1 + v4 = iadd v0, v2 + v5 = isub v3, v4 + return v5 +} + +; function %test_isub_iadd_iadd_cancel(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = isub v1, v2 +; return v6 +; } + +;; ((x - z) - (y - z)) --> (x - y) +function %test_isub_isub_isub_cancel(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = isub v0, v2 + v4 = isub v1, v2 + v5 = isub v3, v4 + return v5 +} + +; function %test_isub_isub_isub_cancel(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = isub v0, v1 +; return v6 +; } + +;; ((x - y) - (x - z)) --> (z - y) +function %test_isub_isub_isub_swap_cancel(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = isub v0, v1 + v4 = isub v0, v2 + v5 = isub v3, v4 + return v5 +} + +; function %test_isub_isub_isub_swap_cancel(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = isub v2, v1 +; return v6 +; } + +;; umin(x, y) + umax(x, y) --> x + y +function %test_iadd_umin_umax(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = umin v0, v1 + v3 = umax v0, v1 + v4 = iadd v2, v3 + return v4 +} + +; function %test_iadd_umin_umax(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v6 = iadd v1, v0 +; return v6 +; } + +;; smin(x, y) + smax(x, y) --> x + y +function %test_iadd_smin_smax(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = smin v0, v1 + v3 = smax v0, v1 + v4 = iadd v2, v3 + return v4 +} + +; function %test_iadd_smin_smax(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v6 = iadd v1, v0 +; return v6 +; } + +;; ((x + z) - (y + z)) --> (x - y) +function %test_isub_iadd_iadd_common_rhs(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = iadd v0, v2 + v4 = iadd v1, v2 + v5 = isub v3, v4 + return v5 +} + +; function %test_isub_iadd_iadd_common_rhs(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = isub v0, v1 +; return v6 +; } + +;; ((x - y) + (y + z)) --> (x + z) +function %test_iadd_isub_iadd_cancel(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = isub v0, v1 + v4 = iadd v1, v2 + v5 = iadd v3, v4 + return v5 +} + +; function %test_iadd_isub_iadd_cancel(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = iadd v0, v2 +; return v6 +; } + +;; (x - (x + y)) --> -y +function %test_isub_iadd_to_ineg(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + v3 = isub v0, v2 + return v3 +} + +; function %test_isub_iadd_to_ineg(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v4 = ineg v1 +; return v4 +; } + +;; (x + (y + (z - x))) --> (y + z) +function %test_iadd_iadd_isub_cancel(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = isub v2, v0 + v4 = iadd v1, v3 + v5 = iadd v0, v4 + return v5 +} + +; function %test_iadd_iadd_isub_cancel(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = iadd v1, v2 +; return v6 +; } + +;; (eq ty (iadd cty x y) (iadd cty y x)) -> 1 +function %simplify_icmp_eq_iadd_commute(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + v3 = iadd v1, v0 + v4 = icmp eq v2, v3 + return v4 +} + +; function %simplify_icmp_eq_iadd_commute(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = iconst.i8 1 +; return v5 ; v5 = 1 +; } + +;; (eq ty (iadd cty x y) (iadd cty y x)) -> 1 +function %simplify_vector_icmp_eq_iadd_commute(i32x4, i32x4) -> i32x4 fast { +block0(v0: i32x4, v1: i32x4): + v2 = iadd v0, v1 + v3 = iadd v1, v0 + v4 = icmp eq v2, v3 + return v4 +} + +; function %simplify_vector_icmp_eq_iadd_commute(i32x4, i32x4) -> i32x4 fast { +; block0(v0: i32x4, v1: i32x4): +; v5 = icmp eq v0, v0 +; return v5 +; } + +;; (x - y) != x --> y != 0 +function %simplify_icmp_ne_isub_x(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = isub v0, v1 + v3 = icmp ne v2, v0 + return v3 +} + +; function %simplify_icmp_ne_isub_x(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v4 = iconst.i32 0 +; v5 = icmp ne v1, v4 ; v4 = 0 +; return v5 +; } + +;; (x - y) != x --> y != 0 (vector) +function %simplify_vector_icmp_ne_isub_x(i32x4, i32x4) -> i32x4 fast { +block0(v0: i32x4, v1: i32x4): + v2 = isub v0, v1 + v3 = icmp ne v2, v0 + return v3 +} + +; function %simplify_vector_icmp_ne_isub_x(i32x4, i32x4) -> i32x4 fast { +; block0(v0: i32x4, v1: i32x4): +; v2 = isub v0, v1 +; v3 = icmp ne v2, v0 +; return v3 +; } diff --git a/cranelift/filetests/filetests/egraph/bitops.clif b/cranelift/filetests/filetests/egraph/bitops.clif index 9ec681012df2..1ffd6dec748d 100644 --- a/cranelift/filetests/filetests/egraph/bitops.clif +++ b/cranelift/filetests/filetests/egraph/bitops.clif @@ -482,3 +482,479 @@ block0(v0: i32, v1: i32): ; check: v2 = band v1, v0 ; check: return v2 } + +;; (bor ty (bxor ty (bxor ty x y) z) x) -> (bor ty (bxor ty y z) x) +function %test_bor_bxor_bxor_x(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v1 + v4 = bxor v3, v2 + v5 = bor v4, v0 + return v5 +} + +; function %test_bor_bxor_bxor_x(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = bxor v1, v2 +; v7 = bor v6, v0 +; return v7 +; } + +;; (eq ty (bxor cty x z) (bxor cty y z)) -> (eq ty x y) +function %simplify_icmp_eq_bxor_shared_rhs(i32, i32, i32) -> i8 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v2 + v4 = bxor v1, v2 + v5 = icmp eq v3, v4 + return v5 +} + +; function %simplify_icmp_eq_bxor_shared_rhs(i32, i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = icmp eq v0, v1 +; return v6 +; } + +;; (bor ty y (bnot ty (bor ty x y))) -> (bor ty y (bnot ty x)) +function %test_bor_bnot_bor_absorb(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bnot v2 + v4 = bor v1, v3 + return v4 +} + +; function %test_bor_bnot_bor_absorb(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bnot v0 +; v6 = bor v1, v5 +; return v6 +; } + +;; (band ty y (bnot ty (band ty x y))) -> (band ty y (bnot ty x)) +function %test_band_bnot_band_absorb(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = band v0, v1 + v3 = bnot v2 + v4 = band v1, v3 + return v4 +} + +; function %test_band_bnot_band_absorb(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bnot v0 +; v6 = band v1, v5 +; return v6 +; } + +;; (bxor ty (bnot ty x) (bor ty x y)) -> (bor ty x (bnot ty y)) +function %test_bxor_bnot_bor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bor v0, v1 + v4 = bxor v2, v3 + return v4 +} + +; function %test_bxor_bnot_bor(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bnot v1 +; v6 = bor v0, v5 +; return v6 +; } + +;; (bor ty (slt ty x y) (sgt ty x y)) -> (ne ty x y) +function %test_bor_slt_sgt(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp slt v0, v1 + v3 = icmp sgt v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_slt_sgt(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp ne v0, v1 +; return v5 +; } + +;; (bor ty (ult ty x y) (ugt ty x y)) -> (ne ty x y) +function %test_bor_ult_ugt(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp ult v0, v1 + v3 = icmp ugt v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_ult_ugt(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp ne v0, v1 +; return v5 +; } + +;; (bnot ty (band ty (bnot ty x) y)) -> (bor ty x (bnot ty y)) +function %test_bnot_band_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = band v2, v1 + v4 = bnot v3 + return v4 +} + +; function %test_bnot_band_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bnot v1 +; v6 = bor v0, v5 +; return v6 +; } + +;; (bnot ty (bor ty (bnot ty x) y)) -> (band ty x (bnot ty y)) +function %test_bnot_bor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bor v2, v1 + v4 = bnot v3 + return v4 +} + +; function %test_bnot_bor_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bnot v1 +; v6 = band v0, v5 +; return v6 +; } + +;; (bor ty (ult ty x y) (eq ty x y)) -> (ule ty x y) +function %test_bor_ult_eq(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp ult v0, v1 + v3 = icmp eq v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_ult_eq(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp ule v0, v1 +; return v5 +; } + +;; (bor ty (ugt ty y x) (eq ty x y)) -> (ule ty x y) +function %test_bor_ugt_eq_swap(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp ugt v1, v0 + v3 = icmp eq v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_ugt_eq_swap(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp ule v0, v1 +; return v5 +; } + +;; (bor ty (slt ty x y) (eq ty x y)) -> (sle ty x y) +function %test_bor_slt_eq(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp slt v0, v1 + v3 = icmp eq v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_slt_eq(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp sle v0, v1 +; return v5 +; } + +;; (bor ty (sgt ty y x) (eq ty x y)) -> (sle ty x y) +function %test_bor_sgt_eq_swap(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp sgt v1, v0 + v3 = icmp eq v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_sgt_eq_swap(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp sle v0, v1 +; return v5 +; } + +;; (bor ty (bxor ty x z) (bxor ty (bxor ty x z) y)) -> (bor ty (bxor ty x z) y) +function %test_bor_bxor_bxor_chain(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v1 + v4 = bxor v3, v2 + v5 = bor v3, v4 + return v5 +} + +; function %test_bor_bxor_bxor_chain(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = bor v3, v2 +; return v6 +; } + +;; (bor ty x (bxor ty x y)) -> (bor ty x y) +function %test_bor_bxor_self(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bor v0, v2 + return v3 +} + +; function %test_bor_bxor_self(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v4 = bor v0, v1 +; return v4 +; } + +;; (bnot ty (iadd ty (bnot ty x) y)) -> (isub ty x y) +function %test_bnot_iadd_bnot_to_isub(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = iadd v2, v1 + v4 = bnot v3 + return v4 +} + +; function %test_bnot_iadd_bnot_to_isub(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = isub v0, v1 +; return v5 +; } + +;; (bor ty (bxor ty x z) (bor ty y x)) -> (bor ty (bor ty y x) z) +function %test_bor_bxor_bor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v2 + v4 = bor v1, v0 + v5 = bor v3, v4 + return v5 +} + +; function %test_bor_bxor_bor(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v4 = bor v1, v0 +; v6 = bor v4, v2 +; return v6 +; } + +;; (bxor ty (bor ty x y) (bxor ty x y)) -> (band ty x y) +function %test_bxor_bor_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bxor v0, v1 + v4 = bxor v2, v3 + return v4 +} + +; function %test_bxor_bor_bxor(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = band v0, v1 +; return v5 +; } + +;; (bor ty (band ty (bxor ty x y) z) x) -> (bor ty (band ty y z) x) +function %test_bor_band_bxor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v1 + v4 = band v3, v2 + v5 = bor v4, v0 + return v5 +} + +; function %test_bor_band_bxor(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = band v1, v2 +; v7 = bor v6, v0 +; return v7 +; } + +;; (bor ty (bor ty x y) (bxor ty x z)) -> (bor ty (bor ty x y) z) +function %test_bor_bor_bxor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bor v0, v1 + v4 = bxor v0, v2 + v5 = bor v3, v4 + return v5 +} + +; function %test_bor_bor_bxor(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v3 = bor v0, v1 +; v6 = bor v3, v2 +; return v6 +; } + +;; (ne ty (bxor cty x z) (bxor cty y z)) -> (ne ty x y) +function %simplify_icmp_ne_bxor_shared_rhs(i32, i32, i32) -> i8 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v2 + v4 = bxor v1, v2 + v5 = icmp ne v3, v4 + return v5 +} + +; function %simplify_icmp_ne_bxor_shared_rhs(i32, i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = icmp ne v0, v1 +; return v6 +; } + +;; (bor ty (band ty x y) (band ty x (bnot ty y))) -> x +function %test_bor_band_band_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = band v0, v1 + v3 = bnot v1 + v4 = band v0, v3 + v5 = bor v2, v4 + return v5 +} + +; function %test_bor_band_band_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; return v0 +; } + +;; (band ty (bor ty x y) (bor ty x (bnot ty y))) -> x +function %test_band_bor_bor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bnot v1 + v4 = bor v0, v3 + v5 = band v2, v4 + return v5 +} + +; function %test_band_bor_bor_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; return v0 +; } + +;; (bor ty (bxor ty x y) (bnot ty x)) -> (bnot ty (band ty y x)) +function %test_bor_bxor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bnot v0 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_bxor_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = band v1, v0 +; v6 = bnot v5 +; return v6 +; } + +;; (bxor ty (bxor ty x y) x) -> y +function %test_bxor_bxor_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bxor v2, v0 + return v3 +} + +; function %test_bxor_bxor_x(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; return v1 +; } + +;; (bor ty (band ty x (bor ty y z)) y) -> (bor ty (band ty x z) y) +function %test_bor_band_bor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bor v1, v2 + v4 = band v0, v3 + v5 = bor v4, v1 + return v5 +} + +; function %test_bor_band_bor(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = band v0, v2 +; v7 = bor v6, v1 +; return v7 +; } + +;; (band ty (bor ty x (band ty y z)) y) -> (band ty (bor ty x z) y) +function %test_band_bor_band(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = band v1, v2 + v4 = bor v0, v3 + v5 = band v4, v1 + return v5 +} + +; function %test_band_bor_band(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = bor v0, v2 +; v7 = band v6, v1 +; return v7 +; } + +;; (bor ty (bxor ty x (band ty y z)) y) -> (bor ty x y) +function %test_bor_bxor_band(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = band v1, v2 + v4 = bxor v0, v3 + v5 = bor v4, v1 + return v5 +} + +; function %test_bor_bxor_band(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = bor v0, v1 +; return v6 +; } + +;; (iadd ty (bxor ty (band ty x y) y) z) -> (isub ty (iadd ty y z) (band ty x y)) +function %test_iadd_bxor_band_to_isub(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = band v0, v1 + v4 = bxor v3, v1 + v5 = iadd v4, v2 + return v5 +} + +; function %test_iadd_bxor_band_to_isub(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = iadd v1, v2 +; v7 = band v0, v1 +; v8 = isub v6, v7 +; return v8 +; } + +;; (band ty y (bor ty (bnot ty y) x)) -> (band ty y x) +function %test_band_bor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = bor v2, v0 + v4 = band v1, v3 + return v4 +} + +; function %test_band_bor_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = band v1, v0 +; return v5 +; } + +;; (bor ty y (band ty (bnot ty y) x)) -> (bor ty y x) +function %test_bor_band_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v2, v0 + v4 = bor v1, v3 + return v4 +} + +; function %test_bor_band_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bor v1, v0 +; return v5 +; } \ No newline at end of file diff --git a/cranelift/filetests/filetests/egraph/cprop.clif b/cranelift/filetests/filetests/egraph/cprop.clif index fd3d88cb2202..5a691e160655 100644 --- a/cranelift/filetests/filetests/egraph/cprop.clif +++ b/cranelift/filetests/filetests/egraph/cprop.clif @@ -158,6 +158,55 @@ block0: ; check: v3 = iconst.i16 -4 ; check: return v3 +;; 0xf0 == 0b00001111 +function %rotr() -> i8 { +block0: + v0 = iconst.i8 0x0f + v1 = iconst.i8 2 + v2 = rotr v0, v1 + return v2 +} + +;; 0b11000011 == -61 +; check: v3 = iconst.i8 -61 +; check: return v3 + +function %rotr_i16() -> i16 { +block0: + v0 = iconst.i16 0x0f + v1 = iconst.i8 2 + v2 = rotr v0, v1 + return v2 +} + +;; 0b1100000000000011 == -16381 +; check: v3 = iconst.i16 -16381 +; check: return v3 + +function %rotl() -> i8 { +block0: + v0 = iconst.i8 0xf0 + v1 = iconst.i8 2 + v2 = rotl v0, v1 + return v2 +} + +;; 0b11000011 == -61 +; check: v3 = iconst.i8 -61 +; check: return v3 + +function %rotl_i16() -> i16 { +block0: + v0 = iconst.i16 0xf000 + v1 = iconst.i8 2 + v2 = rotl v0, v1 + return v2 +} + +;; 0b11000011 == -61 +; check: v3 = iconst.i16 -16381 +; check: return v3 + function %icmp_eq_i32() -> i8 { block0: v0 = iconst.i32 1 @@ -435,6 +484,19 @@ block0: ; check: v4 = f16const -0.0 ; check: return v4 ; v4 = -0.0 +function %f16_fmin_promotes_correctly() -> f64 { +block0: + v1 = f16const 0x1.0p0 + v2 = f16const 0x1.0p1 + v3 = fmin v1, v2 + v4 = fpromote.f64 v3 + return v4 +} + +; check: v5 = f16const 0x1.000p0 +; check: v4 = fpromote.f64 v5 ; v5 = 0x1.000p0 +; check: return v4 + function %f16_fmax() -> f16 { block0: v1 = f16const -0x1.5p6 diff --git a/cranelift/filetests/filetests/egraph/fold-bitops.clif b/cranelift/filetests/filetests/egraph/fold-bitops.clif index 4c4a9e14ab20..6a273df1252b 100644 --- a/cranelift/filetests/filetests/egraph/fold-bitops.clif +++ b/cranelift/filetests/filetests/egraph/fold-bitops.clif @@ -107,3 +107,390 @@ block0(v0: i32, v1: i32): ; return v7 ; } +;; (band ty (bor ty x y) x) -> x +function %test_band_bor_absorb_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = band v2, v0 + return v3 +} + +; function %test_band_bor_absorb_x(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; return v0 +; } + +;; (band ty (bor ty x z) (bor ty y z)) -> (bor ty (band ty x y) z) +function %test_band_bor_bor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bor v0, v2 + v4 = bor v1, v2 + v5 = band v3, v4 + return v5 +} + +; function %test_band_bor_bor(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v8 = band v1, v0 +; v9 = bor v8, v2 +; return v9 +; } + +;; (bor ty (band ty x z) (band ty y z)) -> (band ty (bor ty x y) z) +function %test_bor_band_band(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = band v0, v2 + v4 = band v1, v2 + v5 = bor v3, v4 + return v5 +} + +; function %test_bor_band_band(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v8 = bor v1, v0 +; v9 = band v8, v2 +; return v9 +; } + +;; (bxor ty (bor ty x y) (bor ty x (bnot ty y))) -> (bnot ty x) +function %test_bxor_bor_bor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bnot v1 + v4 = bor v0, v3 + v5 = bxor v2, v4 + return v5 +} + +; function %test_bxor_bor_bor_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v6 = bnot v0 +; return v6 +; } + +;; (band ty (bnot ty x) (bnot ty y)) -> (bnot ty (bor ty x y)) +function %test_band_bnot_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bnot v1 + v4 = band v2, v3 + return v4 +} + +; function %test_band_bnot_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v7 = bor v1, v0 +; v8 = bnot v7 +; return v8 +; } + +;; (bor ty (bnot ty x) (bnot ty y)) -> (bnot ty (band ty x y)) +function %test_bor_bnot_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bnot v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_bnot_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v7 = band v1, v0 +; v8 = bnot v7 +; return v8 +; } + + +;; ((x & ~y) - (x & y)) --> ((x ^ y) - y) +function %test_isub_band_band_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v0, v2 + v4 = band v0, v1 + v5 = isub v3, v4 + return v5 +} + +; function %test_isub_band_band_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v6 = bxor v0, v1 +; v7 = isub v6, v1 +; return v7 +; } + +;; (x & ~y) | (~x & y) --> (x ^ y) +function %test_bor_band_bnot_to_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v0, v2 + v4 = bnot v0 + v5 = band v4, v1 + v6 = bor v3, v5 + return v6 +} + +; function %test_bor_band_bnot_to_bxor(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v8 = bxor v1, v0 +; return v8 +; } + +;; (x & ~y) | (x ^ y) --> (x ^ y) +function %test_bor_band_bnot_bxor_absorb(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v0, v2 + v4 = bxor v0, v1 + v5 = bor v3, v4 + return v5 +} + +; function %test_bor_band_bnot_bxor_absorb(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v4 = bxor v0, v1 +; return v4 +; } + +;; (x & ~y) ^ ~x --> ~(x & y) +function %test_bxor_band_bnot_to_bnot_band(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v0, v2 + v4 = bnot v0 + v5 = bxor v3, v4 + return v5 +} + +; function %test_bxor_band_bnot_to_bnot_band(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v8 = band v1, v0 +; v12 = bnot v8 +; return v12 +; } + +;; (~x & y) ^ x --> x | y +function %test_bxor_band_bnot_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = band v2, v1 + v4 = bxor v3, v0 + return v4 +} + +; function %test_bxor_band_bnot_x(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bor v0, v1 +; return v5 +; } + +;; (x | y) & ~(x ^ y) --> x & y +function %test_band_bor_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bxor v0, v1 + v4 = bnot v3 + v5 = band v2, v4 + return v5 +} + +; function %test_band_bor_bnot_bxor(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v7 = band v1, v0 +; return v7 +; } + +;; x | ~(x ^ y) --> x | ~y +function %test_bor_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bnot v2 + v4 = bor v0, v3 + return v4 +} + +; function %test_bor_bnot_bxor(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bnot v1 +; v6 = bor v0, v5 +; return v6 +; } + +;; x | ((~x) ^ y) --> x | ~y +function %test_bor_bxor_bnot_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bxor v2, v1 + v4 = bor v0, v3 + return v4 +} + +; function %test_bor_bxor_bnot_x(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bnot v1 +; v6 = bor v0, v5 +; return v6 +; } + +;; x & ~(x ^ y) --> x & y +function %test_band_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bnot v2 + v4 = band v0, v3 + return v4 +} + +; function %test_band_bnot_bxor(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = band v0, v1 +; return v5 +; } + +;; x & ((~x) ^ y) --> x & y +function %test_band_bxor_bnot_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bxor v2, v1 + v4 = band v0, v3 + return v4 +} + +; function %test_band_bxor_bnot_x(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = band v0, v1 +; return v5 +; } + +;; (x | y) | (x ^ y) --> (x | y) +function %test_bor_bor_bxor_same(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bxor v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_bor_bxor_same(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = bor v1, v0 +; return v5 +; } + +;; (x ^ y) & ((y ^ z) ^ x) --> (x ^ y) & ~z +function %test_band_bxor_bxor_bnot(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v1 + v4 = bxor v1, v2 + v5 = bxor v4, v0 + v6 = band v3, v5 + return v6 +} + +; function %test_band_bxor_bxor_bnot(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v3 = bxor v0, v1 +; v7 = bnot v2 +; v8 = band v3, v7 +; return v8 +; } + +;; (~x & y) ^ z --> (x & y) ^ (z ^ y) +function %test_bxor_band_bnot_general(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bnot v0 + v4 = band v3, v1 + v5 = bxor v4, v2 + return v5 +} + +; function %test_bxor_band_bnot_general(i32, i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32, v2: i32): +; v6 = band v0, v1 +; v7 = bxor v2, v1 +; v8 = bxor v6, v7 +; return v8 +; } + +;; ~x - ~y --> y - x +function %test_isub_bnot_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bnot v1 + v4 = isub v2, v3 + return v4 +} + +; function %test_isub_bnot_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = isub v1, v0 +; return v5 +; } + +;; (~x & y) | ~(x | y) --> ~x +function %test_bor_band_bnot_bor_to_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = band v2, v1 + v4 = bor v0, v1 + v5 = bnot v4 + v6 = bor v3, v5 + return v6 +} + +; function %test_bor_band_bnot_bor_to_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v2 = bnot v0 +; return v2 +; } + +;; (~x | y) & ~(x & y) --> ~x +function %test_band_bor_bnot_band_to_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bor v2, v1 + v4 = band v0, v1 + v5 = bnot v4 + v6 = band v3, v5 + return v6 +} + +; function %test_band_bor_bnot_band_to_bnot(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v2 = bnot v0 +; return v2 +; } + +;; (x & y) | ~(x | y) --> ~(x ^ y) +function %test_bor_band_bnot_bor_to_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = band v0, v1 + v3 = bor v0, v1 + v4 = bnot v3 + v5 = bor v2, v4 + return v5 +} + +; function %test_bor_band_bnot_bor_to_bnot_bxor(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v8 = bxor v1, v0 +; v9 = bnot v8 +; return v9 +; } + +;; (~x | y) ^ (x ^ y) --> x | ~y +function %test_bxor_bor_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bor v2, v1 + v4 = bxor v0, v1 + v5 = bxor v3, v4 + return v5 +} + +; function %test_bxor_bor_bnot_bxor(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v6 = bnot v1 +; v7 = bor v0, v6 +; return v7 +; } + diff --git a/cranelift/filetests/filetests/egraph/icmp-parameterized.clif b/cranelift/filetests/filetests/egraph/icmp-parameterized.clif index 0d5222f9219a..a99f4c0d8b4b 100644 --- a/cranelift/filetests/filetests/egraph/icmp-parameterized.clif +++ b/cranelift/filetests/filetests/egraph/icmp-parameterized.clif @@ -2239,8 +2239,8 @@ block0(v0: i32, v1: i32): ; function %icmp_or_eq_ugt(i32, i32) -> i8 fast { ; block0(v0: i32, v1: i32): -; v5 = icmp uge v0, v1 -; return v5 +; v6 = icmp uge v0, v1 +; return v6 ; } function %icmp_or_eq_uge(i32, i32) -> i8 { @@ -2295,8 +2295,8 @@ block0(v0: i32, v1: i32): ; function %icmp_or_eq_sgt(i32, i32) -> i8 fast { ; block0(v0: i32, v1: i32): -; v5 = icmp sge v0, v1 -; return v5 +; v6 = icmp sge v0, v1 +; return v6 ; } function %icmp_or_eq_sge(i32, i32) -> i8 { @@ -2519,8 +2519,8 @@ block0(v0: i32, v1: i32): ; function %icmp_or_ult_ugt(i32, i32) -> i8 fast { ; block0(v0: i32, v1: i32): -; v5 = icmp ne v0, v1 -; return v5 +; v6 = icmp ne v1, v0 +; return v6 ; } function %icmp_or_ult_uge(i32, i32) -> i8 { @@ -2759,8 +2759,8 @@ block0(v0: i32, v1: i32): ; function %icmp_or_ugt_eq(i32, i32) -> i8 fast { ; block0(v0: i32, v1: i32): -; v5 = icmp uge v0, v1 -; return v5 +; v6 = icmp uge v0, v1 +; return v6 ; } function %icmp_or_ugt_ne(i32, i32) -> i8 { @@ -2787,8 +2787,8 @@ block0(v0: i32, v1: i32): ; function %icmp_or_ugt_ult(i32, i32) -> i8 fast { ; block0(v0: i32, v1: i32): -; v5 = icmp ne v0, v1 -; return v5 +; v6 = icmp ne v1, v0 +; return v6 ; } function %icmp_or_ugt_ule(i32, i32) -> i8 { @@ -3175,8 +3175,8 @@ block0(v0: i32, v1: i32): ; function %icmp_or_slt_sgt(i32, i32) -> i8 fast { ; block0(v0: i32, v1: i32): -; v5 = icmp ne v0, v1 -; return v5 +; v6 = icmp ne v1, v0 +; return v6 ; } function %icmp_or_slt_sge(i32, i32) -> i8 { @@ -3351,8 +3351,8 @@ block0(v0: i32, v1: i32): ; function %icmp_or_sgt_eq(i32, i32) -> i8 fast { ; block0(v0: i32, v1: i32): -; v5 = icmp sge v0, v1 -; return v5 +; v6 = icmp sge v0, v1 +; return v6 ; } function %icmp_or_sgt_ne(i32, i32) -> i8 { @@ -3443,8 +3443,8 @@ block0(v0: i32, v1: i32): ; function %icmp_or_sgt_slt(i32, i32) -> i8 fast { ; block0(v0: i32, v1: i32): -; v5 = icmp ne v0, v1 -; return v5 +; v6 = icmp ne v1, v0 +; return v6 ; } function %icmp_or_sgt_sle(i32, i32) -> i8 { diff --git a/cranelift/filetests/filetests/egraph/selects.clif b/cranelift/filetests/filetests/egraph/selects.clif index 81a1b00a3b33..216dd07398e3 100644 --- a/cranelift/filetests/filetests/egraph/selects.clif +++ b/cranelift/filetests/filetests/egraph/selects.clif @@ -89,3 +89,142 @@ block0(v0: i32, v1: i32): ; return v7 ; v7 = 0 ; } +function %select_eq_xy_xy_to_y(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = icmp eq v0, v1 + v3 = select v2, v0, v1 + return v3 +} + +; function %select_eq_xy_xy_to_y(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; return v1 +; } + +;; (x | (y != z)) ? y : z ==> y +function %select_bor_ne_to_y(i8, i8, i8) -> i8 fast { +block0(v0: i8, v1: i8, v2: i8): + v3 = icmp ne v1, v2 + v4 = bor v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; function %select_bor_ne_to_y(i8, i8, i8) -> i8 fast { +; block0(v0: i8, v1: i8, v2: i8): +; return v1 +; } + +;; (x == y) ? x : (x | y) ==> (x | y) +function %select_eq_xy_x_borxy_to_borxy(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = icmp eq v0, v1 + v3 = bor v0, v1 + v4 = select v2, v0, v3 + return v4 +} + +; function %select_eq_xy_x_borxy_to_borxy(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v3 = bor v0, v1 +; return v3 +; } + +;; (x != y) ? (x & y) : y ==> (x & y) +function %select_ne_xy_bandxy_y_to_bandxy(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = icmp ne v0, v1 + v3 = band v0, v1 + v4 = select v2, v3, v1 + return v4 +} + +; function %select_ne_xy_bandxy_y_to_bandxy(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v3 = band v0, v1 +; return v3 +; } + +;; (x <_u y) ? 1 : (x == y) ==> (x <=_u y) +function %select_ult_one_eq_to_ule(i8, i8) -> i8 fast { +block0(v0: i8, v1: i8): + v2 = icmp ult v0, v1 + v3 = iconst.i8 1 + v4 = icmp eq v0, v1 + v5 = select v2, v3, v4 + return v5 +} + +; function %select_ult_one_eq_to_ule(i8, i8) -> i8 fast { +; block0(v0: i8, v1: i8): +; v6 = icmp ule v0, v1 +; return v6 +; } + +;; (x < y) ? 1 : (x > y) ==> (x != y) +function %select_slt_one_sgt_to_ne(i8, i8) -> i8 fast { +block0(v0: i8, v1: i8): + v2 = icmp slt v0, v1 + v3 = iconst.i8 1 + v4 = icmp sgt v0, v1 + v5 = select v2, v3, v4 + return v5 +} + +; function %select_slt_one_sgt_to_ne(i8, i8) -> i8 fast { +; block0(v0: i8, v1: i8): +; v6 = icmp ne v0, v1 +; return v6 +; } + +;; (x ? 1 : (x | y)) ==> (x ? 1 : y) +function %select_x_one_borxy_to_select_x_one_y(i8, i8) -> i8 fast { +block0(v0: i8, v1: i8): + v2 = iconst.i8 1 + v3 = bor v0, v1 + v4 = select v0, v2, v3 + return v4 +} + +; function %select_x_one_borxy_to_select_x_one_y(i8, i8) -> i8 fast { +; block0(v0: i8, v1: i8): +; v2 = iconst.i8 1 +; v5 = select v0, v2, v1 ; v2 = 1 +; return v5 +; } + +;; if x == n then (y - n) else (y - x) ==> (y - x) +function %select_eq_xn_yminusn_yminusx_to_yminusx(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = iconst.i32 7 + v3 = icmp eq v0, v2 + v4 = isub v1, v2 + v5 = isub v1, v0 + v6 = select v3, v4, v5 + return v6 +} + +; function %select_eq_xn_yminusn_yminusx_to_yminusx(i32, i32) -> i32 fast { +; block0(v0: i32, v1: i32): +; v5 = isub v1, v0 +; return v5 +; } + +;; select(cond, eq(z, y), eq(p, y)) ==> eq(y, select(cond, z, p)) +function %select_eq_eq_to_eq_select(i8, i32, i32) -> i8 fast { +block0(v0: i8, v1: i32, v2: i32): + v3 = icmp eq v2, v1 + v4 = iconst.i32 7 + v5 = icmp eq v4, v1 + v6 = select v0, v3, v5 + return v6 +} + +; function %select_eq_eq_to_eq_select(i8, i32, i32) -> i8 fast { +; block0(v0: i8, v1: i32, v2: i32): +; v4 = iconst.i32 7 +; v9 = select v0, v2, v4 ; v4 = 7 +; v10 = icmp eq v1, v9 +; return v10 +; } + diff --git a/cranelift/filetests/filetests/egraph/shifts.clif b/cranelift/filetests/filetests/egraph/shifts.clif index f0c0e0754dee..c0319a9b8286 100644 --- a/cranelift/filetests/filetests/egraph/shifts.clif +++ b/cranelift/filetests/filetests/egraph/shifts.clif @@ -758,6 +758,21 @@ block0(v0: i8, v1: i8): ; check: return v0 +function %rotr_iconst_select_iconsts(i8) -> i8 { +block0(v0: i8): + v1 = iconst.i8 1 + v2 = iconst.i8 1 + v3 = iconst.i8 2 + v4 = select v0, v2, v3 + v5 = rotr.i8 v1, v4 + return v5 +} + +; check: v6 = iconst.i8 -128 +; check: v7 = iconst.i8 64 +; check: v8 = select v0, v6, v7 +; check: return v8 + function %shifts_to_rotl(i64) -> i64 { block0(v0: i64): v1 = iconst.i16 3 diff --git a/cranelift/filetests/filetests/isa/aarch64/bti.clif b/cranelift/filetests/filetests/isa/aarch64/bti.clif index f2ba08c270cc..72c7e648c394 100644 --- a/cranelift/filetests/filetests/isa/aarch64/bti.clif +++ b/cranelift/filetests/filetests/isa/aarch64/bti.clif @@ -57,31 +57,30 @@ block5(v5: i32): ; hint #0x22 ; block1: ; offset 0x4 ; cmp w0, #3 -; b.hs #0x54 +; b.hs #0x50 ; csel x11, xzr, x0, hs -; csdb -; adr x10, #0x24 +; adr x10, #0x20 ; ldrsw x11, [x10, w11, uxtw #2] ; add x10, x10, x11 ; br x10 ; .byte 0x24, 0x00, 0x00, 0x00 ; .byte 0x18, 0x00, 0x00, 0x00 ; .byte 0x0c, 0x00, 0x00, 0x00 -; block2: ; offset 0x30 +; block2: ; offset 0x2c ; hint #0x24 ; mov w5, #3 -; b #0x58 -; block3: ; offset 0x3c +; b #0x54 +; block3: ; offset 0x38 ; hint #0x24 ; mov w5, #2 -; b #0x58 -; block4: ; offset 0x48 +; b #0x54 +; block4: ; offset 0x44 ; hint #0x24 ; mov w5, #1 -; b #0x58 -; block5: ; offset 0x54 +; b #0x54 +; block5: ; offset 0x50 ; mov w5, #4 -; block6: ; offset 0x58 +; block6: ; offset 0x54 ; add w0, w0, w5 ; ret @@ -124,20 +123,19 @@ block2: ; ldr x5, [x0] ; mov x8, x5 ; cmp w0, #1 -; b.hs #0x40 +; b.hs #0x3c ; csel x7, xzr, x0, hs -; csdb -; adr x6, #0x2c +; adr x6, #0x28 ; ldrsw x7, [x6, w7, uxtw #2] ; add x6, x6, x7 ; br x6 ; .byte 0x04, 0x00, 0x00, 0x00 -; block2: ; offset 0x30 +; block2: ; offset 0x2c ; hint #0x24 ; mov x0, x8 ; add x0, x0, #0x2a ; ret -; block3: ; offset 0x40 +; block3: ; offset 0x3c ; mov x0, x8 ; ret diff --git a/cranelift/filetests/filetests/isa/aarch64/bti_with_csdb.clif b/cranelift/filetests/filetests/isa/aarch64/bti_with_csdb.clif new file mode 100644 index 000000000000..3c00aee53480 --- /dev/null +++ b/cranelift/filetests/filetests/isa/aarch64/bti_with_csdb.clif @@ -0,0 +1,174 @@ +test compile precise-output +set unwind_info=false +target aarch64 use_bti use_csdb + +function %f1(i32) -> i32 { +block0(v0: i32): + br_table v0, block4, [block1, block2, block3] + +block1: + v1 = iconst.i32 1 + jump block5(v1) + +block2: + v2 = iconst.i32 2 + jump block5(v2) + +block3: + v3 = iconst.i32 3 + jump block5(v3) + +block4: + v4 = iconst.i32 4 + jump block5(v4) + +block5(v5: i32): + v6 = iadd.i32 v0, v5 + return v6 +} + +; VCode: +; bti c +; block0: +; emit_island 44 +; subs wzr, w0, #3 +; b.hs label4 ; csel x11, xzr, x0, hs ; csdb ; adr x10, pc+16 ; ldrsw x11, [x10, x11, uxtw #2] ; add x10, x10, x11 ; br x10 ; jt_entries [MachLabel(3), MachLabel(2), MachLabel(1)] +; block1: +; bti j +; movz w5, #3 +; b label5 +; block2: +; bti j +; movz w5, #2 +; b label5 +; block3: +; bti j +; movz w5, #1 +; b label5 +; block4: +; movz w5, #4 +; b label5 +; block5: +; add w0, w0, w5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; hint #0x22 +; block1: ; offset 0x4 +; cmp w0, #3 +; b.hs #0x54 +; csel x11, xzr, x0, hs +; csdb +; adr x10, #0x24 +; ldrsw x11, [x10, w11, uxtw #2] +; add x10, x10, x11 +; br x10 +; .byte 0x24, 0x00, 0x00, 0x00 +; .byte 0x18, 0x00, 0x00, 0x00 +; .byte 0x0c, 0x00, 0x00, 0x00 +; block2: ; offset 0x30 +; hint #0x24 +; mov w5, #3 +; b #0x58 +; block3: ; offset 0x3c +; hint #0x24 +; mov w5, #2 +; b #0x58 +; block4: ; offset 0x48 +; hint #0x24 +; mov w5, #1 +; b #0x58 +; block5: ; offset 0x54 +; mov w5, #4 +; block6: ; offset 0x58 +; add w0, w0, w5 +; ret + +function %f2(i64) -> i64 { +block0(v0: i64): + v1 = ireduce.i32 v0 + v2 = load.i64 notrap aligned table v0 + br_table v1, block1, [block2] + +block1: + return v2 + +block2: + v3 = iconst.i64 42 + v4 = iadd.i64 v2, v3 + return v4 +} + +; VCode: +; bti c +; block0: +; ldr x5, [x0] +; mov x8, x5 +; emit_island 36 +; subs wzr, w0, #1 +; b.hs label2 ; csel x7, xzr, x0, hs ; csdb ; adr x6, pc+16 ; ldrsw x7, [x6, x7, uxtw #2] ; add x6, x6, x7 ; br x6 ; jt_entries [MachLabel(1)] +; block1: +; bti j +; mov x0, x8 +; add x0, x0, #42 +; ret +; block2: +; mov x0, x8 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; hint #0x22 +; block1: ; offset 0x4 +; ldr x5, [x0] +; mov x8, x5 +; cmp w0, #1 +; b.hs #0x40 +; csel x7, xzr, x0, hs +; csdb +; adr x6, #0x2c +; ldrsw x7, [x6, w7, uxtw #2] +; add x6, x6, x7 +; br x6 +; .byte 0x04, 0x00, 0x00, 0x00 +; block2: ; offset 0x30 +; hint #0x24 +; mov x0, x8 +; add x0, x0, #0x2a +; ret +; block3: ; offset 0x40 +; mov x0, x8 +; ret + +function %f3(i64) -> i64 { + fn0 = %g(i64) -> i64 + +block0(v0: i64): + v1 = call fn0(v0) + return v1 +} + +; VCode: +; bti c +; stp fp, lr, [sp, #-16]! +; mov fp, sp +; block0: +; load_ext_name_far x3, TestCase(%g)+0 +; blr x3 +; ldp fp, lr, [sp], #16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; hint #0x22 +; stp x29, x30, [sp, #-0x10]! +; mov x29, sp +; block1: ; offset 0xc +; ldr x3, #0x14 +; b #0x1c +; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0 +; .byte 0x00, 0x00, 0x00, 0x00 +; blr x3 +; ldp x29, x30, [sp], #0x10 +; ret diff --git a/cranelift/filetests/filetests/isa/aarch64/condops-with-csdb.clif b/cranelift/filetests/filetests/isa/aarch64/condops-with-csdb.clif new file mode 100644 index 000000000000..55f22b875018 --- /dev/null +++ b/cranelift/filetests/filetests/isa/aarch64/condops-with-csdb.clif @@ -0,0 +1,1282 @@ +test compile precise-output +set unwind_info=false +target aarch64 use_csdb + +function %f(i8, i8, i8) -> i8 { +block0(v0: i8, v1: i8, v2: i8): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i8, i16, i16) -> i16 { +block0(v0: i8, v1: i16, v2: i16): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i8, i32, i32) -> i32 { +block0(v0: i8, v1: i32, v2: i32): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i8, i64, i64) -> i64 { +block0(v0: i8, v1: i64, v2: i64): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i8, i128, i128) -> i128 { +block0(v0: i8, v1: i128, v2: i128): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w6, w0 +; subs wzr, w6, #42 +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w6, w0 +; cmp w6, #0x2a +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret + +function %f(i16, i8, i8) -> i8 { +block0(v0: i16, v1: i8, v2: i8): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i16, i16, i16) -> i16 { +block0(v0: i16, v1: i16, v2: i16): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i16, i32, i32) -> i32 { +block0(v0: i16, v1: i32, v2: i32): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i16, i64, i64) -> i64 { +block0(v0: i16, v1: i64, v2: i64): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i16, i128, i128) -> i128 { +block0(v0: i16, v1: i128, v2: i128): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w6, w0 +; subs wzr, w6, #42 +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w6, w0 +; cmp w6, #0x2a +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret + +function %f(i32, i8, i8) -> i8 { +block0(v0: i32, v1: i8, v2: i8): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i32, i16, i16) -> i16 { +block0(v0: i32, v1: i16, v2: i16): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i32, i32, i32) -> i32 { +block0(v0: i32, v1: i32, v2: i32): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i32, i64, i64) -> i64 { +block0(v0: i32, v1: i64, v2: i64): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i32, i128, i128) -> i128 { +block0(v0: i32, v1: i128, v2: i128): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret + +function %f(i64, i8, i8) -> i8 { +block0(v0: i64, v1: i8, v2: i8): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i64, i16, i16) -> i16 { +block0(v0: i64, v1: i16, v2: i16): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i64, i32, i32) -> i32 { +block0(v0: i64, v1: i32, v2: i32): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i64, i64, i64) -> i64 { +block0(v0: i64, v1: i64, v2: i64): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x1, x2, eq +; ret + +function %f(i64, i128, i128) -> i128 { +block0(v0: i64, v1: i128, v2: i128): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret + +function %f(i128, i8, i8) -> i8 { +block0(v0: i128, v1: i8, v2: i8): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select.i8 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x7, #42 +; movz x8, #0 +; subs xzr, x0, x7 +; ccmp x1, x8, #nzcv, eq +; csel x0, x2, x3, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x7, #0x2a +; mov x8, #0 +; cmp x0, x7 +; ccmp x1, x8, #0, eq +; csel x0, x2, x3, eq +; ret + +function %f(i128, i16, i16) -> i16 { +block0(v0: i128, v1: i16, v2: i16): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select.i16 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x7, #42 +; movz x8, #0 +; subs xzr, x0, x7 +; ccmp x1, x8, #nzcv, eq +; csel x0, x2, x3, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x7, #0x2a +; mov x8, #0 +; cmp x0, x7 +; ccmp x1, x8, #0, eq +; csel x0, x2, x3, eq +; ret + +function %f(i128, i32, i32) -> i32 { +block0(v0: i128, v1: i32, v2: i32): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select.i32 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x7, #42 +; movz x8, #0 +; subs xzr, x0, x7 +; ccmp x1, x8, #nzcv, eq +; csel x0, x2, x3, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x7, #0x2a +; mov x8, #0 +; cmp x0, x7 +; ccmp x1, x8, #0, eq +; csel x0, x2, x3, eq +; ret + +function %f(i128, i64, i64) -> i64 { +block0(v0: i128, v1: i64, v2: i64): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select.i64 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x7, #42 +; movz x8, #0 +; subs xzr, x0, x7 +; ccmp x1, x8, #nzcv, eq +; csel x0, x2, x3, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x7, #0x2a +; mov x8, #0 +; cmp x0, x7 +; ccmp x1, x8, #0, eq +; csel x0, x2, x3, eq +; ret + +function %f(i128, i128, i128) -> i128 { +block0(v0: i128, v1: i128, v2: i128): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select.i128 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x10, #42 +; movz x11, #0 +; subs xzr, x0, x10 +; ccmp x1, x11, #nzcv, eq +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x10, #0x2a +; mov x11, #0 +; cmp x0, x10 +; ccmp x1, x11, #0, eq +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; ret + +function %f(i8, i8, i8) -> i8 { +block0(v0: i8, v1: i8, v2: i8): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i8 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i8, i16, i16) -> i16 { +block0(v0: i8, v1: i16, v2: i16): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i16 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i8, i32, i32) -> i32 { +block0(v0: i8, v1: i32, v2: i32): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i32 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i8, i64, i64) -> i64 { +block0(v0: i8, v1: i64, v2: i64): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i64 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i8, i128, i128) -> i128 { +block0(v0: i8, v1: i128, v2: i128): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i128 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxtb w6, w0 +; subs wzr, w6, #42 +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w6, w0 +; cmp w6, #0x2a +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret + +function %f(i16, i8, i8) -> i8 { +block0(v0: i16, v1: i8, v2: i8): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i8 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i16, i16, i16) -> i16 { +block0(v0: i16, v1: i16, v2: i16): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i16 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i16, i32, i32) -> i32 { +block0(v0: i16, v1: i32, v2: i32): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i32 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i16, i64, i64) -> i64 { +block0(v0: i16, v1: i64, v2: i64): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i64 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w4, w0 +; subs wzr, w4, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w4, w0 +; cmp w4, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i16, i128, i128) -> i128 { +block0(v0: i16, v1: i128, v2: i128): + v3 = iconst.i16 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i128 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; uxth w6, w0 +; subs wzr, w6, #42 +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxth w6, w0 +; cmp w6, #0x2a +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret + +function %f(i32, i8, i8) -> i8 { +block0(v0: i32, v1: i8, v2: i8): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i8 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i32, i16, i16) -> i16 { +block0(v0: i32, v1: i16, v2: i16): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i16 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i32, i32, i32) -> i32 { +block0(v0: i32, v1: i32, v2: i32): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i32 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i32, i64, i64) -> i64 { +block0(v0: i32, v1: i64, v2: i64): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i64 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i32, i128, i128) -> i128 { +block0(v0: i32, v1: i128, v2: i128): + v3 = iconst.i32 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i128 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret + +function %f(i64, i8, i8) -> i8 { +block0(v0: i64, v1: i8, v2: i8): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i8 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i64, i16, i16) -> i16 { +block0(v0: i64, v1: i16, v2: i16): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i16 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i64, i32, i32) -> i32 { +block0(v0: i64, v1: i32, v2: i32): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i32 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i64, i64, i64) -> i64 { +block0(v0: i64, v1: i64, v2: i64): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i64 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x1, x2, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x1, x2, eq +; csdb +; ret + +function %f(i64, i128, i128) -> i128 { +block0(v0: i64, v1: i128, v2: i128): + v3 = iconst.i64 42 + v4 = icmp eq v0, v3 + v5 = select_spectre_guard.i128 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs xzr, x0, #42 +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp x0, #0x2a +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret + +function %f(i128, i8, i8) -> i8 { +block0(v0: i128, v1: i8, v2: i8): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select_spectre_guard.i8 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x7, #42 +; movz x8, #0 +; subs xzr, x0, x7 +; ccmp x1, x8, #nzcv, eq +; csel x0, x2, x3, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x7, #0x2a +; mov x8, #0 +; cmp x0, x7 +; ccmp x1, x8, #0, eq +; csel x0, x2, x3, eq +; csdb +; ret + +function %f(i128, i16, i16) -> i16 { +block0(v0: i128, v1: i16, v2: i16): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select_spectre_guard.i16 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x7, #42 +; movz x8, #0 +; subs xzr, x0, x7 +; ccmp x1, x8, #nzcv, eq +; csel x0, x2, x3, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x7, #0x2a +; mov x8, #0 +; cmp x0, x7 +; ccmp x1, x8, #0, eq +; csel x0, x2, x3, eq +; csdb +; ret + +function %f(i128, i32, i32) -> i32 { +block0(v0: i128, v1: i32, v2: i32): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select_spectre_guard.i32 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x7, #42 +; movz x8, #0 +; subs xzr, x0, x7 +; ccmp x1, x8, #nzcv, eq +; csel x0, x2, x3, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x7, #0x2a +; mov x8, #0 +; cmp x0, x7 +; ccmp x1, x8, #0, eq +; csel x0, x2, x3, eq +; csdb +; ret + +function %f(i128, i64, i64) -> i64 { +block0(v0: i128, v1: i64, v2: i64): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select_spectre_guard.i64 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x7, #42 +; movz x8, #0 +; subs xzr, x0, x7 +; ccmp x1, x8, #nzcv, eq +; csel x0, x2, x3, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x7, #0x2a +; mov x8, #0 +; cmp x0, x7 +; ccmp x1, x8, #0, eq +; csel x0, x2, x3, eq +; csdb +; ret + +function %f(i128, i128, i128) -> i128 { +block0(v0: i128, v1: i128, v2: i128): + v3 = iconst.i64 42 + v4 = uextend.i128 v3 + v5 = icmp eq v0, v4 + v6 = select_spectre_guard.i128 v5, v1, v2 + return v6 +} + +; VCode: +; block0: +; movz x10, #42 +; movz x11, #0 +; subs xzr, x0, x10 +; ccmp x1, x11, #nzcv, eq +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mov x10, #0x2a +; mov x11, #0 +; cmp x0, x10 +; ccmp x1, x11, #0, eq +; csel x0, x2, x4, eq +; csel x1, x3, x5, eq +; csdb +; ret + +function %g(i8) -> i8 { +block0(v0: i8): + v3 = iconst.i8 42 + v4 = icmp eq v0, v3 + return v4 +} + +; VCode: +; block0: +; uxtb w2, w0 +; subs wzr, w2, #42 +; cset x0, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; uxtb w2, w0 +; cmp w2, #0x2a +; cset x0, eq +; ret + +function %h(i8, i8, i8) -> i8 { +block0(v0: i8, v1: i8, v2: i8): + v3 = bitselect.i8 v0, v1, v2 + return v3 +} + +; VCode: +; block0: +; and w4, w1, w0 +; bic w6, w2, w0 +; orr w0, w4, w6 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; and w4, w1, w0 +; bic w6, w2, w0 +; orr w0, w4, w6 +; ret + +function %i(i8, i8, i8) -> i8 { +block0(v0: i8, v1: i8, v2: i8): + v3 = select.i8 v0, v1, v2 + return v3 +} + +; VCode: +; block0: +; ands wzr, w0, #255 +; csel x0, x1, x2, ne +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; tst w0, #0xff +; csel x0, x1, x2, ne +; ret + +function %i(i32, i8, i8) -> i8 { +block0(v0: i32, v1: i8, v2: i8): + v3 = iconst.i32 42 + v4 = icmp.i32 eq v0, v3 + v5 = select.i8 v4, v1, v2 + return v5 +} + +; VCode: +; block0: +; subs wzr, w0, #42 +; csel x0, x1, x2, eq +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #0x2a +; csel x0, x1, x2, eq +; ret + +function %i128_select(i8, i128, i128) -> i128 { +block0(v0: i8, v1: i128, v2: i128): + v3 = select.i128 v0, v1, v2 + return v3 +} + +; VCode: +; block0: +; ands wzr, w0, #255 +; csel x0, x2, x4, ne +; csel x1, x3, x5, ne +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; tst w0, #0xff +; csel x0, x2, x4, ne +; csel x1, x3, x5, ne +; ret diff --git a/cranelift/filetests/filetests/isa/aarch64/condops.clif b/cranelift/filetests/filetests/isa/aarch64/condops.clif index 63917f095115..38e78fdf0be9 100644 --- a/cranelift/filetests/filetests/isa/aarch64/condops.clif +++ b/cranelift/filetests/filetests/isa/aarch64/condops.clif @@ -580,7 +580,6 @@ block0(v0: i8, v1: i8, v2: i8): ; uxtb w4, w0 ; subs wzr, w4, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: @@ -588,7 +587,6 @@ block0(v0: i8, v1: i8, v2: i8): ; uxtb w4, w0 ; cmp w4, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i8, i16, i16) -> i16 { @@ -604,7 +602,6 @@ block0(v0: i8, v1: i16, v2: i16): ; uxtb w4, w0 ; subs wzr, w4, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: @@ -612,7 +609,6 @@ block0(v0: i8, v1: i16, v2: i16): ; uxtb w4, w0 ; cmp w4, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i8, i32, i32) -> i32 { @@ -628,7 +624,6 @@ block0(v0: i8, v1: i32, v2: i32): ; uxtb w4, w0 ; subs wzr, w4, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: @@ -636,7 +631,6 @@ block0(v0: i8, v1: i32, v2: i32): ; uxtb w4, w0 ; cmp w4, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i8, i64, i64) -> i64 { @@ -652,7 +646,6 @@ block0(v0: i8, v1: i64, v2: i64): ; uxtb w4, w0 ; subs wzr, w4, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: @@ -660,7 +653,6 @@ block0(v0: i8, v1: i64, v2: i64): ; uxtb w4, w0 ; cmp w4, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i8, i128, i128) -> i128 { @@ -677,7 +669,6 @@ block0(v0: i8, v1: i128, v2: i128): ; subs wzr, w6, #42 ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret ; ; Disassembled: @@ -686,7 +677,6 @@ block0(v0: i8, v1: i128, v2: i128): ; cmp w6, #0x2a ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret function %f(i16, i8, i8) -> i8 { @@ -702,7 +692,6 @@ block0(v0: i16, v1: i8, v2: i8): ; uxth w4, w0 ; subs wzr, w4, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: @@ -710,7 +699,6 @@ block0(v0: i16, v1: i8, v2: i8): ; uxth w4, w0 ; cmp w4, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i16, i16, i16) -> i16 { @@ -726,7 +714,6 @@ block0(v0: i16, v1: i16, v2: i16): ; uxth w4, w0 ; subs wzr, w4, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: @@ -734,7 +721,6 @@ block0(v0: i16, v1: i16, v2: i16): ; uxth w4, w0 ; cmp w4, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i16, i32, i32) -> i32 { @@ -750,7 +736,6 @@ block0(v0: i16, v1: i32, v2: i32): ; uxth w4, w0 ; subs wzr, w4, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: @@ -758,7 +743,6 @@ block0(v0: i16, v1: i32, v2: i32): ; uxth w4, w0 ; cmp w4, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i16, i64, i64) -> i64 { @@ -774,7 +758,6 @@ block0(v0: i16, v1: i64, v2: i64): ; uxth w4, w0 ; subs wzr, w4, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: @@ -782,7 +765,6 @@ block0(v0: i16, v1: i64, v2: i64): ; uxth w4, w0 ; cmp w4, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i16, i128, i128) -> i128 { @@ -799,7 +781,6 @@ block0(v0: i16, v1: i128, v2: i128): ; subs wzr, w6, #42 ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret ; ; Disassembled: @@ -808,7 +789,6 @@ block0(v0: i16, v1: i128, v2: i128): ; cmp w6, #0x2a ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret function %f(i32, i8, i8) -> i8 { @@ -823,14 +803,12 @@ block0(v0: i32, v1: i8, v2: i8): ; block0: ; subs wzr, w0, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; cmp w0, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i32, i16, i16) -> i16 { @@ -845,14 +823,12 @@ block0(v0: i32, v1: i16, v2: i16): ; block0: ; subs wzr, w0, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; cmp w0, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i32, i32, i32) -> i32 { @@ -867,14 +843,12 @@ block0(v0: i32, v1: i32, v2: i32): ; block0: ; subs wzr, w0, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; cmp w0, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i32, i64, i64) -> i64 { @@ -889,14 +863,12 @@ block0(v0: i32, v1: i64, v2: i64): ; block0: ; subs wzr, w0, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; cmp w0, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i32, i128, i128) -> i128 { @@ -912,7 +884,6 @@ block0(v0: i32, v1: i128, v2: i128): ; subs wzr, w0, #42 ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret ; ; Disassembled: @@ -920,7 +891,6 @@ block0(v0: i32, v1: i128, v2: i128): ; cmp w0, #0x2a ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret function %f(i64, i8, i8) -> i8 { @@ -935,14 +905,12 @@ block0(v0: i64, v1: i8, v2: i8): ; block0: ; subs xzr, x0, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; cmp x0, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i64, i16, i16) -> i16 { @@ -957,14 +925,12 @@ block0(v0: i64, v1: i16, v2: i16): ; block0: ; subs xzr, x0, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; cmp x0, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i64, i32, i32) -> i32 { @@ -979,14 +945,12 @@ block0(v0: i64, v1: i32, v2: i32): ; block0: ; subs xzr, x0, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; cmp x0, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i64, i64, i64) -> i64 { @@ -1001,14 +965,12 @@ block0(v0: i64, v1: i64, v2: i64): ; block0: ; subs xzr, x0, #42 ; csel x0, x1, x2, eq -; csdb ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; cmp x0, #0x2a ; csel x0, x1, x2, eq -; csdb ; ret function %f(i64, i128, i128) -> i128 { @@ -1024,7 +986,6 @@ block0(v0: i64, v1: i128, v2: i128): ; subs xzr, x0, #42 ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret ; ; Disassembled: @@ -1032,7 +993,6 @@ block0(v0: i64, v1: i128, v2: i128): ; cmp x0, #0x2a ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret function %f(i128, i8, i8) -> i8 { @@ -1051,7 +1011,6 @@ block0(v0: i128, v1: i8, v2: i8): ; subs xzr, x0, x7 ; ccmp x1, x8, #nzcv, eq ; csel x0, x2, x3, eq -; csdb ; ret ; ; Disassembled: @@ -1061,7 +1020,6 @@ block0(v0: i128, v1: i8, v2: i8): ; cmp x0, x7 ; ccmp x1, x8, #0, eq ; csel x0, x2, x3, eq -; csdb ; ret function %f(i128, i16, i16) -> i16 { @@ -1080,7 +1038,6 @@ block0(v0: i128, v1: i16, v2: i16): ; subs xzr, x0, x7 ; ccmp x1, x8, #nzcv, eq ; csel x0, x2, x3, eq -; csdb ; ret ; ; Disassembled: @@ -1090,7 +1047,6 @@ block0(v0: i128, v1: i16, v2: i16): ; cmp x0, x7 ; ccmp x1, x8, #0, eq ; csel x0, x2, x3, eq -; csdb ; ret function %f(i128, i32, i32) -> i32 { @@ -1109,7 +1065,6 @@ block0(v0: i128, v1: i32, v2: i32): ; subs xzr, x0, x7 ; ccmp x1, x8, #nzcv, eq ; csel x0, x2, x3, eq -; csdb ; ret ; ; Disassembled: @@ -1119,7 +1074,6 @@ block0(v0: i128, v1: i32, v2: i32): ; cmp x0, x7 ; ccmp x1, x8, #0, eq ; csel x0, x2, x3, eq -; csdb ; ret function %f(i128, i64, i64) -> i64 { @@ -1138,7 +1092,6 @@ block0(v0: i128, v1: i64, v2: i64): ; subs xzr, x0, x7 ; ccmp x1, x8, #nzcv, eq ; csel x0, x2, x3, eq -; csdb ; ret ; ; Disassembled: @@ -1148,7 +1101,6 @@ block0(v0: i128, v1: i64, v2: i64): ; cmp x0, x7 ; ccmp x1, x8, #0, eq ; csel x0, x2, x3, eq -; csdb ; ret function %f(i128, i128, i128) -> i128 { @@ -1168,7 +1120,6 @@ block0(v0: i128, v1: i128, v2: i128): ; ccmp x1, x11, #nzcv, eq ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret ; ; Disassembled: @@ -1179,7 +1130,6 @@ block0(v0: i128, v1: i128, v2: i128): ; ccmp x1, x11, #0, eq ; csel x0, x2, x4, eq ; csel x1, x3, x5, eq -; csdb ; ret function %g(i8) -> i8 { diff --git a/cranelift/filetests/filetests/isa/aarch64/issue-shift-masked-imm-type.clif b/cranelift/filetests/filetests/isa/aarch64/issue-shift-masked-imm-type.clif new file mode 100644 index 000000000000..9e4d5185cbc9 --- /dev/null +++ b/cranelift/filetests/filetests/isa/aarch64/issue-shift-masked-imm-type.clif @@ -0,0 +1,95 @@ +test compile precise-output +set unwind_info=false +target aarch64 + +;; Regression test: shift_masked_imm in amode_no_more_iconst must use the ishl +;; type, not the load access type. When load.i8 has ishl.i64 by 56, the old code +;; computed shift_masked_imm(I8, 56) = 56 & 7 = 0, incorrectly folding the +;; shift into a RegScaled amode with LSL #0. The correct masking is +;; shift_masked_imm(I64, 56) = 56 & 63 = 56, which does not match ty_bytes(I8) +;; and prevents the fold. + +function %load_i8_ishl56_should_not_fold(i64, i64) -> i8 { +block0(v0: i64, v1: i64): + v2 = iconst.i64 56 + v3 = ishl v1, v2 + v4 = iadd v0, v3 + v5 = load.i8 v4 + return v5 +} + +; VCode: +; block0: +; lsl x4, x1, #56 +; ldrb w0, [x0, x4] +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; lsl x4, x1, #0x38 +; ldrb w0, [x0, x4] ; trap: heap_oob +; ret + +function %load_i16_ishl17_should_not_fold(i64, i64) -> i16 { +block0(v0: i64, v1: i64): + v2 = iconst.i64 17 + v3 = ishl v1, v2 + v4 = iadd v0, v3 + v5 = load.i16 v4 + return v5 +} + +; VCode: +; block0: +; lsl x4, x1, #17 +; ldrh w0, [x0, x4] +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; lsl x4, x1, #0x11 +; ldrh w0, [x0, x4] ; trap: heap_oob +; ret + +function %load_i32_ishl34_should_not_fold(i64, i64) -> i32 { +block0(v0: i64, v1: i64): + v2 = iconst.i64 34 + v3 = ishl v1, v2 + v4 = iadd v0, v3 + v5 = load.i32 v4 + return v5 +} + +; VCode: +; block0: +; lsl x4, x1, #34 +; ldr w0, [x0, x4] +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; lsl x4, x1, #0x22 +; ldr w0, [x0, x4] ; trap: heap_oob +; ret + +;; Same as the i8 case but with iadd operands swapped +function %load_i8_ishl56_swapped_should_not_fold(i64, i64) -> i8 { +block0(v0: i64, v1: i64): + v2 = iconst.i64 56 + v3 = ishl v1, v2 + v4 = iadd v3, v0 + v5 = load.i8 v4 + return v5 +} + +; VCode: +; block0: +; lsl x4, x1, #56 +; ldrb w0, [x4, x0] +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; lsl x4, x1, #0x38 +; ldrb w0, [x4, x0] ; trap: heap_oob +; ret diff --git a/cranelift/filetests/filetests/isa/aarch64/jumptable-no-spectre.clif b/cranelift/filetests/filetests/isa/aarch64/jumptable-no-spectre.clif new file mode 100644 index 000000000000..f34789f2b735 --- /dev/null +++ b/cranelift/filetests/filetests/isa/aarch64/jumptable-no-spectre.clif @@ -0,0 +1,78 @@ +test compile precise-output +set unwind_info=false +set enable_table_access_spectre_mitigation=false +target aarch64 + +function %f(i32) -> i32 { +block0(v0: i32): + br_table v0, block4, [block1, block2, block3] + +block1: + v1 = iconst.i32 1 + jump block5(v1) + +block2: + v2 = iconst.i32 2 + jump block5(v2) + +block3: + v3 = iconst.i32 3 + jump block5(v3) + +block4: + v4 = iconst.i32 4 + jump block5(v4) + +block5(v5: i32): + v6 = iadd.i32 v0, v5 + return v6 +} + +; VCode: +; block0: +; emit_island 44 +; subs wzr, w0, #3 +; b.hs label4 ; csel x11, xzr, x0, hs ; csdb ; adr x10, pc+16 ; ldrsw x11, [x10, x11, uxtw #2] ; add x10, x10, x11 ; br x10 ; jt_entries [MachLabel(3), MachLabel(2), MachLabel(1)] +; block1: +; movz w5, #3 +; b label5 +; block2: +; movz w5, #2 +; b label5 +; block3: +; movz w5, #1 +; b label5 +; block4: +; movz w5, #4 +; b label5 +; block5: +; add w0, w0, w5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #3 +; b.hs #0x40 +; csel x11, xzr, x0, hs +; adr x10, #0x1c +; ldrsw x11, [x10, w11, uxtw #2] +; add x10, x10, x11 +; br x10 +; .byte 0x1c, 0x00, 0x00, 0x00 +; .byte 0x14, 0x00, 0x00, 0x00 +; .byte 0x0c, 0x00, 0x00, 0x00 +; block1: ; offset 0x28 +; mov w5, #3 +; b #0x44 +; block2: ; offset 0x30 +; mov w5, #2 +; b #0x44 +; block3: ; offset 0x38 +; mov w5, #1 +; b #0x44 +; block4: ; offset 0x40 +; mov w5, #4 +; block5: ; offset 0x44 +; add w0, w0, w5 +; ret + diff --git a/cranelift/filetests/filetests/isa/aarch64/jumptable.clif b/cranelift/filetests/filetests/isa/aarch64/jumptable.clif index d400f74eda5f..999ac448d26d 100644 --- a/cranelift/filetests/filetests/isa/aarch64/jumptable.clif +++ b/cranelift/filetests/filetests/isa/aarch64/jumptable.clif @@ -51,28 +51,27 @@ block5(v5: i32): ; Disassembled: ; block0: ; offset 0x0 ; cmp w0, #3 -; b.hs #0x44 +; b.hs #0x40 ; csel x11, xzr, x0, hs -; csdb -; adr x10, #0x20 +; adr x10, #0x1c ; ldrsw x11, [x10, w11, uxtw #2] ; add x10, x10, x11 ; br x10 ; .byte 0x1c, 0x00, 0x00, 0x00 ; .byte 0x14, 0x00, 0x00, 0x00 ; .byte 0x0c, 0x00, 0x00, 0x00 -; block1: ; offset 0x2c +; block1: ; offset 0x28 ; mov w5, #3 -; b #0x48 -; block2: ; offset 0x34 +; b #0x44 +; block2: ; offset 0x30 ; mov w5, #2 -; b #0x48 -; block3: ; offset 0x3c +; b #0x44 +; block3: ; offset 0x38 ; mov w5, #1 -; b #0x48 -; block4: ; offset 0x44 +; b #0x44 +; block4: ; offset 0x40 ; mov w5, #4 -; block5: ; offset 0x48 +; block5: ; offset 0x44 ; add w0, w0, w5 ; ret diff --git a/cranelift/filetests/filetests/isa/aarch64/jumptable_with_csdb.clif b/cranelift/filetests/filetests/isa/aarch64/jumptable_with_csdb.clif new file mode 100644 index 000000000000..3712b4fef91e --- /dev/null +++ b/cranelift/filetests/filetests/isa/aarch64/jumptable_with_csdb.clif @@ -0,0 +1,78 @@ +test compile precise-output +set unwind_info=false +target aarch64 use_csdb + +function %f(i32) -> i32 { +block0(v0: i32): + br_table v0, block4, [block1, block2, block3] + +block1: + v1 = iconst.i32 1 + jump block5(v1) + +block2: + v2 = iconst.i32 2 + jump block5(v2) + +block3: + v3 = iconst.i32 3 + jump block5(v3) + +block4: + v4 = iconst.i32 4 + jump block5(v4) + +block5(v5: i32): + v6 = iadd.i32 v0, v5 + return v6 +} + +; VCode: +; block0: +; emit_island 44 +; subs wzr, w0, #3 +; b.hs label4 ; csel x11, xzr, x0, hs ; csdb ; adr x10, pc+16 ; ldrsw x11, [x10, x11, uxtw #2] ; add x10, x10, x11 ; br x10 ; jt_entries [MachLabel(3), MachLabel(2), MachLabel(1)] +; block1: +; movz w5, #3 +; b label5 +; block2: +; movz w5, #2 +; b label5 +; block3: +; movz w5, #1 +; b label5 +; block4: +; movz w5, #4 +; b label5 +; block5: +; add w0, w0, w5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; cmp w0, #3 +; b.hs #0x44 +; csel x11, xzr, x0, hs +; csdb +; adr x10, #0x20 +; ldrsw x11, [x10, w11, uxtw #2] +; add x10, x10, x11 +; br x10 +; .byte 0x1c, 0x00, 0x00, 0x00 +; .byte 0x14, 0x00, 0x00, 0x00 +; .byte 0x0c, 0x00, 0x00, 0x00 +; block1: ; offset 0x2c +; mov w5, #3 +; b #0x48 +; block2: ; offset 0x34 +; mov w5, #2 +; b #0x48 +; block3: ; offset 0x3c +; mov w5, #1 +; b #0x48 +; block4: ; offset 0x44 +; mov w5, #4 +; block5: ; offset 0x48 +; add w0, w0, w5 +; ret + diff --git a/cranelift/filetests/filetests/isa/aarch64/preserve-all.clif b/cranelift/filetests/filetests/isa/aarch64/preserve-all.clif index f361163f9864..2f3672baf409 100644 --- a/cranelift/filetests/filetests/isa/aarch64/preserve-all.clif +++ b/cranelift/filetests/filetests/isa/aarch64/preserve-all.clif @@ -53,41 +53,73 @@ block0(v0: i64): ; stp x4, x5, [sp, #-16]! ; stp x2, x3, [sp, #-16]! ; stp x0, x1, [sp, #-16]! -; stp d30, d31, [sp, #-16]! -; stp d28, d29, [sp, #-16]! -; stp d26, d27, [sp, #-16]! -; stp d24, d25, [sp, #-16]! -; stp d22, d23, [sp, #-16]! -; stp d20, d21, [sp, #-16]! -; stp d18, d19, [sp, #-16]! -; stp d16, d17, [sp, #-16]! -; stp d14, d15, [sp, #-16]! -; stp d12, d13, [sp, #-16]! -; stp d10, d11, [sp, #-16]! -; stp d8, d9, [sp, #-16]! -; stp d6, d7, [sp, #-16]! -; stp d4, d5, [sp, #-16]! -; stp d2, d3, [sp, #-16]! -; stp d0, d1, [sp, #-16]! +; str q31, [sp, #-16]! +; str q30, [sp, #-16]! +; str q29, [sp, #-16]! +; str q28, [sp, #-16]! +; str q27, [sp, #-16]! +; str q26, [sp, #-16]! +; str q25, [sp, #-16]! +; str q24, [sp, #-16]! +; str q23, [sp, #-16]! +; str q22, [sp, #-16]! +; str q21, [sp, #-16]! +; str q20, [sp, #-16]! +; str q19, [sp, #-16]! +; str q18, [sp, #-16]! +; str q17, [sp, #-16]! +; str q16, [sp, #-16]! +; str q15, [sp, #-16]! +; str q14, [sp, #-16]! +; str q13, [sp, #-16]! +; str q12, [sp, #-16]! +; str q11, [sp, #-16]! +; str q10, [sp, #-16]! +; str q9, [sp, #-16]! +; str q8, [sp, #-16]! +; str q7, [sp, #-16]! +; str q6, [sp, #-16]! +; str q5, [sp, #-16]! +; str q4, [sp, #-16]! +; str q3, [sp, #-16]! +; str q2, [sp, #-16]! +; str q1, [sp, #-16]! +; str q0, [sp, #-16]! ; block0: ; load_ext_name_far x2, TestCase(%libcall)+0 ; blr x2 -; ldp d0, d1, [sp], #16 -; ldp d2, d3, [sp], #16 -; ldp d4, d5, [sp], #16 -; ldp d6, d7, [sp], #16 -; ldp d8, d9, [sp], #16 -; ldp d10, d11, [sp], #16 -; ldp d12, d13, [sp], #16 -; ldp d14, d15, [sp], #16 -; ldp d16, d17, [sp], #16 -; ldp d18, d19, [sp], #16 -; ldp d20, d21, [sp], #16 -; ldp d22, d23, [sp], #16 -; ldp d24, d25, [sp], #16 -; ldp d26, d27, [sp], #16 -; ldp d28, d29, [sp], #16 -; ldp d30, d31, [sp], #16 +; ldr q0, [sp], #16 +; ldr q1, [sp], #16 +; ldr q2, [sp], #16 +; ldr q3, [sp], #16 +; ldr q4, [sp], #16 +; ldr q5, [sp], #16 +; ldr q6, [sp], #16 +; ldr q7, [sp], #16 +; ldr q8, [sp], #16 +; ldr q9, [sp], #16 +; ldr q10, [sp], #16 +; ldr q11, [sp], #16 +; ldr q12, [sp], #16 +; ldr q13, [sp], #16 +; ldr q14, [sp], #16 +; ldr q15, [sp], #16 +; ldr q16, [sp], #16 +; ldr q17, [sp], #16 +; ldr q18, [sp], #16 +; ldr q19, [sp], #16 +; ldr q20, [sp], #16 +; ldr q21, [sp], #16 +; ldr q22, [sp], #16 +; ldr q23, [sp], #16 +; ldr q24, [sp], #16 +; ldr q25, [sp], #16 +; ldr q26, [sp], #16 +; ldr q27, [sp], #16 +; ldr q28, [sp], #16 +; ldr q29, [sp], #16 +; ldr q30, [sp], #16 +; ldr q31, [sp], #16 ; ldp x0, x1, [sp], #16 ; ldp x2, x3, [sp], #16 ; ldp x4, x5, [sp], #16 @@ -113,44 +145,76 @@ block0(v0: i64): ; stp x4, x5, [sp, #-0x10]! ; stp x2, x3, [sp, #-0x10]! ; stp x0, x1, [sp, #-0x10]! -; stp d30, d31, [sp, #-0x10]! -; stp d28, d29, [sp, #-0x10]! -; stp d26, d27, [sp, #-0x10]! -; stp d24, d25, [sp, #-0x10]! -; stp d22, d23, [sp, #-0x10]! -; stp d20, d21, [sp, #-0x10]! -; stp d18, d19, [sp, #-0x10]! -; stp d16, d17, [sp, #-0x10]! -; stp d14, d15, [sp, #-0x10]! -; stp d12, d13, [sp, #-0x10]! -; stp d10, d11, [sp, #-0x10]! -; stp d8, d9, [sp, #-0x10]! -; stp d6, d7, [sp, #-0x10]! -; stp d4, d5, [sp, #-0x10]! -; stp d2, d3, [sp, #-0x10]! -; stp d0, d1, [sp, #-0x10]! -; block1: ; offset 0x6c -; ldr x2, #0x74 -; b #0x7c +; str q31, [sp, #-0x10]! +; str q30, [sp, #-0x10]! +; str q29, [sp, #-0x10]! +; str q28, [sp, #-0x10]! +; str q27, [sp, #-0x10]! +; str q26, [sp, #-0x10]! +; str q25, [sp, #-0x10]! +; str q24, [sp, #-0x10]! +; str q23, [sp, #-0x10]! +; str q22, [sp, #-0x10]! +; str q21, [sp, #-0x10]! +; str q20, [sp, #-0x10]! +; str q19, [sp, #-0x10]! +; str q18, [sp, #-0x10]! +; str q17, [sp, #-0x10]! +; str q16, [sp, #-0x10]! +; str q15, [sp, #-0x10]! +; str q14, [sp, #-0x10]! +; str q13, [sp, #-0x10]! +; str q12, [sp, #-0x10]! +; str q11, [sp, #-0x10]! +; str q10, [sp, #-0x10]! +; str q9, [sp, #-0x10]! +; str q8, [sp, #-0x10]! +; str q7, [sp, #-0x10]! +; str q6, [sp, #-0x10]! +; str q5, [sp, #-0x10]! +; str q4, [sp, #-0x10]! +; str q3, [sp, #-0x10]! +; str q2, [sp, #-0x10]! +; str q1, [sp, #-0x10]! +; str q0, [sp, #-0x10]! +; block1: ; offset 0xac +; ldr x2, #0xb4 +; b #0xbc ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %libcall 0 ; .byte 0x00, 0x00, 0x00, 0x00 ; blr x2 -; ldp d0, d1, [sp], #0x10 -; ldp d2, d3, [sp], #0x10 -; ldp d4, d5, [sp], #0x10 -; ldp d6, d7, [sp], #0x10 -; ldp d8, d9, [sp], #0x10 -; ldp d10, d11, [sp], #0x10 -; ldp d12, d13, [sp], #0x10 -; ldp d14, d15, [sp], #0x10 -; ldp d16, d17, [sp], #0x10 -; ldp d18, d19, [sp], #0x10 -; ldp d20, d21, [sp], #0x10 -; ldp d22, d23, [sp], #0x10 -; ldp d24, d25, [sp], #0x10 -; ldp d26, d27, [sp], #0x10 -; ldp d28, d29, [sp], #0x10 -; ldp d30, d31, [sp], #0x10 +; ldr q0, [sp], #0x10 +; ldr q1, [sp], #0x10 +; ldr q2, [sp], #0x10 +; ldr q3, [sp], #0x10 +; ldr q4, [sp], #0x10 +; ldr q5, [sp], #0x10 +; ldr q6, [sp], #0x10 +; ldr q7, [sp], #0x10 +; ldr q8, [sp], #0x10 +; ldr q9, [sp], #0x10 +; ldr q10, [sp], #0x10 +; ldr q11, [sp], #0x10 +; ldr q12, [sp], #0x10 +; ldr q13, [sp], #0x10 +; ldr q14, [sp], #0x10 +; ldr q15, [sp], #0x10 +; ldr q16, [sp], #0x10 +; ldr q17, [sp], #0x10 +; ldr q18, [sp], #0x10 +; ldr q19, [sp], #0x10 +; ldr q20, [sp], #0x10 +; ldr q21, [sp], #0x10 +; ldr q22, [sp], #0x10 +; ldr q23, [sp], #0x10 +; ldr q24, [sp], #0x10 +; ldr q25, [sp], #0x10 +; ldr q26, [sp], #0x10 +; ldr q27, [sp], #0x10 +; ldr q28, [sp], #0x10 +; ldr q29, [sp], #0x10 +; ldr q30, [sp], #0x10 +; ldr q31, [sp], #0x10 ; ldp x0, x1, [sp], #0x10 ; ldp x2, x3, [sp], #0x10 ; ldp x4, x5, [sp], #0x10 diff --git a/cranelift/filetests/filetests/isa/aarch64/simd.clif b/cranelift/filetests/filetests/isa/aarch64/simd.clif index bf01fc4bbf5b..997e4660952f 100644 --- a/cranelift/filetests/filetests/isa/aarch64/simd.clif +++ b/cranelift/filetests/filetests/isa/aarch64/simd.clif @@ -33,14 +33,14 @@ block0: ; VCode: ; block0: -; movz x0, #42679 -; dup v0.8h, w0 +; movz w1, #42679 +; dup v0.8h, w1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mov x0, #0xa6b7 -; dup v0.8h, w0 +; mov w1, #0xa6b7 +; dup v0.8h, w1 ; ret function %f4(i32, i8x16, i8x16) -> i8x16 { diff --git a/cranelift/filetests/filetests/isa/riscv64/issue-12811.clif b/cranelift/filetests/filetests/isa/riscv64/issue-12811.clif new file mode 100644 index 000000000000..c6286a8ce1e7 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/issue-12811.clif @@ -0,0 +1,8273 @@ +test compile precise-output +set opt_level=speed_and_size +set is_pic +target riscv64 has_m has_a has_f has_d has_zca has_zcd + +function u0:388(i64 vmctx) tail { + gv0 = vmctx + gv1 = load.i64 notrap aligned readonly gv0+8 + gv2 = load.i64 notrap aligned gv1+16 + gv3 = vmctx + gv4 = load.i64 notrap aligned readonly can_move gv3+48 + gv5 = load.i64 notrap aligned gv4+8 + gv6 = load.i64 notrap aligned readonly can_move gv4 + sig0 = (i64 vmctx, i64, i32) tail + sig1 = (i64 vmctx, i64, i32, i32) tail + fn0 = colocated u0:988 sig0 + fn1 = colocated u0:1 sig1 + stack_limit = gv2 + +block0(v0: i64): + v8 = iconst.i32 0 + v10 = iconst.i32 0 + v13 = iconst.i32 0x6a64_d003 + v14 = iconst.i32 0x259a_c9da + v17 = iconst.i64 0 + v24211 = iconst.i32 0 + v58 = iconst.i64 0 + v65 = iconst.i64 0 + v102 = iconst.i32 0 + v104 = iconst.i32 0 + v108 = iconst.i64 0 + v24217 = iconst.i32 1 + v24219 = iconst.i32 0 + v178 = iconst.i64 0 + v201 = iconst.i64 0 + v232 = iconst.i32 0 + v24233 = iconst.i32 1 + v24236 = iconst.i32 0 + v24250 = iconst.i64 0 + v24254 = iconst.i32 0 + v375 = iconst.i64 0 + v24259 = iconst.i32 0 + v24263 = iconst.i32 0 + v24264 = iconst.i64 0 + v22186 = iconst.i64 0x9829_0f25 + v24266 = iconst.i32 0 + v24269 = iconst.i32 0 + v24275 = iconst.i32 0 + v24278 = iconst.i32 0 + v24292 = iconst.i32 0 + v725 = iconst.i32 0x7c7d_8f3f + v24303 = iconst.i32 0 + v24307 = iconst.i32 1 + v862 = iconst.i32 0x5b1f_26e3 + v24322 = iconst.i32 1 + v24324 = iconst.i32 0 + v24334 = iconst.i32 1 + v24338 = iconst.i32 0 + v24349 = iconst.i32 1 + v24351 = iconst.i64 0 + v1053 = iconst.i32 0x6f8d_0ebc + v24364 = iconst.i32 0 + v1193 = iconst.i32 0x484e_1b88 + v24382 = iconst.i32 1 + v24385 = iconst.i32 1 + v24393 = iconst.i32 0 + v1307 = iconst.i32 0x110e_499e + v24401 = iconst.i32 0 + v24410 = iconst.i32 1 + v1472 = iconst.i32 0x71fd_2446 + v24429 = iconst.i32 0 + v24446 = iconst.i32 0 + v1721 = iconst.i32 -2110270025 + v1728 = iconst.i32 0 + v24475 = iconst.i64 4 + v24477 = iconst.i32 1 + v1762 = iconst.i64 -1189234569106998626 + v24483 = iconst.i64 0 + v24484 = iconst.i64 -3125806403652056647 + v24485 = iconst.i64 0 + v24488 = iconst.i32 0 + v1786 = iconst.i64 0 + v1813 = iconst.i64 -1815653915230829749 + v1818 = iconst.i64 0 + v24490 = iconst.i64 1 + v1843 = iconst.i64 0x1966_535e_8fd6_e899 + v1848 = iconst.i64 0 + v1849 = iconst.i64 -6463255809729266144 + v24492 = iconst.i64 0 + v24499 = iconst.i64 8 + v1878 = iconst.i64 -215881931355568304 + v24502 = iconst.i64 0 + v22564 = iconst.i64 0xf94c_74e1 + v22570 = iadd v17, v22564 + v1962 = iconst.i64 0x693e_8091_c42f_a14d + v2010 = iconst.i64 -5918764626053886762 + v2015 = iconst.i64 0 + v2016 = iconst.i64 -2727724485612306603 + v2021 = iconst.i64 0 + v2109 = iconst.i64 0x1b95_1541_a025_39cc + v2114 = iconst.i64 0 + v2115 = iconst.i64 -4121029925073297973 + v2120 = iconst.i64 0 + v2135 = iconst.i64 0 + v2138 = iconst.i64 0 + store little heap v2135, v2138 + v22604 = iconst.i64 0xa575_8d25 + v22610 = iadd v17, v22604 + v2172 = iconst.i64 0 + v2178 = iconst.i64 0 + v2233 = iconst.i64 0 + v2239 = iconst.i64 0 + v2245 = iconst.i64 0 + v2252 = iconst.i64 -1080571076602855166 + v2257 = iconst.i64 0 + v2258 = iconst.i64 0x3fe6_6069_72af_4620 + v2263 = iconst.i64 0 + v2282 = iconst.i64 0 + v2288 = iconst.i64 0 + v2293 = iconst.i64 0 + v2316 = iconst.i64 0x694b_650c_8765_dde3 + v2325 = iconst.i64 0 + v2331 = iconst.i64 0 + v2335 = iconst.i64 0x7644_47b1_426b_e08d + v2344 = iconst.i64 0 + store little heap v24488, v17 + v2402 = iconst.i64 0 + v2419 = iconst.i64 0 + v22655 = iconst.i64 0xdb0f_f6d3 + v2430 = iadd v17, v22655 + v2455 = iconst.i64 0 + v2472 = iconst.i64 0 + v2478 = iconst.i64 0 + v2484 = iconst.i64 0 + v2485 = iconst.i64 -1631205654047087254 + v2490 = iconst.i64 0 + v2496 = iconst.i64 0 + v2502 = iconst.i64 0 + v2508 = iconst.i64 0 + v2514 = iconst.i64 0 + v2515 = iconst.i64 0x2847_e6df_85fa_9bcc + v2520 = iconst.i64 0 + v2526 = iconst.i64 0 + v2532 = iconst.i64 0 + v2538 = iconst.i64 0 + v2545 = iconst.i64 -3266833041445772419 + v2550 = iconst.i64 0 + v2551 = iconst.i64 -1040596166663495080 + v2556 = iconst.i64 0 + v2561 = iconst.i64 0 + v2591 = iconst.i64 0 + v2624 = iconst.i64 0 + v24511 = iconst.i32 3 + v24512 = iconst.i32 8 + v2654 = iconst.i64 0 + v2666 = iconst.i64 0 + v2672 = iconst.i64 0 + v2678 = iconst.i64 0 + v2679 = iconst.i64 0x3655_06bc_a5f5_6676 + v2684 = iconst.i64 0 + v2685 = iconst.i64 0x301c_f70b_6836_22f2 + v2690 = iconst.i64 0 + v2696 = iconst.i64 0 + v2702 = iconst.i64 0 + v2708 = iconst.i64 0 + v22676 = iconst.i64 0xb24c_7a77 + v22682 = iadd v17, v22676 + v22686 = iconst.i64 0x9003_66cd + v22692 = iadd v17, v22686 + v2728 = iconst.i64 0 + v2735 = iconst.i64 0 + v2740 = iconst.i64 0 + v2741 = iconst.i64 0x6680_8aa1_a0ff_dfe8 + v2746 = iconst.i64 0 + v2752 = iconst.i64 0 + v2757 = iconst.i64 0 + v2766 = iconst.i64 0 + v2784 = iconst.i64 0 + store little heap v24502, v2784 + v22694 = iconst.i64 0xd31a_4fa5 + v2795 = iadd v17, v22694 + v2801 = iconst.i64 0 + v2812 = iconst.i64 0 + v2822 = iconst.i64 0 + v2838 = iconst.i64 0 + v2844 = iconst.i64 0 + v2850 = iconst.i64 0 + v2851 = iconst.i64 -4133557854234934585 + v2856 = iconst.i64 0 + v2857 = iconst.i64 0x03dc_56fc_bef0_743d + v2862 = iconst.i64 0 + v2868 = iconst.i64 0 + v2874 = iconst.i64 0 + v2881 = iconst.i64 0x69ba_ca21_133a_2b35 + v2886 = iconst.i64 0 + v2892 = iconst.i64 0 + v2898 = iconst.i64 0 + v2901 = iconst.i64 0 + v2907 = iconst.i64 0 + v2912 = iconst.i64 0 + v2917 = iconst.i64 0 + v2925 = iconst.i64 0 + v2937 = iconst.i64 0 + v2951 = iconst.i64 0 + v2989 = iconst.i64 0 + v2996 = iconst.i64 0 + v3008 = iconst.i64 0 + v22705 = iconst.i64 0xa6e2_34f5 + v3037 = iadd v17, v22705 + v3043 = iconst.i64 0 + v3096 = iconst.i64 -3064438737746659866 + v3117 = iconst.i64 0 + v3122 = iconst.i64 0 + v22727 = iconst.i64 0 + store little heap v24502, v22727 + v3130 = iconst.i64 0x0d0a_8676_0499_d6a2 + v3135 = iconst.i64 0 + store little heap v24502, v17 + v3146 = iconst.i64 0 + v3152 = iconst.i64 0 + v3158 = iconst.i64 0x7043_623e_9ceb_ec55 + v3167 = iconst.i64 0 + v3178 = iconst.i64 0 + v3192 = iconst.i64 -3731921677327922329 + v3197 = iconst.i64 0 + v3203 = iconst.i64 0 + v22736 = iconst.i64 0 + v3219 = iconst.i64 0 + v3225 = iconst.i64 0 + v3231 = iconst.i64 0 + v3237 = iconst.i64 0 + v3247 = iconst.i64 0x05ec_e962_969f_4dac + v3250 = iconst.i64 0 + v3276 = iconst.i64 0 + v3292 = iconst.i64 0 + v3298 = iconst.i64 0 + v3324 = iconst.i64 -2599277520415467421 + v3381 = iconst.i64 0 + v3387 = iconst.i64 0 + v22756 = iconst.i64 0 + v3436 = iconst.i64 0 + v3442 = iconst.i64 0 + v3454 = iconst.i64 0 + v3460 = iconst.i64 0 + v3472 = iconst.i64 0 + v3478 = iconst.i64 0 + v3494 = iconst.i64 0 + v3547 = iconst.i64 0 + v3553 = iconst.i64 0 + v3559 = iconst.i64 0 + v3565 = iconst.i64 0 + v3571 = iconst.i64 0 + v3572 = iconst.i64 0x70cb_8dc2_bb2e_28b1 + v3577 = iconst.i64 0 + v3588 = iconst.i64 0 + v22782 = iconst.i64 0 + v3609 = iconst.i64 0 + v3621 = iconst.i64 0 + v3625 = iconst.i64 -6362659692744213906 + v3645 = iconst.i64 0 + v3651 = iconst.i64 0 + v3657 = iconst.i64 0 + v3668 = iconst.i64 0 + v3680 = iconst.i64 0 + v22792 = iconst.i64 0 + store little heap v24502, v22792 + v3731 = iconst.i64 0 + v3742 = iconst.i64 0 + v3748 = iconst.i64 0 + v3766 = iconst.i64 0 + v3772 = iconst.i64 0 + v3778 = iconst.i64 0 + v3791 = iconst.i64 0 + v3792 = iconst.i64 -9098932586818364787 + v3797 = iconst.i64 0 + v3803 = iconst.i64 0 + v3842 = iconst.i64 0 + v22797 = iconst.i64 0xe469_6bb1 + v22803 = iadd v17, v22797 + v3877 = iconst.i64 0x758c_35aa_c7f9_73c1 + v22812 = iconst.i64 0 + v3893 = iconst.i64 0 + v3924 = iconst.i64 0 + v3938 = iconst.i64 0 + v3949 = iconst.i64 0 + v3955 = iconst.i64 0 + v3961 = iconst.i64 0 + v3967 = iconst.i64 0 + v3968 = iconst.i64 0x06df_0bbe_a599_cc3d + v3973 = iconst.i64 0 + v22817 = iconst.i64 0xe25f_f8bd + v22823 = iadd v17, v22817 + v3986 = iconst.i64 0 + v3998 = iconst.i64 0 + v4005 = iconst.i64 0 + v4006 = iconst.i64 -2528897731034940525 + v4011 = iconst.i64 0 + v4018 = iconst.i64 0 + v22842 = iconst.i64 0 + v4037 = iconst.i64 0 + v4038 = iconst.i64 -3326960962371663458 + v4043 = iconst.i64 0 + v4049 = iconst.i64 0 + v4055 = iconst.i64 0 + v4061 = iconst.i64 0 + v4063 = iconst.i64 -1599524888362908605 + v4068 = iconst.i64 0 + v4081 = iconst.i64 0 + v4092 = iconst.i64 0 + v4149 = iconst.i64 0 + v22851 = iconst.i64 0 + v4163 = iconst.i64 -380333440441163971 + v4168 = iconst.i64 0 + v4174 = iconst.i64 0 + v4180 = iconst.i64 0 + v4194 = iconst.i64 0x751c_db17_5032_6e53 + v4199 = iconst.i64 0 + v4211 = iconst.i64 0 + v4216 = iconst.i64 0 + v4217 = iconst.i64 -63027115626651990 + v4222 = iconst.i64 0 + v4223 = iconst.i64 -3470792264301098512 + v4228 = iconst.i64 0 + v4233 = iconst.i64 0 + v4244 = iconst.i64 0 + v4249 = iconst.i64 0 + v4260 = iconst.i64 0 + v4277 = iconst.i64 0 + v4283 = iconst.i64 0 + v22871 = iconst.i64 0 + v4312 = iconst.i64 0x166e_68e3_df15_40fe + v22875 = iconst.i64 0xec96_de25 + v22881 = iadd v17, v22875 + v4359 = iconst.i64 0 + v4379 = iconst.i64 0 + v4395 = iconst.i64 0 + v4412 = iconst.i64 0 + v4431 = iconst.i64 0 + v4448 = iconst.i64 0 + v4454 = iconst.i64 0 + v4462 = iconst.i64 -6256061542819568466 + v4465 = iconst.i64 0 + v4471 = iconst.i64 0 + v4476 = iconst.i64 0 + store little heap v4471, v4476 + v4506 = iconst.i64 0 + v4512 = iconst.i64 0 + v4524 = iconst.i64 0 + v4542 = iconst.i64 0 + v4583 = iconst.i64 0 + v24524 = iconst.i64 0 + v4611 = iconst.i64 0 + v22901 = iconst.i64 0xe339_7913 + v22907 = iadd v17, v22901 + v24526 = iconst.i64 0 + v24528 = iconst.i64 0 + v4642 = iconst.i64 0 + v4643 = iconst.i64 -3750999391305143603 + v4648 = iconst.i64 0 + v4665 = iconst.i64 0 + v24530 = iconst.i64 0 + v24532 = iconst.i64 0 + v4676 = iconst.i64 -1662910503669140302 + v4681 = iconst.i64 0 + v22934 = iconst.i64 0 + v24534 = iconst.i64 0 + v24536 = iconst.i64 0 + v4713 = iconst.i64 0 + v24539 = iconst.i64 0 + v4758 = iconst.i64 0 + v4785 = iconst.i64 0 + v4791 = iconst.i64 0 + v4798 = iconst.i64 0x3567_7460_f576_64a1 + v4803 = iconst.i64 0 + v4804 = iconst.i64 -1294563631327298950 + v4809 = iconst.i64 0 + v4815 = iconst.i64 0 + v4821 = iconst.i64 0 + v22948 = iconst.i64 0xce13_465b + v22954 = iadd v17, v22948 + v4835 = iconst.i64 0x50f6_e9ad_5da0_d274 + v4840 = iconst.i64 0 + v4846 = iconst.i64 0 + v4852 = iconst.i64 0 + v4859 = iconst.i64 0x3c40_721c_2ac8_b803 + v4864 = iconst.i64 0 + v4870 = iconst.i64 0 + v4915 = iconst.i64 0 + v4921 = iconst.i64 0 + v4927 = iconst.i64 0 + v4933 = iconst.i64 0 + v4939 = iconst.i64 0 + v4945 = iconst.i64 0 + store little heap v24475, v4945 + v4966 = iconst.i64 0 + store little heap v24502, v4966 + v4977 = iconst.i64 0 + v4987 = iconst.i64 0 + v5022 = iconst.i64 0 + v5030 = iconst.i64 0x195b_7b30_776c_0aee + v22964 = iconst.i64 0 + v22973 = iconst.i64 0 + v5048 = iconst.i64 0 + v5054 = iconst.i64 0 + v5061 = iconst.i64 0x26c3_9678_48a6_bd6f + v5066 = iconst.i64 0 + v5067 = iconst.i64 -6748914763644999742 + v5072 = iconst.i64 0 + v5084 = iconst.i64 0 + v5091 = iconst.i64 0x36ab_9404_0ca2_8475 + v5096 = iconst.i64 0 + v5098 = iconst.i64 -3941160104288148401 + v22982 = iconst.i64 0 + v5109 = iconst.i32 0 + v5114 = iconst.i64 0 + v5127 = iconst.i64 0 + v22991 = iconst.i64 0 + v5146 = iconst.i64 0 + v5157 = iconst.i64 0 + v5168 = iconst.i64 0 + v5174 = iconst.i64 0 + v23001 = iconst.i64 0 + v5194 = iconst.i64 0 + v5224 = iconst.i64 0 + v5229 = iconst.i64 0 + v5230 = iconst.i64 -943862128230135015 + v5235 = iconst.i64 0 + v5242 = iconst.i64 0 + v5248 = iconst.i64 0 + v5254 = iconst.i64 0 + v5261 = iconst.i64 -3531309504982176884 + v5266 = iconst.i64 0 + v23010 = iconst.i64 0 + v5279 = iconst.i64 0 + v5285 = iconst.i64 0 + v5292 = iconst.i64 0x3769_6193_5710_f733 + v5297 = iconst.i64 0 + v5298 = iconst.i64 -1629047801239818028 + v5303 = iconst.i64 0 + v23012 = iconst.i64 0xc223_b689 + v5309 = iadd v17, v23012 + v5359 = iconst.i64 0 + v5372 = iconst.i64 0 + v5378 = iconst.i64 0 + v5404 = iconst.i64 0 + v5410 = iconst.i64 0 + v5421 = iconst.i64 0 + v5439 = iconst.i64 0 + v5445 = iconst.i64 0 + v5457 = iconst.i64 0 + v5458 = iconst.i64 0x036e_0c08_410f_8772 + v5463 = iconst.i64 0 + v5464 = iconst.i64 -3699244067484155154 + v5469 = iconst.i64 0 + v5475 = iconst.i64 0 + v5481 = iconst.i64 0 + v5488 = iconst.i64 0x7f99_52c6_ecdf_d001 + v5493 = iconst.i64 0 + v5494 = iconst.i64 -2996804676234760916 + v5499 = iconst.i64 0 + v5505 = iconst.i64 0 + v5511 = iconst.i64 0 + v5518 = iconst.i64 -2540435248737108768 + v5523 = iconst.i64 0 + v5529 = iconst.i64 0 + v5538 = iconst.i64 0 + v5544 = iconst.i64 0 + v5563 = iconst.i64 0 + v5571 = iconst.i64 -6689368337086891592 + v5585 = iconst.i64 0 + v5591 = iconst.i64 0 + v5605 = iconst.i64 0 + v5644 = iconst.i64 0 + v5649 = iconst.i64 0 + v5674 = iconst.i64 0x1764_a1b1_d096_3938 + v5683 = iconst.i64 0 + v23023 = iconst.i64 0xca73_ba65 + v5688 = iadd v17, v23023 + v23031 = iconst.i64 0 + v5786 = iconst.i64 0 + v5792 = iconst.i64 0 + v5804 = iconst.i64 0 + v5810 = iconst.i64 0 + v5822 = iconst.i64 0 + v5847 = iconst.i64 0 + v5863 = iconst.i64 0 + v5886 = iconst.i64 0 + v5891 = iconst.i64 0 + v5892 = iconst.i64 -3818850384249184361 + v5897 = iconst.i64 0 + v5909 = iconst.i64 0 + v5915 = iconst.i64 0 + v5922 = iconst.i64 -3624976395942583737 + v5927 = iconst.i64 0 + v5939 = iconst.i64 0 + v5945 = iconst.i64 0 + v5951 = iconst.i64 0 + v5963 = iconst.i64 0 + v5968 = iconst.i64 0 + v5979 = iconst.i64 0 + v24543 = iconst.i64 9 + v5985 = iconst.i64 0 + v5997 = iconst.i64 0 + v23040 = iconst.i64 0 + v6059 = iconst.i64 0 + v6065 = iconst.i64 0 + v6072 = iconst.i64 -2279117953864985485 + v6077 = iconst.i64 0 + v6083 = iconst.i64 0 + v6089 = iconst.i64 0 + v6095 = iconst.i64 0 + v6104 = iconst.i64 -1326975163803121187 + v6109 = iconst.i64 0 + v6116 = iconst.i64 0 + v6122 = iconst.i64 0 + v6128 = iconst.i64 0 + v6135 = iconst.i64 -778819862258901658 + v6140 = iconst.i64 0 + v6194 = iconst.i64 0 + v6205 = iconst.i64 0x2e3e_955f_1f7c_7c40 + v6208 = iconst.i64 0 + v6214 = iconst.i64 0 + v6225 = iconst.i64 0 + v6234 = iconst.i64 0 + v6245 = iconst.i64 -9223372036854775801 + v6250 = iconst.i64 0 + v6255 = iconst.i64 0 + v6261 = iconst.i64 0 + v6266 = iconst.i64 0 + v6272 = iconst.i64 0 + v6280 = iconst.i64 -1903094205370759427 + v6285 = iconst.i64 0 + v6291 = iconst.i64 0 + v6303 = iconst.i64 0 + v6304 = iconst.i64 -1651222978253018882 + v6309 = iconst.i64 0 + v23099 = iconst.i64 0 + v6322 = iconst.i64 0 + v6328 = iconst.i64 0 + v6335 = iconst.i64 -369925497731584443 + v6340 = iconst.i64 0 + v6341 = iconst.i64 -1578988967238973528 + v6346 = iconst.i64 0 + v6351 = iconst.i64 0 + v6368 = iconst.i64 0 + v6374 = iconst.i64 0 + v23108 = iconst.i64 0 + v6387 = iconst.i64 0 + v24545 = iadd v17, v22186 + v23117 = iconst.i64 0 + v6437 = iconst.i64 0 + v6461 = iconst.i64 0 + v6467 = iconst.i64 0 + v6473 = iconst.i64 0 + v6479 = iconst.i64 0 + v6480 = iconst.i64 -5785179674186779315 + v6485 = iconst.i64 0 + v6487 = iconst.i64 -5706842645921394637 + v23126 = iconst.i64 0 + v6498 = iconst.i64 0 + v6504 = iconst.i64 0 + v6511 = iconst.i64 -1111105240221470428 + v6516 = iconst.i64 0 + v6522 = iconst.i64 0 + v6528 = iconst.i64 0 + v6534 = iconst.i64 0 + v6540 = iconst.i64 0 + v6546 = iconst.i64 0 + v6547 = iconst.i64 0x2dd4_4069_3595_5a0f + v6552 = iconst.i64 0 + v6557 = iconst.i64 0 + v6574 = iconst.i64 0 + v6580 = iconst.i64 0 + v6585 = iconst.i64 0 + v6591 = iconst.i64 0 + v6603 = iconst.i64 0 + v6624 = iconst.i64 0x1f7c_ccc5_1df9_846c + v6629 = iconst.i64 0 + v6636 = iconst.i64 0 + v6676 = iconst.i64 0 + v6682 = iconst.i64 0 + v6688 = iconst.i64 0 + v6700 = iconst.i64 0 + v6713 = iconst.i64 0 + v6719 = iconst.i64 0 + v23157 = iconst.i64 0 + v23166 = iconst.i64 0 + v6733 = iconst.i64 0x27c0_7926_d8cb_a478 + v6738 = iconst.i64 0 + v6744 = iconst.i64 0 + v6750 = iconst.i64 0 + v6756 = iconst.i64 0 + v6758 = iconst.i64 0x0ccf_c045_b4ff_7c50 + v23176 = iconst.i64 0 + v6783 = iconst.i64 0 + v23185 = iconst.i64 0 + v6796 = iconst.i64 0 + v6810 = iconst.i64 0x1825_a1df_a6a7_6ba7 + v6815 = iconst.i64 0 + v23194 = iconst.i64 0 + v6840 = iconst.i64 0 + v6882 = iconst.i64 0 + v6888 = iconst.i64 0 + v6894 = iconst.i64 0 + v6899 = iconst.i64 0 + v6905 = iconst.i64 0 + v6906 = iconst.i64 0x3c38_88a3_5d9d_6df0 + v6911 = iconst.i64 0 + v6917 = iconst.i64 0 + v6923 = iconst.i64 0 + v6930 = iconst.i64 -2846509815769218309 + v6935 = iconst.i64 0 + v6936 = iconst.i64 0x398c_dc58_23bc_e6c0 + v6941 = iconst.i64 0 + v6953 = iconst.i64 0 + v6958 = iconst.i64 0 + v6959 = iconst.i64 -4281926801480074896 + v6964 = iconst.i64 0 + v23212 = iconst.i64 0xbe73_b30b + v23218 = iadd v17, v23212 + v6976 = iconst.i64 0 + v6985 = iconst.i64 0 + v6997 = iconst.i64 0 + v7002 = iconst.i64 0 + v7026 = iconst.i64 0 + v7027 = iconst.i64 0x3238_c09c_9ee6_0a68 + v7047 = iconst.i64 0 + v7058 = iconst.i64 0 + v7063 = iconst.i64 0 + v7086 = iconst.i64 0 + v7092 = iconst.i64 0 + v7098 = iconst.i64 0 + v7103 = iconst.i64 0 + v7109 = iconst.i64 0 + v7115 = iconst.i64 0 + v7121 = iconst.i64 0 + v7127 = iconst.i64 0 + v7132 = iconst.i64 0 + v7140 = iconst.i64 0x1e8d_25f1_88aa_88ac + v7145 = iconst.i64 0 + v7151 = iconst.i64 0 + v7157 = iconst.i64 0 + v7164 = iconst.i64 0x1ea1_285a_11a6_d491 + v7169 = iconst.i64 0 + v7170 = iconst.i64 -473024102478659629 + v7175 = iconst.i64 0 + v7180 = iconst.i64 0 + v7193 = iconst.i64 0 + v7199 = iconst.i64 0 + v7205 = iconst.i64 0 + v7212 = iconst.i64 0 + v7218 = iconst.i64 0 + v7236 = iconst.i64 0 + v7291 = iconst.i64 0 + v7297 = iconst.i64 0 + v7302 = iconst.i64 0 + v7309 = iconst.i64 0 + v7310 = iconst.i64 -2430136760927541139 + v7315 = iconst.i64 0 + v7321 = iconst.i64 0 + v7327 = iconst.i64 0 + v7335 = iconst.i64 -893728654452329684 + v7340 = iconst.i64 0 + v23246 = iconst.i64 0 + v7353 = iconst.i64 0 + v7359 = iconst.i64 0 + v7364 = iconst.i64 0 + v7365 = iconst.i64 -3509537394138835009 + v7370 = iconst.i64 0 + v7382 = iconst.i64 0 + v7386 = iconst.i32 0 + v7395 = iconst.i64 0 + v23265 = iconst.i64 0 + v7413 = iconst.i64 0 + v7419 = iconst.i64 0 + v7441 = iconst.i32 0 + v7472 = iconst.i64 0 + v7500 = iconst.i64 0 + v7506 = iconst.i64 0 + v7514 = iconst.i64 -2754025843204930835 + v7519 = iconst.i64 0 + v7520 = iconst.i64 -1555935494309644779 + v7525 = iconst.i64 0 + v7531 = iconst.i64 0 + v7537 = iconst.i64 0 + v7543 = iconst.i64 0 + v7544 = iconst.i64 0x2f0d_da7f_159d_427c + v7549 = iconst.i64 0 + v7550 = iconst.i64 0x1708_32eb_95de_4085 + v7555 = iconst.i64 0 + v7561 = iconst.i64 0 + v7567 = iconst.i64 0 + v7581 = iconst.i64 0 + v7582 = iconst.i64 0x0b59_8665_0876_e524 + v7587 = iconst.i64 0 + v7592 = iconst.i64 0 + v7600 = iconst.i32 0 + v7603 = iconst.i64 0 + v7608 = iconst.i64 0 + v7625 = iconst.i64 0 + v7630 = iconst.i64 0 + v7658 = iconst.i64 0 + v7675 = iconst.i64 0 + v7711 = iconst.i64 0 + v7717 = iconst.i64 0 + v7718 = iconst.i64 0x1f7b_ac1a_4636_16b1 + v7723 = iconst.i64 0 + v23311 = iconst.i64 0 + v7736 = iconst.i64 0 + v7742 = iconst.i64 0 + v7748 = iconst.i64 0 + v7749 = iconst.i64 -3679032798703809350 + v7754 = iconst.i64 0 + v7755 = iconst.i64 0x01ed_e95a_1bb4_5269 + v7760 = iconst.i64 0 + v7772 = iconst.i64 0 + v7778 = iconst.i64 0 + v7779 = iconst.i64 0x1ec1_7280_e790_476d + v7784 = iconst.i64 0 + v7801 = iconst.i64 0 + v7812 = iconst.i64 0 + v7828 = iconst.i64 0 + v7857 = iconst.i64 0 + v7883 = iconst.i64 0 + v7889 = iconst.i64 0 + v7903 = iconst.i64 0 + v7907 = iconst.i64 0 + v7922 = iconst.i64 0 + v7933 = iconst.i64 0 + v7939 = iconst.i64 0 + v7944 = iconst.i64 0 + v7945 = iconst.i64 0x3f08_9c2a_59c1_7b84 + v7950 = iconst.i64 0 + v7951 = iconst.i64 -1537561632830964992 + v7956 = iconst.i64 0 + v7962 = iconst.i64 0 + v7968 = iconst.i64 0 + v7974 = iconst.i64 0 + v7975 = iconst.i64 0x2887_89a9_a50d_eb4e + v7980 = iconst.i64 0 + v7981 = iconst.i64 -8272463613716164590 + v7986 = iconst.i64 0 + v7992 = iconst.i64 0 + v7998 = iconst.i64 0 + v8004 = iconst.i64 0 + v8005 = iconst.i64 0x3b4f_6c8a_5563_6d00 + v8010 = iconst.i64 0 + v8011 = iconst.i64 0x4c75_3a92_0bd7_ce22 + v8016 = iconst.i64 0 + v8021 = iconst.i64 0 + v8032 = iconst.i64 0 + v23333 = iconst.i64 0 + v8045 = iconst.i64 0 + v8057 = iconst.i64 0 + v8063 = iconst.i64 0 + v8114 = iconst.i64 0 + v8126 = iconst.i64 0 + v8145 = iconst.i64 0x19f3_c0b1_8ca2_bb4c + v8150 = iconst.i64 0 + v8156 = iconst.i64 0 + v8162 = iconst.i64 0 + v23351 = iconst.i64 0 + v8174 = iconst.i64 0 + v8175 = iconst.i64 0x2c00_dc4f_e650_40ce + v8180 = iconst.i64 0 + v8181 = iconst.i64 0x4b8d_0c27_9423_ff21 + v8186 = iconst.i64 0 + v8192 = iconst.i64 0 + v8198 = iconst.i64 0 + v8206 = iconst.i64 -539487974322705498 + v8211 = iconst.i64 0 + v8212 = iconst.i64 -5528688337749841393 + v8217 = iconst.i64 0 + v8225 = iconst.i64 0 + v8242 = iconst.i64 0 + v8248 = iconst.i64 0 + v8272 = iconst.i64 0 + v23369 = iconst.i64 0 + v23378 = iconst.i64 0 + v8308 = iconst.i64 0 + v8326 = iconst.i64 0 + v8332 = iconst.i64 0 + v8344 = iconst.i64 0 + v8345 = iconst.i64 0x33cc_4bad_fa8d_d684 + v8350 = iconst.i64 0 + v8356 = iconst.i64 0 + v8362 = iconst.i64 0 + v8367 = iconst.i64 0 + v8368 = iconst.i64 0x2feb_2a03_0bd3_4aea + v8373 = iconst.i64 0 + v8374 = iconst.i64 0x3968_db1f_7fa2_c5e0 + v8379 = iconst.i64 0 + v8391 = iconst.i64 0 + v8399 = iconst.i64 0x312e_31c8_00d7_0e5f + v23388 = iconst.i64 0 + v8405 = iconst.i64 -4289268203788529739 + v8410 = iconst.i64 0 + v8420 = iconst.i64 0 + v8426 = iconst.i64 0 + v8432 = iconst.i64 0 + v8438 = iconst.i64 0 + v8444 = iconst.i64 0 + v8450 = iconst.i64 0 + v8460 = iconst.i64 -7141273296289569939 + v8469 = iconst.i64 0 + v8481 = iconst.i64 0 + v8495 = iconst.i64 0 + v24550 = iconst.i32 13 + v8512 = iconst.i64 0 + v8530 = iconst.i64 0 + v8535 = iconst.i64 0 + v8536 = iconst.i64 -1211311475698043490 + v8541 = iconst.i64 0 + v8553 = iconst.i64 0 + v8559 = iconst.i64 0 + v8564 = iconst.i64 0 + v8570 = iconst.i64 0 + v8571 = iconst.i64 0x6d38_e6a4_0e0c_65b7 + v8576 = iconst.i64 0 + v8588 = iconst.i64 0 + v8593 = iconst.i64 0 + v8594 = iconst.i64 0x2373_d593_7e18_c5cb + v8599 = iconst.i64 0 + v8600 = iconst.i64 0x57c8_ae13_525d_8d9d + v8605 = iconst.i64 0 + v8610 = iconst.i64 0 + v8629 = iconst.i64 0 + v8642 = iconst.i64 0 + v8690 = iconst.i64 0 + v8699 = iconst.i64 0 + v8735 = iconst.i64 0 + v8741 = iconst.i64 0 + v8747 = iconst.i64 0 + v8748 = iconst.i64 0x1473_b1ee_af82_1d13 + v8753 = iconst.i64 0 + v8754 = iconst.i64 -1875591182078677468 + v8759 = iconst.i64 0 + v8765 = iconst.i64 0 + v8771 = iconst.i64 0 + v8776 = iconst.i64 0 + v8777 = iconst.i64 -926875954355121659 + v8782 = iconst.i64 0 + v8789 = iconst.i64 0 + v8801 = iconst.i64 0 + v8806 = iconst.i64 0 + v8813 = iconst.i64 0 + v8825 = iconst.i64 0 + v8834 = iconst.i64 0 + v8846 = iconst.i64 0 + v8858 = iconst.i64 0 + v8888 = iconst.i64 0 + v8893 = iconst.i64 0 + v8899 = iconst.i64 0 + v8908 = iconst.i64 0 + v8929 = iconst.i64 0 + v8935 = iconst.i64 0 + v8941 = iconst.i64 0 + v8946 = iconst.i64 0 + v8947 = iconst.i64 0x542d_ba79_522e_c500 + v8952 = iconst.i64 0 + v8958 = iconst.i64 0 + v8964 = iconst.i64 0 + v8970 = iconst.i64 0 + v8976 = iconst.i64 0 + v23412 = iconst.i64 0xc391_ddbd + v23418 = iadd v17, v23412 + v8989 = iconst.i64 0 + v8995 = iconst.i64 0 + v9001 = iconst.i64 0 + v23427 = iconst.i64 0 + v9020 = iconst.i64 0 + v9026 = iconst.i32 0 + v9031 = iconst.i64 0 + v9040 = iconst.i64 0 + v23431 = iconst.i64 0xaac2_3531 + v23437 = iadd v17, v23431 + v9059 = iconst.i64 0 + v9067 = iconst.i64 0 + v23446 = iconst.i64 0 + v9102 = iconst.i64 0 + v9126 = iconst.i64 0 + v9131 = iconst.i64 0 + v23465 = iconst.i64 0 + v9152 = iconst.i64 0 + v9196 = iconst.i64 0 + v9205 = iconst.i64 0 + v9211 = iconst.i64 0 + v9216 = iconst.i64 0 + v9222 = iconst.i64 0 + v9255 = iconst.i64 0 + v9279 = iconst.i64 0 + v9284 = iconst.i64 0 + v9285 = iconst.i64 -9223372036854775792 + v9290 = iconst.i64 0 + v9306 = iconst.i64 0 + v9317 = iconst.i64 0 + v9329 = iconst.i64 0 + v9341 = iconst.i64 0 + v23474 = iconst.i64 0 + v9374 = iconst.i32 0 + v9379 = iconst.i64 0 + v9385 = iconst.i64 0 + v9391 = iconst.i64 0 + v9396 = iconst.i64 0 + v9402 = iconst.i64 0 + v9403 = iconst.i64 0x1019_dfb0_b00d_7680 + v9408 = iconst.i64 0 + v9419 = iconst.i64 0 + v9424 = iconst.i64 0 + v9430 = iconst.i64 0 + v9437 = iconst.i32 0 + v9440 = iconst.i64 0 + v23485 = iconst.i64 0xfe54_cc39 + v23491 = iadd v17, v23485 + v9498 = iconst.i64 0 + v9502 = iconst.i64 0 + v9508 = iconst.i64 0 + v9532 = iconst.i64 0 + v9556 = iconst.i64 0 + v9569 = iconst.i64 0 + v9592 = iconst.i64 0 + v9598 = iconst.i64 0 + v9604 = iconst.i64 0 + v9609 = iconst.i64 0 + v9610 = iconst.i64 0x14fe_27cb_f03b_1af7 + v9615 = iconst.i64 0 + v9616 = iconst.i64 0x36c9_0665_e46e_cd63 + v9621 = iconst.i64 0 + v9659 = iconst.i64 0 + v9677 = iconst.i64 0 + v9719 = iconst.i64 0 + v9728 = iconst.i64 0 + v9734 = iconst.i64 0 + v9753 = iconst.i64 0 + v9787 = iconst.i64 0 + v9801 = iconst.i64 0 + v9819 = iconst.i64 0 + v9825 = iconst.i64 0 + v23547 = iconst.i64 0x914f_c80b + v23553 = iadd v17, v23547 + v9839 = iconst.i64 -1564846967885957061 + v9844 = iconst.i64 0 + v9861 = iconst.i64 0 + v9867 = iconst.i64 0 + v9868 = iconst.i64 0x4438_4023_543c_d5d2 + v9873 = iconst.i64 0 + v9879 = iconst.i64 0 + v9893 = iconst.i64 -821871689667927821 + v9898 = iconst.i64 0 + v9899 = iconst.i64 -6711283848528723427 + v9904 = iconst.i64 0 + v23562 = iconst.i64 0 + v9923 = iconst.i64 0 + v9929 = iconst.i64 0 + v9941 = iconst.i64 0 + v9971 = iconst.i64 0 + v9977 = iconst.i64 0 + v9987 = iconst.i64 0 + v9993 = iconst.i64 0 + v9998 = iconst.i64 0 + v10005 = iconst.i64 0 + v10006 = iconst.i64 0x040b_2123_539b_89ab + v10011 = iconst.i64 0 + v10017 = iconst.i64 0 + v10025 = iconst.i64 0 + v10031 = iconst.i64 0 + v10068 = iconst.i64 0 + v10074 = iconst.i64 0 + v23566 = iconst.i64 0xc3ed_ff43 + v23572 = iadd v17, v23566 + v10088 = iconst.i64 0x4180_8790_cefc_6824 + v10093 = iconst.i64 0 + v10118 = iconst.i64 0 + v10124 = iconst.i64 0 + v10130 = iconst.i64 0 + v10136 = iconst.i64 0 + v10155 = iconst.i64 0 + v10170 = iconst.i64 0 + v10176 = iconst.i64 0 + v10181 = iconst.i64 0 + v10201 = iconst.i64 0 + v10227 = iconst.i64 0 + v23591 = iconst.i64 0 + v10268 = iconst.i64 0 + v10295 = iconst.i64 0 + v23600 = iconst.i64 0 + v10313 = iconst.i64 0 + v10318 = iconst.i64 0 + v10324 = iconst.i64 0 + v10344 = iconst.i64 -1095411716774001580 + v10354 = iconst.i64 0 + v10360 = iconst.i64 0 + v10372 = iconst.i64 0 + v10403 = iconst.i64 0 + v10406 = iconst.i64 0 + v10412 = iconst.i64 0 + v10417 = iconst.i64 0 + v10432 = iconst.i64 0 + v10443 = iconst.i64 0 + v10445 = iconst.i32 0x4b91_1f91 + v23614 = iconst.i64 0xd4d5_bddf + v23620 = iadd v17, v23614 + v10456 = iconst.i64 0 + v10461 = iconst.i64 0 + v10462 = iconst.i64 0x3f96_dcad_5e48_60ad + v10467 = iconst.i64 0 + v10469 = iconst.i64 0x532e_2f77_9fea_e0a1 + v23629 = iconst.i64 0 + v10480 = iconst.i64 0 + v10486 = iconst.i64 0 + v10492 = iconst.i64 0 + v10493 = iconst.i64 0x1003_4db3_92b4_df98 + v10498 = iconst.i64 0 + v10510 = iconst.i64 0 + v10516 = iconst.i64 0 + v10523 = iconst.i64 0 + v10529 = iconst.i64 0 + v23638 = iconst.i64 0 + v10542 = iconst.i64 0 + v10547 = iconst.i64 0 + v10562 = iconst.i64 0x415e_163c_ccac_923b + v23656 = iconst.i64 0 + v10573 = iconst.i64 0 + v10594 = iconst.i64 0 + v10600 = iconst.i64 0 + v10610 = iconst.i64 0 + v10627 = iconst.i64 0 + v10633 = iconst.i64 0 + v10641 = iconst.i64 -4067228757008511281 + v10646 = iconst.i64 0 + v10647 = iconst.i64 0x0b22_18f2_f690_334f + v10652 = iconst.i64 0 + v10658 = iconst.i64 0 + v10663 = iconst.i64 0 + v10698 = iconst.i64 0 + v10712 = iconst.i64 0 + v10718 = iconst.i64 0 + v10727 = iconst.i64 0 + v10736 = iconst.i64 0 + v10747 = iconst.i64 0 + v10751 = iconst.i64 0x297f_fd5b_fff0_5da2 + v10760 = iconst.i64 0 + v10766 = iconst.i64 0 + v10772 = iconst.i64 0 + store little heap v24475, v10772 + v10778 = iconst.i64 0 + v23674 = iconst.i64 0 + v10791 = iconst.i64 0 + v10797 = iconst.i64 0 + v10803 = iconst.i64 0 + v10814 = iconst.i64 0 + v10849 = iconst.i64 0 + v10855 = iconst.i64 0 + v10867 = iconst.i64 0 + v10883 = iconst.i64 0 + v10888 = iconst.i64 0 + v10894 = iconst.i64 0 + v23683 = iconst.i64 0 + v10930 = iconst.i64 0 + v10943 = iconst.i64 0 + v10946 = iconst.i64 0 + store little heap v10943, v10946 + v10952 = iconst.i64 0 + v10973 = iconst.i64 0 + v10987 = iconst.i64 0 + v10993 = iconst.i64 0 + v11011 = iconst.i64 0 + v11017 = iconst.i64 0 + v11023 = iconst.i64 0 + v11033 = iconst.i64 0 + v11038 = iconst.i64 -220006835105832468 + v11043 = iconst.i64 0 + v11057 = iconst.i64 0 + v11069 = iconst.i64 0 + v11075 = iconst.i64 0 + v11081 = iconst.i64 0 + v23692 = iconst.i64 0 + v11101 = iconst.i64 0 + v11107 = iconst.i64 0 + v11113 = iconst.i64 0 + v11119 = iconst.i64 0 + v11120 = iconst.i64 -282575123483181235 + v11125 = iconst.i64 0 + v11127 = iconst.i64 0x212b_5b75_4c19_563b + v11132 = iconst.i64 0 + v11138 = iconst.i64 0 + v11144 = iconst.i64 0 + v11158 = iconst.i64 0x004c_e186_396f_0c85 + v11163 = iconst.i64 0 + v11181 = iconst.i64 0 + v11199 = iconst.i64 0 + v11215 = iconst.i64 -8117707843369584135 + v11229 = iconst.i64 0 + v11240 = iconst.i64 0 + v11255 = iconst.i64 0 + v11270 = iconst.i64 0 + v11295 = iconst.i64 0 + v11298 = iconst.i64 -5437530724550071890 + v11331 = iconst.i64 0 + v11341 = iconst.i64 -1206047550242513956 + v11346 = iconst.i64 0 + v11363 = iconst.i64 0 + v11395 = iconst.i64 0 + v11398 = iconst.i64 0x2955_2fb8_c260_7cbe + v11401 = iconst.i64 0 + v11444 = iconst.i64 0 + v11460 = iconst.i64 0 + v11480 = iconst.i64 0 + v11510 = iconst.i64 0 + v11511 = iconst.i64 0x4e54_f936_f21a_c2a2 + v11516 = iconst.i64 0 + v11517 = iconst.i64 -1960451704053797162 + v11522 = iconst.i64 0 + v11540 = iconst.i64 0 + v11546 = iconst.i64 0 + v11547 = iconst.i64 -1812558034864271102 + v11552 = iconst.i64 0 + v11558 = iconst.i64 0 + v11564 = iconst.i64 0 + v11569 = iconst.i64 0 + v11570 = iconst.i64 0x7716_abf3_aeae_8e9d + v11575 = iconst.i64 0 + v11588 = iconst.i64 0 + v11596 = iconst.i64 0x67dc_71a8_ad62_2910 + v23741 = iconst.i64 0 + v11613 = iconst.i64 0 + v11620 = iconst.i64 0 + v11626 = iconst.i64 0 + v11657 = iconst.i64 0 + v11678 = iconst.i64 0 + v11697 = iconst.i64 0 + v11703 = iconst.i64 0 + v11711 = iconst.i64 0 + v11718 = iconst.i64 0 + v11737 = iconst.i64 0 + v11748 = iconst.i64 0 + v11760 = iconst.i64 0 + v11766 = iconst.i64 0 + v11779 = iconst.i64 0x6958_89d9_e8cb_b995 + v11784 = iconst.i64 0 + v11791 = iconst.i64 0 + v11811 = iconst.i64 -3365381723352213844 + v23760 = iconst.i64 0 + v23762 = iconst.i64 0xfdd8_770f + v11840 = iadd v17, v23762 + v11856 = iconst.i64 0 + v11875 = iconst.i64 0 + v11895 = iconst.i64 0 + v11919 = iconst.i64 0 + v11925 = iconst.i64 -318814853156093995 + v11930 = iconst.i64 0 + v11954 = iconst.i64 0 + v11960 = iconst.i64 0 + v11961 = iconst.i64 0x6cc1_6bc1_f3b7_a28e + v11966 = iconst.i64 0 + v11972 = iconst.i64 0 + v11978 = iconst.i64 0 + v11984 = iconst.i64 0 + v23770 = iconst.i64 0 + v11992 = iconst.i64 0x0484_8c49_57cc_c7b8 + v11997 = iconst.i64 0 + v12003 = iconst.i64 0 + v12009 = iconst.i64 0 + v12021 = iconst.i64 0 + v12027 = iconst.i64 0 + v12043 = iconst.i64 0x00e3_d080_2d8a_47e9 + v12052 = iconst.i64 0 + v12080 = iconst.i64 0 + v12086 = iconst.i64 0 + v12089 = iconst.i64 0 + v12113 = iconst.i64 0 + v12119 = iconst.i64 0 + v12125 = iconst.i64 0 + v12126 = iconst.i64 -8597832965098348823 + v12131 = iconst.i64 0 + v12137 = iconst.i64 0 + v12143 = iconst.i64 0 + v12149 = iconst.i64 0 + v12156 = iconst.i64 0x1666_e1d5_7298_e0e0 + v12161 = iconst.i64 0 + v12162 = iconst.i64 0x2bab_3ec8_812a_5155 + v12167 = iconst.i64 0 + v12177 = iconst.i64 0 + v12182 = iconst.i64 0 + v12187 = iconst.i64 0 + v12188 = iconst.i64 0x31e3_0164_030b_0ff3 + v12193 = iconst.i64 0 + v12200 = iconst.i64 0 + v23787 = iconst.i64 0xfd70_2331 + v23793 = iadd v17, v23787 + v12218 = iconst.i64 0 + v12224 = iconst.i64 0 + v12230 = iconst.i64 0 + v12236 = iconst.i64 0 + v12242 = iconst.i64 0 + v12270 = iconst.i64 0 + v12275 = iconst.i64 0 + v12330 = iconst.i64 0 + v12336 = iconst.i64 0 + v12342 = iconst.i64 0 + v12343 = iconst.i64 -1513648042477806047 + v12348 = iconst.i64 0 + v12360 = iconst.i64 0 + v12366 = iconst.i64 0 + v12380 = iconst.i64 -3876577509444182385 + v12385 = iconst.i64 0 + v23797 = iconst.i64 0xbc95_6c8d + v23803 = iadd v17, v23797 + v12398 = iconst.i64 0 + v12403 = iconst.i64 0 + v12410 = iconst.i64 0 + v12411 = iconst.i64 0x5751_7ee9_9cc5_9ca7 + v12416 = iconst.i64 0 + v12421 = iconst.i64 0 + v12436 = iconst.i64 0 + v23812 = iconst.i64 0 + v12449 = iconst.i64 0 + v12455 = iconst.i64 0 + v12461 = iconst.i64 0 + v12475 = iconst.i64 0x69ef_2dab_5692_a148 + v12495 = iconst.i64 0 + v12505 = iconst.i64 0 + v12516 = iconst.i64 0 + v12540 = iconst.i64 0 + v23816 = iconst.i64 0xd8dd_7147 + v23822 = iadd v17, v23816 + v12566 = iconst.i64 0 + v12572 = iconst.i64 0 + v12579 = iconst.i64 0x39af_11ea_10fe_492f + v12584 = iconst.i64 0 + v12585 = iconst.i64 -2343243191701620364 + v12590 = iconst.i64 0 + v23831 = iconst.i64 0 + v12608 = iconst.i64 0 + v12609 = iconst.i64 0x554c_0d05_aeff_dc58 + v12614 = iconst.i64 0 + v12620 = iconst.i64 0 + v12640 = iconst.i64 0 + v12646 = iconst.i64 0 + v12652 = iconst.i64 0 + v12671 = iconst.i64 0 + v12684 = iconst.i64 0 + v12695 = iconst.i64 0 + v12704 = iconst.i64 0 + v12715 = iconst.i64 0 + v12726 = iconst.i64 0 + v12732 = iconst.i64 0 + v12738 = iconst.i64 0 + v12744 = iconst.i64 0 + v12745 = iconst.i64 -8203329360505575356 + v12750 = iconst.i64 0 + v23840 = iconst.i64 0 + v12762 = iconst.i64 0 + v12768 = iconst.i64 0 + v12773 = iconst.i64 0 + v12774 = iconst.i64 -457279479832121108 + v12779 = iconst.i64 0 + v12780 = iconst.i64 -4589259683427666612 + v12785 = iconst.i64 0 + v12791 = iconst.i64 0 + v12797 = iconst.i64 0 + v12802 = iconst.i64 0 + v12803 = iconst.i64 -4064521300991009009 + v12808 = iconst.i64 0 + v12809 = iconst.i64 0x11f3_4c8a_da01_a597 + v12814 = iconst.i64 0 + v12819 = iconst.i64 0 + v12828 = iconst.i64 0 + v12834 = iconst.i64 0 + v12840 = iconst.i64 0 + v12846 = iconst.i64 0 + v12852 = iconst.i64 0 + v12879 = iconst.i64 0 + v23849 = iconst.i64 0 + v12896 = iconst.i64 0 + v12918 = iconst.i64 0 + v12931 = iconst.i64 0 + v12936 = iconst.i64 0 + v12937 = iconst.i64 0x350a_710c_8a0f_fcc3 + v12942 = iconst.i64 0 + v23853 = iconst.i64 0xd567_3c81 + v23859 = iadd v17, v23853 + v12955 = iconst.i64 0 + v12961 = iconst.i64 0 + v12966 = iconst.i64 0 + v12967 = iconst.i64 0x0bd8_8069_99a7_8420 + v12972 = iconst.i64 0 + v12984 = iconst.i64 0 + v12990 = iconst.i64 0 + v12995 = iconst.i64 0 + v13003 = iconst.i64 -2348637401025631785 + v13008 = iconst.i64 0 + v13013 = iconst.i64 0 + v13022 = iconst.i64 0 + v13034 = iconst.i64 0 + v13046 = iconst.i64 0 + v13047 = iconst.i64 -3164553342834853840 + v13052 = iconst.i64 0 + v13075 = iconst.i64 0 + v13081 = iconst.i64 0 + v13095 = iconst.i64 0 + v13106 = iconst.i64 0 + v13112 = iconst.i64 0 + v13130 = iconst.i64 0 + v13135 = iconst.i64 0 + v13143 = iconst.i64 -3944328223404150048 + v13148 = iconst.i64 0 + v13167 = iconst.i64 0 + v13168 = iconst.i64 0x1eb6_87e5_9371_f111 + v13173 = iconst.i64 0 + v13179 = iconst.i64 0 + v13185 = iconst.i64 0 + v13191 = iconst.i64 0 + v13196 = iconst.i64 0 + v13197 = iconst.i64 0x274e_fb4c_5ecd_b036 + v13202 = iconst.i64 0 + v23872 = iconst.i64 0x8690_2a5f + v23878 = iadd v17, v23872 + v13214 = iconst.i64 0 + v13231 = iconst.i64 0 + v13238 = iconst.i64 0 + v13250 = iconst.i64 0 + v13262 = iconst.i64 0 + v13268 = iconst.i64 0 + v13274 = iconst.i64 0 + v13285 = iconst.i64 0 + v13305 = iconst.i64 0 + v13330 = iconst.i64 0 + v13336 = iconst.i64 0 + v13342 = iconst.i64 0 + v13348 = iconst.i64 0 + v13354 = iconst.i64 0 + v13355 = iconst.i64 0x4506_a098_22bc_e217 + v13360 = iconst.i64 0 + v13390 = iconst.i64 0 + v13396 = iconst.i64 0 + v13402 = iconst.i64 0 + v13408 = iconst.i64 0 + v13409 = iconst.i64 -6455205359296310519 + v13414 = iconst.i64 0 + v13435 = iconst.i64 0 + v13441 = iconst.i64 0 + v13447 = iconst.i64 0 + v13469 = iconst.i64 0 + v13488 = iconst.i64 0 + v13502 = iconst.i64 0 + v13529 = iconst.i64 0 + v23920 = iconst.i64 0 + v13540 = iconst.i64 0 + v13570 = iconst.i64 0 + v13576 = iconst.i64 0 + v13581 = iconst.i64 0 + v13582 = iconst.i64 0x5635_0a79_457b_2ef8 + v13587 = iconst.i64 0 + v23940 = iconst.i64 0 + v13599 = iconst.i64 0 + v13609 = iconst.i64 0 + v13615 = iconst.i64 0 + v13622 = iconst.i64 0 + v13623 = iconst.i64 -2298414935272572928 + v13628 = iconst.i64 0 + v13652 = iconst.i64 0 + v13661 = iconst.i64 0 + v13667 = iconst.i64 0 + v13673 = iconst.i64 0 + v13679 = iconst.i64 0 + v23969 = iconst.i64 0 + v13785 = iconst.i64 0 + v13795 = iconst.i64 0 + v13822 = iconst.i64 0 + v13864 = iconst.i64 0 + store little heap v24488, v13864 + v23993 = iconst.i64 0xdad1_1f59 + v23999 = iadd v17, v23993 + v24003 = iconst.i64 0x8560_7b1f + v24009 = iadd v17, v24003 + v13919 = iconst.i64 0 + store little heap v24490, v13919 + v13938 = iconst.i64 0 + v13944 = iconst.i64 0 + v13961 = iconst.i64 0 + v13966 = iconst.i64 0 + v13982 = iconst.i64 0 + v13993 = iconst.i64 0 + v14022 = iconst.i64 0 + v14027 = iconst.i64 0 + store little heap v24502, v14027 + v14033 = iconst.i64 0 + store little heap v24502, v14033 + v14039 = iconst.i64 0 + v14043 = iconst.i64 -1984357341440851753 + v14076 = iconst.i64 0 + v14093 = iconst.i64 0 + v14104 = iconst.i64 0 + v14109 = iconst.i64 -4582928081857057481 + v14112 = iconst.i64 0 + v14119 = iconst.i64 0 + v14125 = iconst.i64 0 + v14137 = iconst.i64 0 + v14149 = iconst.i64 0 + v14155 = iconst.i64 0 + v14161 = iconst.i64 0 + v14178 = iconst.i64 0 + v14183 = iconst.i64 0 + v14191 = iconst.i64 0 + v14207 = iconst.i64 0 + v14213 = iconst.i64 0 + v14227 = iconst.i64 0 + v14232 = iconst.i64 0 + v24021 = iconst.i64 0 + v14254 = iconst.i64 0 + v14266 = iconst.i64 0 + v14276 = iconst.i64 0 + store little heap v24502, v14276 + v14292 = iconst.i64 0 + store little heap v24502, v14292 + v14298 = iconst.i64 0 + store little heap v24488, v14298 + v14308 = iconst.i64 0 + v14319 = iconst.i64 0 + v14330 = iconst.i64 0 + store little heap v8, v14330 + v14353 = iconst.i64 0 + v14359 = iconst.i64 0 + v14365 = iconst.i64 0 + v14371 = iconst.i64 0 + v14377 = iconst.i64 0 + v14383 = iconst.i64 0 + v14389 = iconst.i64 0 + v14395 = iconst.i64 0 + v14405 = iconst.i64 0 + v14411 = iconst.i64 0 + v14429 = iconst.i64 0 + v24045 = iconst.i64 0 + v14442 = iconst.i64 0 + v14449 = iconst.i64 0x7a2d_6e45_abe0_f6c6 + v14454 = iconst.i64 0 + v14467 = iconst.i64 0 + v14473 = iconst.i64 0 + v14485 = iconst.i64 0 + v14486 = iconst.i64 0x1c16_423c_61ae_e132 + v14491 = iconst.i64 0 + v14501 = iconst.i64 0 + v14507 = iconst.i64 0 + v14531 = iconst.i64 -589663243741313041 + v14536 = iconst.i64 0 + v14541 = iconst.i64 0 + v14551 = iconst.i64 0 + v14557 = iconst.i64 0 + v14569 = iconst.i64 0 + v14587 = iconst.i64 0 + v14611 = iconst.i64 0 + v14616 = iconst.i64 0 + v14630 = iconst.i64 0 + v14636 = iconst.i64 0 + v14642 = iconst.i64 0 + v24063 = iconst.i64 0 + v14656 = iconst.i64 0x6b8e_d4af_1142_fcd7 + v14661 = iconst.i64 0 + v14674 = iconst.i64 0 + v14680 = iconst.i64 0 + v14699 = iconst.i64 0 + v14704 = iconst.i64 0 + v14705 = iconst.i64 0x2358_72f3_076b_6f28 + v14710 = iconst.i64 0 + v14733 = iconst.i64 0 + v14734 = iconst.i64 -388390384262418337 + v14739 = iconst.i64 0 + v14745 = iconst.i64 0 + v14761 = iconst.i64 0 + v14767 = iconst.i64 0 + v14779 = iconst.i64 0 + v14791 = iconst.i64 0 + v14797 = iconst.i64 0 + v14814 = iconst.i64 0 + v14828 = iconst.i64 0 + v14840 = iconst.i64 0 + v14847 = iconst.i64 0 + v14853 = iconst.i64 0 + v14859 = iconst.i64 0 + v14860 = iconst.i64 0x2269_6f9a_eeca_2b7d + v14865 = iconst.i64 0 + v14866 = iconst.i64 -1540462688560787561 + v14871 = iconst.i64 0 + v14883 = iconst.i64 0 + v24073 = iconst.i64 0 + v14897 = iconst.i64 0x52dd_31d6_4578_dd0b + v14902 = iconst.i64 0 + v14908 = iconst.i64 0 + v14914 = iconst.i64 0 + v14926 = iconst.i64 0 + v14938 = iconst.i64 0 + v14944 = iconst.i64 0 + v14950 = iconst.i64 0 + v14956 = iconst.i64 0 + v15005 = iconst.i64 0 + v24076 = iconst.i64 0x94ab_f849 + v15024 = iadd v17, v24076 + v15030 = iconst.i64 0 + v15036 = iconst.i64 0 + v15042 = iconst.i64 0 + v15056 = iconst.i64 0x0ebc_0be0_7496_e446 + v15061 = iconst.i64 0 + v15073 = iconst.i64 0 + v15080 = iconst.i64 0x498f_8596_35b4_e68c + v15085 = iconst.i64 0 + v15086 = iconst.i64 -4519528276824892627 + v15091 = iconst.i64 0 + v15105 = iconst.i64 0 + v15116 = iconst.i64 0 + v15127 = iconst.i64 0 + v24093 = iconst.i64 0 + v15168 = iconst.i64 -444811622952961017 + v15183 = iconst.i64 0 + v15189 = iconst.i64 0 + v15195 = iconst.i64 0 + v15202 = iconst.i64 0 + v15215 = iconst.i64 0 + v15241 = iconst.i64 0 + v15255 = iconst.i64 0 + v15284 = iconst.i64 0 + v15285 = iconst.i64 -4094131869136971711 + v15290 = iconst.i64 0 + v15291 = iconst.i64 0x1986_52c9_ffb5_3bde + v15296 = iconst.i64 0 + v15302 = iconst.i64 0 + v15308 = iconst.i64 0 + v15313 = iconst.i64 0 + v15319 = iconst.i64 0 + v15320 = iconst.i64 0x241c_e60d_a504_5196 + v15325 = iconst.i64 0 + v15332 = iconst.i64 0 + v15338 = iconst.i64 0 + v15343 = iconst.i64 0 + v15344 = iconst.i64 0x65ea_64e5_8e84_5308 + v15349 = iconst.i64 0 + v24111 = iconst.i64 0 + v15362 = iconst.i64 0 + v15374 = iconst.i64 0 + v15386 = iconst.i64 0 + v15392 = iconst.i64 0 + v15450 = iconst.i64 0 + v15463 = iconst.i64 0 + v24127 = iconst.i64 0 + v15476 = iconst.i64 0 + v15482 = iconst.i64 0 + v24131 = iconst.i64 0x9705_9965 + v24137 = iadd v17, v24131 + v15490 = iconst.i64 0x2ba1_0c19_7cd8_09b5 + v15495 = iconst.i64 0 + v15501 = iconst.i64 0 + v15507 = iconst.i64 0 + v15513 = iconst.i64 0 + v15514 = iconst.i64 0x173e_d9e3_7271_fb74 + v15519 = iconst.i64 0 + v15520 = iconst.i64 0x0b9e_1efb_5017_0ec6 + v15524 = iconst.i64 0x38e0 + v15525 = iadd v1786, v15524 + v15535 = iconst.i64 0 + v15541 = iconst.i64 0 + v15550 = iconst.i64 0 + v15556 = iconst.i64 0 + v15570 = iconst.i64 0 + v15571 = iconst.i64 -9223372036854775797 + v15575 = iconst.i64 0x395c + v15576 = iadd v1786, v15575 + v15594 = iconst.i64 0 + v15600 = iconst.i64 0 + v15612 = iconst.i64 0 + v15630 = iconst.i64 0 + v15637 = iconst.i64 0 + v15647 = iconst.i64 0 + v15667 = iconst.i64 0 + v15685 = iconst.i64 0 + v15691 = iconst.i64 0 + v15704 = iconst.i64 -2646031602873363504 + v15708 = iconst.i64 0x3a10 + v15709 = iadd v1786, v15708 + v15721 = iconst.i64 0 + v15739 = iconst.i64 0 + v15745 = iconst.i64 0 + v15751 = iconst.i64 0 + v15758 = iconst.i64 -4481925662202438200 + v15762 = iconst.i64 0x39c8 + v15763 = iadd v1786, v15762 + v15764 = iconst.i64 -4877997782289574454 + v15768 = iconst.i64 0x39c0 + v15769 = iadd v1786, v15768 + v15775 = iconst.i64 0 + v15780 = iconst.i64 0 + v15786 = iconst.i64 0 + v15792 = iconst.i64 0 + v15798 = iconst.i64 0 + v15799 = iconst.i64 -2631626801409752768 + v15803 = iconst.i64 0x3a64 + v15804 = iadd v1786, v15803 + v15824 = iconst.i64 0 + v15833 = iconst.i64 0 + v15838 = iconst.i64 0 + v15849 = iconst.i64 0 + v15867 = iconst.i64 0 + v15878 = iconst.i64 0 + v24564 = iconst.i32 3016 + v24642 = iconst.i32 1 + v24643 = iconst.i32 2 + store little heap v24211, v17 + store little heap v24211, v58 + store little heap v24211, v65 + store little heap v24219, v178 + store little heap v102, v108 + store little heap v102, v201 + v370 = iconst.i64 0 + v1523 = iconst.i32 0 + v2807 = iconst.i64 0 + v2948 = iconst.i64 0 + store little heap v24490, v3588 + store little heap v24488, v22782 + store little heap v24488, v3645 + store little heap v24475, v3651 + store little heap v24490, v3657 + store little heap v24477, v3668 + store little heap v8, v1786 + v4087 = iconst.i64 0 + store little heap v24488, v5224 + store little heap v8, v5229 + store little heap v24502, v5242 + v10864 = iconst.i64 0 + v10934 = iconst.i32 0 + v11734 = iconst.i64 0 + v12291 = iconst.i32 0 + v12690 = iconst.i64 0 + v13466 = iconst.i64 0 + v13483 = iconst.i64 0 + v14314 = iconst.i64 0 + v14665 = iconst.i64 0 + v14756 = iconst.i64 0 + v15000 = iconst.i64 0 + v15250 = iconst.i32 0 + store little heap v24488, v15302 + store little heap v24236, v24250 + store little heap v370, v375 + store little heap v24263, v24264 + v4239 = iconst.i64 0 + store little heap v24488, v4249 + store little heap v24488, v5194 + store little heap v24488, v5248 + v5401 = iconst.i64 0 + store little heap v5401, v5404 + v5600 = iconst.i64 0 + store little heap v24488, v17 + store little heap v24502, v6194 + v6220 = iconst.i64 0 + v6359 = iconst.i32 0 + store little heap v6359, v1786 + store little heap v24488, v6368 + store little heap v24488, v17 + store little heap v24502, v23117 + store little heap v8, v6461 + store little heap v24477, v6580 + store little heap v24475, v6603 + store little heap v24502, v23194 + v8089 = iconst.i64 0 + v8111 = iconst.i64 0 + store little heap v24488, v8192 + store little heap v24488, v8198 + store little heap v24490, v8272 + store little heap v24488, v8530 + store little heap v8, v8535 + v9110 = iconst.i32 0 + v9147 = iconst.i32 0 + store little heap v24488, v17 + store little heap v9719, v2901 + store little heap v10403, v10406 + store little heap v10864, v10867 + v11265 = iconst.i32 0 + store little heap v24488, v17 + store little heap v24502, v23940 + store little heap v14699, v14704 + store little heap v24488, v17 + store little heap v15250, v15255 + store little heap v8, v15284 + v15411 = iconst.i64 0 + v15662 = iconst.i32 0 + store little heap v15662, v15667 + v15873 = iconst.i32 0 + store little heap v15873, v15878 + v2619 = iconst.i64 0 + v3271 = iconst.i32 0 + store little heap v24543, v5985 + store little heap v24502, v5997 + v6432 = iconst.i64 0 + store little heap v8, v6676 + store little heap v7600, v7603 + v7653 = iconst.i64 0 + store little heap v8, v7828 + store little heap v24543, v8248 + store little heap v24550, v8512 + v9097 = iconst.i64 0 + store little heap v9147, v9152 + v9560 = iconst.i64 0 + store little heap v9560, v1786 + store little heap v8, v9609 + store little heap v24502, v9728 + store little heap v24488, v9734 + store little heap v24488, v17 + store little heap v24488, v10658 + store little heap v24488, v10663 + v11890 = iconst.i32 0 + v13061 = iconst.i64 0 + v14348 = iconst.i64 0 + store little heap v14665, v1786 + store little heap v24502, v14674 + store little heap v24502, v14926 + store little heap v24475, v3231 + v3282 = iconst.i64 0 + store little heap v24490, v3494 + v3728 = iconst.i64 0 + v3752 = iconst.i32 0 + v4501 = iconst.i64 0 + store little heap v24488, v1786 + store little heap v24488, v4815 + v4972 = iconst.i64 0 + store little heap v4972, v4977 + v5118 = iconst.i32 0 + store little heap v24502, v5359 + v5416 = iconst.i64 0 + v5425 = iconst.i64 0 + v5580 = iconst.i64 0 + v5630 = iconst.i64 0 + v5639 = iconst.i64 0 + v5842 = iconst.i64 0 + store little heap v24490, v17 + store little heap v8, v23040 + v6441 = iconst.i64 0 + v6656 = iconst.i64 0 + v6862 = iconst.i64 0 + store little heap v24488, v7382 + store little heap v7386, v1786 + store little heap v24477, v23265 + v7807 = iconst.i32 0 + store little heap v7807, v7812 + v7820 = iconst.i32 0 + store little heap v24477, v7974 + store little heap v24488, v7992 + store little heap v24502, v8156 + store little heap v24502, v8469 + store little heap v24502, v8789 + v9202 = iconst.i64 0 + v9250 = iconst.i64 0 + store little heap v24502, v9498 + v9650 = iconst.i64 0 + store little heap v24488, v9677 + store little heap v8, v1786 + store little heap v24502, v9753 + v9782 = iconst.i64 0 + store little heap v24490, v1786 + v10104 = iconst.i64 0 + v10113 = iconst.i64 0 + store little heap v24475, v10130 + store little heap v24488, v2901 + store little heap v24488, v17 + v10589 = iconst.i64 0 + store little heap v10589, v10594 + store little heap v24502, v10600 + store little heap v24488, v17 + store little heap v24502, v10610 + v10693 = iconst.i64 0 + v10703 = iconst.i64 0 + store little heap v10703, v3731 + store little heap v24499, v10791 + v10982 = iconst.i64 0 + v11224 = iconst.i64 0 + v11235 = iconst.i64 0 + v11292 = iconst.i32 0 + v11316 = iconst.i64 0 + v11475 = iconst.i32 0 + v11722 = iconst.i64 0 + store little heap v24477, v12125 + store little heap v24488, v12143 + v12261 = iconst.i64 0 + store little heap v24475, v12671 + v13070 = iconst.i64 0 + v13309 = iconst.i64 0 + v13819 = iconst.i64 0 + v13977 = iconst.i32 0 + store little heap v24499, v14149 + v14238 = iconst.i64 0 + v14258 = iconst.i32 0 + store little heap v24477, v1786 + store little heap v24490, v14405 + store little heap v24477, v24045 + v14597 = iconst.i64 0 + v14606 = iconst.i64 0 + v15111 = iconst.i64 0 + v15547 = iconst.i64 0 + v15844 = iconst.i64 0 + store little heap v15844, v15849 + v3422 = iconst.i64 0 + v3431 = iconst.i64 0 + store little heap v24502, v4542 + v4733 = iconst.i64 0 + v4753 = iconst.i64 0 + v4906 = iconst.i32 0 + store little heap v4906, v1786 + store little heap v24488, v4915 + store little heap v24502, v5168 + store little heap v24502, v5174 + store little heap v24502, v5563 + v5781 = iconst.i64 0 + v5833 = iconst.i64 0 + store little heap v24488, v17 + store little heap v24488, v17 + store little heap v7907, v1786 + store little heap v24477, v8126 + store little heap v24490, v17 + store little heap v24490, v24351 + store little heap v24502, v8690 + v8879 = iconst.i64 0 + store little heap v8879, v1786 + store little heap v8888, v8893 + v9158 = iconst.i32 0 + store little heap v9158, v2901 + store little heap v24488, v9819 + store little heap v24477, v10542 + v10580 = iconst.i64 0 + store little heap v11316, v1786 + store little heap v24488, v11331 + store little heap v24475, v11626 + store little heap v24488, v11760 + store little heap v24490, v12052 + v12869 = iconst.i64 0 + v13090 = iconst.i64 0 + v13222 = iconst.i32 0 + store little heap v24488, v17 + v14173 = iconst.i64 0 + v15445 = iconst.i64 0 + v15454 = iconst.i64 0 + store little heap v24477, v15775 + v15853 = iconst.i64 0 + v3737 = iconst.i64 0 + store little heap v24477, v3893 + v4426 = iconst.i64 0 + v4443 = iconst.i64 0 + v5179 = iconst.i32 0 + v5567 = iconst.i32 0 + store little heap v24502, v5822 + store little heap v24477, v5951 + store little heap v24502, v5963 + store little heap v24488, v6351 + store little heap v24502, v6374 + store little heap v24490, v6387 + store little heap v24488, v1786 + store little heap v24502, v6636 + v7233 = iconst.i64 0 + v7898 = iconst.i64 0 + v8476 = iconst.i64 0 + v8490 = iconst.i64 0 + store little heap v24490, v8899 + store little heap v24502, v9923 + v13280 = iconst.i64 0 + v13497 = iconst.i64 0 + v13933 = iconst.i64 0 + v14084 = iconst.i64 0 + store little heap v24499, v14791 + store little heap v15111, v15116 + store little heap v24502, v17 + store little heap v24488, v15127 + store little heap v15445, v15450 + store little heap v24488, v24127 + v4578 = iconst.i32 0 + store little heap v24490, v6840 + store little heap v24490, v6905 + v7878 = iconst.i64 0 + v8696 = iconst.i32 0 + v10167 = iconst.i64 0 + store little heap v24488, v10227 + store little heap v24490, v10324 + store little heap v24511, v1786 + v11743 = iconst.i64 0 + store little heap v24490, v12137 + istore8 little heap v24477, v12516 + store little heap v24488, v12540 + store little heap v24475, v13262 + store little heap v24502, v13268 + store little heap v24477, v13529 + store little heap v24488, v2666 + v3394 = iconst.i64 0 + store little heap v24477, v4852 + store little heap v24488, v7180 + v8870 = iconst.i32 0 + store little heap v24499, v9929 + store little heap v24502, v23591 + store little heap v24488, v10268 + v10338 = iconst.i32 0 + store little heap v24477, v17 + v4949 = iconst.i32 0 + store little heap v24488, v7026 + store little heap v24490, v1786 + v14013 = iconst.i64 0 + store little heap v24475, v14365 + store little heap v24477, v14840 + store little heap v24490, v14956 + store little heap v15000, v15005 + store little heap v24488, v3986 + v4957 = iconst.i64 0 + store little heap v24488, v7592 + store little heap v24512, v7922 + store little heap v24488, v8021 + v13300 = iconst.i64 0 + store little heap v24499, v14383 + v14421 = iconst.i32 0 + store little heap v14421, v1786 + v14622 = iconst.i32 0 + store little heap v14622, v1786 + v5154 = iconst.i32 0 + store little heap v24477, v7543 + store little heap v24502, v8958 + store little heap v24488, v8964 + store little heap v24488, v8970 + store little heap v10412, v10417 + store little heap v24488, v11138 + store little heap v24488, v11395 + v13508 = iconst.i32 0 + v4374 = iconst.i64 0 + store little heap v4578, v4583 + store little heap v5154, v5157 + store little heap v24488, v17 + store little heap v24502, v2172 + store little heap v24477, v6997 + store little heap v24488, v7002 + store little heap v24502, v7047 + store little heap v24502, v9556 + store little heap v24502, v9569 + store little heap v24488, v9592 + store little heap v24490, v10712 + store little heap v24490, v24351 + v13647 = iconst.i32 0 + store little heap v24477, v15332 + store little heap v24488, v15338 + store little heap v24490, v24111 + store little heap v24488, v15362 + store little heap v24488, v2178 + store little heap v24488, v17 + store little heap v24488, v2233 + store little heap v24488, v2239 + store little heap v24488, v2245 + store little heap v24502, v2282 + store little heap v24488, v2288 + store little heap v24488, v2293 + store little heap v24502, v2325 + store little heap v24488, v2331 + store little heap v24488, v17 + store little heap v24502, v2344 + store little heap v24488, v1786 + store little heap v24488, v2402 + store little heap v24488, v2419 + store little heap v24488, v2455 + store little heap v24488, v2472 + store little heap v24488, v2478 + store little heap v24488, v2484 + store little heap v24502, v2496 + store little heap v24488, v2502 + store little heap v24488, v2508 + store little heap v24488, v2514 + store little heap v24502, v2526 + store little heap v24488, v2532 + store little heap v24488, v2538 + store little heap v8, v2561 + store little heap v24488, v2591 + store little heap v2619, v2624 + store little heap v24488, v17 + store little heap v24502, v1786 + store little heap v24512, v2654 + store little heap v24488, v2672 + store little heap v24488, v2678 + store little heap v24488, v2696 + store little heap v24488, v2702 + store little heap v24488, v2708 + store little heap v24488, v2728 + store little heap v24488, v2735 + store little heap v8, v2740 + store little heap v24502, v2752 + store little heap v24488, v2757 + store little heap v24488, v17 + store little heap v24488, v2766 + store little heap v24502, v2801 + store little heap v2807, v2812 + store little heap v24488, v17 + store little heap v24502, v2822 + store little heap v24488, v2838 + store little heap v24488, v2844 + store little heap v24488, v2850 + store little heap v24488, v2868 + store little heap v24488, v2874 + store little heap v24502, v2892 + store little heap v2898, v2901 + store little heap v2907, v2912 + store little heap v24488, v2917 + store little heap v24488, v17 + store little heap v24502, v2925 + store little heap v8, v2937 + store little heap v24488, v1786 + store little heap v2948, v2951 + store little heap v24488, v1786 + store little heap v24502, v2989 + store little heap v24502, v2996 + store little heap v24502, v3008 + store little heap v24502, v3043 + store little heap v24488, v3117 + store little heap v8, v3122 + store little heap v24502, v3146 + store little heap v24488, v3152 + store little heap v24502, v3167 + store little heap v8, v3178 + store little heap v24502, v17 + store little heap v24488, v3203 + store little heap v24502, v22736 + store little heap v24488, v1786 + store little heap v24502, v3219 + store little heap v24488, v3225 + store little heap v24502, v3237 + store little heap v24488, v17 + store little heap v3271, v3276 + store little heap v3282, v1786 + store little heap v24502, v3292 + store little heap v24488, v3298 + store little heap v24502, v3381 + store little heap v24488, v3387 + store little heap v8, v1786 + store little heap v3394, v1786 + store little heap v24502, v22756 + store little heap v3422, v1786 + store little heap v3431, v3436 + store little heap v24488, v3442 + store little heap v24502, v3454 + store little heap v24502, v3460 + store little heap v24502, v3472 + store little heap v24502, v3478 + store little heap v24502, v1786 + store little heap v24502, v3547 + store little heap v24488, v3553 + store little heap v24488, v3559 + store little heap v24488, v3565 + store little heap v24502, v3571 + store little heap v24502, v17 + store little heap v24502, v3609 + store little heap v24488, v3621 + store little heap v24488, v17 + store little heap v24502, v3680 + store little heap v3728, v3731 + store little heap v3737, v3742 + store little heap v24488, v3748 + store little heap v3752, v1786 + store little heap v24502, v24351 + store little heap v24502, v3766 + store little heap v24488, v3772 + store little heap v24488, v3778 + store little heap v24502, v3791 + store little heap v24502, v3803 + store little heap v8, v3842 + store little heap v24502, v3924 + store little heap v8, v1786 + store little heap v24488, v3938 + store little heap v24490, v23010 + store little heap v8, v3949 + store little heap v24488, v3955 + store little heap v24488, v3961 + store little heap v24488, v3967 + store little heap v24490, v6116 + store little heap v8, v3998 + store little heap v24502, v4005 + store little heap v24490, v23465 + store little heap v24499, v11017 + store little heap v24502, v11023 + store little heap v11033, v1786 + store little heap v24488, v17 + store little heap v24488, v11057 + store little heap v24488, v14908 + store little heap v24488, v14914 + store little heap v24475, v15189 + store little heap v24475, v15392 + store little heap v15411, v1786 + store little heap v24488, v4018 + store little heap v24488, v22842 + store little heap v24502, v4037 + store little heap v24488, v4049 + store little heap v24502, v4055 + store little heap v24502, v4061 + store little heap v24488, v17 + store little heap v24502, v1786 + store little heap v24490, v9508 + brif v10, block42, block43 + +block43: + brif.i32 v13, block41, block44 + +block44: + brif.i32 v14, block40, block45 + +block45: + brif.i32 v24211, block39, block46 + +block46: + brif.i32 v104, block38, block47 + +block47: + brif.i32 v24217, block37, block48 + +block48: + brif.i32 v232, block36, block49 + +block49: + brif.i32 v24233, block35, block50 + +block50: + brif.i32 v24236, block34, block51 + +block51: + brif.i32 v24254, block33, block52 + +block52: + brif.i32 v24259, block32, block53 + +block53: + brif.i32 v24263, block31, block54 + +block54: + brif.i32 v24266, block30, block55 + +block55: + brif.i32 v24269, block29, block56 + +block56: + brif.i32 v24275, block28, block57 + +block57: + brif.i32 v24278, block27, block58 + +block58: + brif.i32 v24292, block26, block59 + +block59: + brif.i32 v725, block25, block60 + +block60: + brif.i32 v24303, block24, block61 + +block61: + brif.i32 v24307, block23, block62 + +block62: + brif.i32 v862, block22, block63 + +block63: + brif.i32 v24322, block21, block64 + +block64: + brif.i32 v24324, block20, block65 + +block65: + brif.i32 v24334, block19, block66 + +block66: + brif.i32 v24338, block18, block67 + +block67: + brif.i32 v24349, block17, block68 + +block68: + brif.i32 v1053, block16, block69 + +block69: + brif.i32 v24364, block15, block70 + +block70: + brif.i32 v1193, block14, block71 + +block71: + brif.i32 v24382, block13, block72 + +block72: + brif.i32 v24385, block12, block73 + +block73: + brif.i32 v24393, block11, block74 + +block74: + brif.i32 v1307, block10, block75 + +block75: + brif.i32 v24401, block9, block76 + +block76: + brif.i32 v24410, block8, block77 + +block77: + brif.i32 v1472, block7, block78 + +block78: + brif.i32 v24429, block6, block79 + +block79: + brif.i32 v24446, block5, block80 + +block80: + brif.i32 v1721, block4, block81 + +block81: + brif.i32 v1728, block3, block82 + +block82: + store.i64 little heap v1762, v24483 + store.i64 little heap v24484, v24485 + store.i64 little heap v1813, v1818 + store.i64 little heap v1843, v1848 + store.i64 little heap v1849, v24492 + store.i64 little heap v1878, v1786 + store.i64 little heap v24502, v22570 + store.i64 little heap v1962, v17 + store.i64 little heap v2010, v2015 + store.i64 little heap v2016, v2021 + store.i64 little heap v2109, v2114 + store.i64 little heap v2115, v2120 + store.i64 little heap v24490, v22610 + store.i64 little heap v2252, v2257 + store.i64 little heap v2258, v2263 + store.i64 little heap v2316, v17 + store.i64 little heap v2335, v17 + store.i64 little heap v24502, v2430 + store.i64 little heap v2485, v2490 + store.i64 little heap v2515, v2520 + store.i64 little heap v2545, v2550 + store.i64 little heap v2551, v2556 + store.i64 little heap v2679, v2684 + store.i64 little heap v2685, v2690 + store.i64 little heap v24502, v22682 + store.i64 little heap v24502, v22692 + store.i64 little heap v2741, v2746 + store.i64 little heap v24490, v2795 + store.i64 little heap v2851, v2856 + store.i64 little heap v2857, v2862 + store.i64 little heap v2881, v2886 + store.i64 little heap v24502, v3037 + store.i64 little heap v3096, v1786 + store.i64 little heap v3130, v3135 + store.i64 little heap v3158, v17 + store.i64 little heap v3192, v3197 + store.i64 little heap v3247, v3250 + store.i64 little heap v3324, v17 + store.i64 little heap v3572, v3577 + store.i64 little heap v3625, v17 + store.i64 little heap v3792, v3797 + store.i32 little heap v24477, v22803 + store.i64 little heap v3877, v22812 + store.i64 little heap v3968, v3973 + store.i64 little heap v24502, v22823 + store.i64 little heap v4006, v4011 + store.i64 little heap v4038, v4043 + store.i64 little heap v4063, v4068 + store.i64 little heap v24502, v4081 + store.i64 little heap v4087, v4092 + store.i32 little heap v24488, v4149 + store.i64 little heap v24502, v22851 + store.i64 little heap v4163, v4168 + store.i32 little heap v24488, v4174 + store.i32 little heap v24488, v4180 + store.i64 little heap v4194, v4199 + store.i32 little heap v24488, v4211 + store.i32 little heap v8, v4216 + store.i64 little heap v4217, v4222 + store.i64 little heap v4223, v4228 + store.i64 little heap v24502, v4233 + store.i64 little heap v4239, v4244 + store.i32 little heap v24488, v4260 + store.i32 little heap v24488, v4277 + store.i32 little heap v24488, v4283 + store.i64 little heap v24502, v22871 + store.i64 little heap v4312, v22881 + store.i32 little heap v24488, v4359 + store.i32 little heap v24488, v17 + store.i64 little heap v4374, v4379 + store.i32 little heap v24488, v1786 + store.i64 little heap v24502, v4395 + store.i32 little heap v24488, v4412 + store.i64 little heap v4426, v4431 + store.i64 little heap v4443, v4448 + store.i32 little heap v24488, v4454 + store.i64 little heap v4462, v4465 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v1786 + store.i64 little heap v4501, v4506 + store.i32 little heap v24488, v4512 + store.i64 little heap v24502, v4524 + store.i32 little heap v24488, v24524 + store.i32 little heap v8, v4611 + store.i64 little heap v24502, v22907 + store.i32 little heap v24488, v24526 + store.i32 little heap v24488, v24528 + store.i32 little heap v8, v4642 + store.i64 little heap v4643, v4648 + store.i32 little heap v24488, v4665 + store.i32 little heap v24488, v24530 + store.i32 little heap v8, v24532 + store.i64 little heap v4676, v4681 + store.i64 little heap v24502, v22934 + store.i32 little heap v24488, v24534 + store.i64 little heap v24502, v24536 + store.i64 little heap v24502, v4713 + store.i64 little heap v4733, v24539 + store.i64 little heap v4753, v4758 + store.i32 little heap v24488, v1786 + store.i32 little heap v24488, v4785 + store.i32 little heap v24488, v4791 + store.i64 little heap v4798, v4803 + store.i64 little heap v4804, v4809 + store.i32 little heap v24488, v4821 + store.i64 little heap v24502, v22954 + store.i64 little heap v4835, v4840 + store.i32 little heap v24488, v4846 + store.i64 little heap v4859, v4864 + store.i64 little heap v24502, v4870 + store.i64 little heap v24502, v4921 + store.i32 little heap v24488, v4927 + store.i64 little heap v24502, v4933 + store.i64 little heap v24502, v4939 + store.i32 little heap v4949, v1786 + store.i64 little heap v4957, v17 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v4987 + store.i32 little heap v24488, v5022 + store.i64 little heap v5030, v22964 + store.i64 little heap v24502, v22973 + store.i32 little heap v24488, v5048 + store.i32 little heap v24488, v5054 + store.i64 little heap v5061, v5066 + store.i64 little heap v5067, v5072 + store.i32 little heap v24488, v5084 + store.i64 little heap v5091, v5096 + store.i64 little heap v5098, v22982 + store.i32 little heap v5109, v5114 + store.i32 little heap v5118, v1786 + store.i32 little heap v24488, v5127 + store.i64 little heap v24502, v22991 + store.i64 little heap v24502, v5146 + store.i32 little heap v5179, v17 + store.i64 little heap v24502, v23001 + store.i64 little heap v5230, v5235 + store.i32 little heap v24488, v5254 + store.i64 little heap v5261, v5266 + store.i32 little heap v24488, v5279 + store.i32 little heap v24488, v5285 + store.i64 little heap v5292, v5297 + store.i64 little heap v5298, v5303 + v5310 = load.i64 little heap v5309 + store.i64 little heap v24502, v5372 + store.i64 little heap v24502, v5378 + store.i64 little heap v24502, v5410 + store.i64 little heap v5416, v5421 + store.i64 little heap v5425, v1786 + store.i32 little heap v8, v5439 + store.i32 little heap v24488, v5445 + store.i32 little heap v8, v5457 + store.i64 little heap v5458, v5463 + store.i64 little heap v5464, v5469 + store.i32 little heap v24488, v5475 + store.i32 little heap v24488, v5481 + store.i64 little heap v5488, v5493 + store.i64 little heap v5494, v5499 + store.i32 little heap v24488, v5505 + store.i32 little heap v24488, v5511 + store.i64 little heap v5518, v5523 + store.i64 little heap v24502, v5529 + store.i32 little heap v24488, v5538 + store.i32 little heap v24488, v5544 + store.i32 little heap v5567, v1786 + store.i64 little heap v5571, v17 + store.i64 little heap v5580, v5585 + store.i64 little heap v24502, v5591 + store.i64 little heap v5600, v5605 + store.i64 little heap v5630, v1786 + store.i64 little heap v5639, v5644 + store.i32 little heap v24488, v5649 + store.i32 little heap v24488, v17 + store.i64 little heap v5674, v17 + store.i64 little heap v24502, v5683 + store.i64 little heap v24502, v5688 + store.i64 little heap v24502, v24351 + store.i64 little heap v24502, v17 + store.i64 little heap v24502, v23031 + store.i64 little heap v5781, v5786 + store.i32 little heap v24488, v5792 + store.i64 little heap v24502, v5804 + store.i64 little heap v24502, v5810 + store.i64 little heap v5833, v1786 + store.i64 little heap v5842, v5847 + store.i32 little heap v24488, v5863 + store.i32 little heap v24488, v5886 + store.i32 little heap v8, v5891 + store.i64 little heap v5892, v5897 + store.i32 little heap v24488, v5909 + store.i32 little heap v24488, v5915 + store.i64 little heap v5922, v5927 + store.i32 little heap v24488, v5939 + store.i32 little heap v24488, v5945 + store.i32 little heap v8, v5968 + store.i32 little heap v24488, v5979 + store.i32 little heap v24488, v6059 + store.i32 little heap v24488, v6065 + store.i64 little heap v6072, v6077 + store.i64 little heap v24502, v6083 + store.i32 little heap v24488, v6089 + store.i32 little heap v24488, v6095 + store.i64 little heap v6104, v6109 + store.i32 little heap v24488, v6122 + store.i32 little heap v24488, v6128 + store.i64 little heap v6135, v6140 + store.i32 little heap v24488, v17 + store.i64 little heap v6205, v6208 + store.i64 little heap v24502, v6214 + store.i64 little heap v6220, v6225 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v6234 + store.i64 little heap v24502, v1786 + store.i64 little heap v6245, v6250 + store.i32 little heap v8, v6255 + store.i32 little heap v24488, v6261 + store.i32 little heap v8, v6266 + store.i32 little heap v24488, v6272 + store.i64 little heap v6280, v6285 + store.i32 little heap v24488, v6291 + store.i32 little heap v24488, v6303 + store.i64 little heap v6304, v6309 + store.i64 little heap v24502, v23099 + store.i32 little heap v24488, v6322 + store.i32 little heap v24488, v6328 + store.i64 little heap v6335, v6340 + store.i64 little heap v6341, v6346 + store.i64 little heap v24502, v23108 + v6393 = load.i64 little heap v24545 + store.i64 little heap v6432, v6437 + store.i64 little heap v6441, v1786 + store.i32 little heap v24488, v6467 + store.i32 little heap v24488, v6473 + store.i32 little heap v24488, v6479 + store.i64 little heap v6480, v6485 + store.i64 little heap v6487, v23126 + store.i32 little heap v24488, v6498 + store.i32 little heap v24488, v6504 + store.i64 little heap v6511, v6516 + store.i64 little heap v24502, v6522 + store.i32 little heap v24488, v6528 + store.i32 little heap v24488, v6534 + store.i32 little heap v24488, v6540 + store.i64 little heap v24502, v6546 + store.i64 little heap v6547, v6552 + store.i32 little heap v24488, v6557 + store.i64 little heap v24502, v6574 + store.i32 little heap v24488, v6585 + store.i32 little heap v24488, v6591 + store.i64 little heap v6624, v6629 + store.i64 little heap v6656, v1786 + store.i32 little heap v24488, v6682 + store.i32 little heap v24488, v6688 + store.i64 little heap v24502, v6700 + store.i32 little heap v24488, v6713 + store.i32 little heap v24488, v6719 + store.i32 little heap v8, v23157 + store.i64 little heap v24502, v23166 + store.i64 little heap v6733, v6738 + store.i32 little heap v24488, v6744 + store.i32 little heap v24488, v6750 + store.i32 little heap v24488, v6756 + store.i64 little heap v6758, v23176 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v6783 + store.i64 little heap v24502, v23185 + store.i32 little heap v24488, v6796 + store.i64 little heap v6810, v6815 + store.i64 little heap v6862, v1786 + store.i32 little heap v8, v6882 + store.i32 little heap v24488, v6888 + store.i32 little heap v24488, v6894 + store.i32 little heap v8, v6899 + store.i64 little heap v6906, v6911 + store.i32 little heap v24488, v6917 + store.i32 little heap v24488, v6923 + store.i64 little heap v6930, v6935 + store.i64 little heap v6936, v6941 + store.i32 little heap v24488, v6953 + store.i32 little heap v8, v6958 + store.i64 little heap v6959, v6964 + store.i64 little heap v24502, v23218 + store.i32 little heap v24488, v6976 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v6985 + store.i64 little heap v7027, v17 + store.i32 little heap v24488, v24351 + store.i64 little heap v7058, v7063 + store.i32 little heap v8, v7086 + store.i32 little heap v24488, v7092 + store.i32 little heap v24488, v7098 + store.i32 little heap v8, v7103 + store.i64 little heap v24502, v7109 + store.i64 little heap v24502, v7115 + store.i32 little heap v24488, v7121 + store.i32 little heap v24488, v7127 + store.i32 little heap v8, v7132 + store.i64 little heap v7140, v7145 + store.i32 little heap v24488, v7151 + store.i32 little heap v24488, v7157 + store.i64 little heap v7164, v7169 + store.i64 little heap v7170, v7175 + store.i32 little heap v24488, v7193 + store.i64 little heap v24502, v7199 + store.i32 little heap v24488, v7205 + store.i64 little heap v24502, v7212 + store.i64 little heap v24502, v7218 + store.i64 little heap v7233, v7236 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v7291 + store.i32 little heap v24488, v7297 + store.i32 little heap v8, v7302 + store.i64 little heap v24502, v7309 + store.i64 little heap v7310, v7315 + store.i32 little heap v24488, v7321 + store.i32 little heap v24488, v7327 + store.i64 little heap v7335, v7340 + store.i64 little heap v24502, v23246 + store.i32 little heap v24488, v7353 + store.i32 little heap v24488, v7359 + store.i32 little heap v8, v7364 + store.i64 little heap v7365, v7370 + store.i32 little heap v24488, v7395 + store.i32 little heap v24488, v7413 + store.i32 little heap v24488, v7419 + store.i32 little heap v7441, v1786 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v7472 + store.i64 little heap v24502, v1786 + store.i32 little heap v24488, v7500 + store.i32 little heap v24488, v7506 + store.i64 little heap v7514, v7519 + store.i64 little heap v7520, v7525 + store.i32 little heap v24488, v7531 + store.i32 little heap v24488, v7537 + store.i64 little heap v7544, v7549 + store.i64 little heap v7550, v7555 + store.i32 little heap v24488, v7561 + store.i32 little heap v24488, v7567 + store.i64 little heap v24502, v7581 + store.i64 little heap v7582, v7587 + store.i32 little heap v24488, v7608 + store.i64 little heap v7625, v7630 + store.i64 little heap v7653, v7658 + store.i64 little heap v24502, v7675 + store.i32 little heap v24488, v7711 + store.i32 little heap v24488, v7717 + store.i64 little heap v7718, v7723 + store.i64 little heap v24502, v23311 + store.i32 little heap v24488, v7736 + store.i32 little heap v24488, v7742 + store.i32 little heap v24488, v7748 + store.i64 little heap v7749, v7754 + store.i64 little heap v7755, v7760 + store.i32 little heap v24488, v7772 + store.i32 little heap v24488, v7778 + store.i64 little heap v7779, v7784 + store.i64 little heap v24502, v7801 + store.i32 little heap v7820, v1786 + store.i32 little heap v24488, v7857 + store.i32 little heap v24488, v17 + store.i64 little heap v7878, v7883 + store.i64 little heap v24502, v7889 + store.i64 little heap v7898, v7903 + store.i32 little heap v24488, v7933 + store.i32 little heap v24488, v7939 + store.i32 little heap v8, v7944 + store.i64 little heap v7945, v7950 + store.i64 little heap v7951, v7956 + store.i32 little heap v24488, v7962 + store.i32 little heap v24488, v7968 + store.i64 little heap v7975, v7980 + store.i64 little heap v7981, v7986 + store.i32 little heap v24488, v7998 + store.i32 little heap v24488, v8004 + store.i64 little heap v8005, v8010 + store.i64 little heap v8011, v8016 + store.i32 little heap v24488, v8032 + store.i64 little heap v24502, v23333 + store.i64 little heap v24502, v8045 + store.i64 little heap v24502, v8057 + store.i64 little heap v24502, v8063 + store.i64 little heap v8089, v17 + store.i64 little heap v8111, v8114 + store.i64 little heap v8145, v8150 + store.i32 little heap v24488, v8162 + store.i32 little heap v24488, v23351 + store.i32 little heap v8, v8174 + store.i64 little heap v8175, v8180 + store.i64 little heap v8181, v8186 + store.i64 little heap v8206, v8211 + store.i64 little heap v8212, v8217 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v8225 + store.i32 little heap v24488, v8242 + store.i64 little heap v24502, v23369 + store.i64 little heap v24502, v23378 + store.i64 little heap v24502, v8308 + store.i32 little heap v24488, v8326 + store.i32 little heap v24488, v8332 + store.i64 little heap v24502, v8344 + store.i64 little heap v8345, v8350 + store.i32 little heap v24488, v8356 + store.i32 little heap v24488, v8362 + store.i32 little heap v8, v8367 + store.i64 little heap v8368, v8373 + store.i64 little heap v8374, v8379 + store.i32 little heap v24488, v8391 + store.i64 little heap v8399, v23388 + store.i64 little heap v8405, v8410 + store.i32 little heap v24488, v8420 + store.i32 little heap v24488, v8426 + store.i64 little heap v24502, v8432 + store.i32 little heap v24488, v8438 + store.i64 little heap v24502, v8444 + store.i64 little heap v24502, v8450 + store.i32 little heap v24488, v17 + store.i64 little heap v8460, v1786 + store.i64 little heap v8476, v8481 + store.i32 little heap v24488, v1786 + store.i64 little heap v8490, v8495 + store.i64 little heap v8536, v8541 + store.i32 little heap v24488, v8553 + store.i32 little heap v24488, v8559 + store.i32 little heap v8, v8564 + store.i64 little heap v24502, v8570 + store.i64 little heap v8571, v8576 + store.i32 little heap v24488, v8588 + store.i32 little heap v8, v8593 + store.i64 little heap v8594, v8599 + store.i64 little heap v8600, v8605 + store.i32 little heap v24488, v8610 + store.i32 little heap v24488, v8629 + store.i32 little heap v24488, v8642 + store.i32 little heap v8696, v8699 + store.i64 little heap v24502, v1786 + store.i32 little heap v24488, v8735 + store.i32 little heap v24488, v8741 + store.i32 little heap v24488, v8747 + store.i64 little heap v8748, v8753 + store.i64 little heap v8754, v8759 + store.i32 little heap v24488, v8765 + store.i32 little heap v24488, v8771 + store.i32 little heap v8, v8776 + store.i64 little heap v8777, v8782 + store.i32 little heap v24488, v8801 + store.i32 little heap v8, v8806 + store.i64 little heap v24502, v8813 + store.i32 little heap v24488, v8825 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v8834 + store.i32 little heap v24488, v8846 + store.i64 little heap v24502, v8858 + store.i32 little heap v8870, v1786 + store.i64 little heap v24502, v8908 + store.i64 little heap v24502, v17 + store.i32 little heap v8, v8929 + store.i32 little heap v24488, v8935 + store.i32 little heap v24488, v8941 + store.i32 little heap v8, v8946 + store.i64 little heap v8947, v8952 + store.i32 little heap v8, v8976 + store.i64 little heap v24490, v23418 + store.i64 little heap v24502, v8989 + store.i32 little heap v24488, v8995 + store.i32 little heap v24488, v9001 + store.i64 little heap v24502, v23427 + store.i64 little heap v24502, v9020 + store.i32 little heap v9026, v9031 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v9040 + store.i64 little heap v24502, v23437 + store.i64 little heap v24502, v9059 + store.i32 little heap v24488, v17 + store.i64 little heap v9067, v17 + store.i64 little heap v24502, v23446 + store.i64 little heap v9097, v9102 + store.i32 little heap v9110, v1786 + store.i32 little heap v24488, v9126 + store.i64 little heap v9131, v3731 + store.i64 little heap v24502, v9196 + store.i64 little heap v9202, v9205 + store.i64 little heap v9211, v9216 + store.i32 little heap v24488, v9222 + store.i64 little heap v24502, v17 + store.i64 little heap v9250, v9255 + store.i64 little heap v9279, v9284 + store.i64 little heap v9285, v9290 + store.i64 little heap v24502, v1786 + store.i64 little heap v24502, v9306 + store.i32 little heap v24488, v9317 + store.i64 little heap v24502, v9329 + store.i64 little heap v24502, v9341 + store.i64 little heap v24502, v23474 + store.i32 little heap v9374, v9379 + store.i32 little heap v24488, v9385 + store.i32 little heap v24488, v9391 + store.i32 little heap v8, v9396 + store.i64 little heap v24502, v9402 + store.i64 little heap v9403, v9408 + store.i64 little heap v9419, v9424 + store.i32 little heap v24488, v9430 + store.i32 little heap v9437, v9440 + store.i64 little heap v24502, v1786 + store.i64 little heap v24502, v23491 + store.i32 little heap v24488, v9502 + store.i64 little heap v24502, v9532 + store.i32 little heap v24488, v9598 + store.i32 little heap v24488, v9604 + store.i64 little heap v9610, v9615 + store.i64 little heap v9616, v9621 + store.i64 little heap v9650, v1786 + store.i64 little heap v24502, v9659 + store.i64 little heap v24502, v24351 + store.i64 little heap v24502, v1786 + store.i64 little heap v9782, v9787 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v9801 + store.i32 little heap v24488, v9825 + store.i64 little heap v24490, v23553 + store.i64 little heap v9839, v9844 + store.i32 little heap v8, v9861 + store.i64 little heap v24502, v9867 + store.i64 little heap v9868, v9873 + store.i32 little heap v24488, v9879 + store.i64 little heap v9893, v9898 + store.i64 little heap v9899, v9904 + store.i32 little heap v24488, v23562 + store.i64 little heap v24502, v9941 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v9971 + store.i64 little heap v24502, v9977 + store.i32 little heap v24488, v9987 + store.i32 little heap v24488, v9993 + store.i32 little heap v8, v9998 + store.i64 little heap v24502, v10005 + store.i64 little heap v10006, v10011 + store.i32 little heap v24488, v10017 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v10025 + store.i64 little heap v24502, v10031 + store.i64 little heap v24502, v24351 + store.i32 little heap v8, v1786 + store.i32 little heap v24488, v10068 + store.i32 little heap v24488, v10074 + store.i64 little heap v24490, v23572 + store.i64 little heap v10088, v10093 + store.i64 little heap v10104, v1786 + store.i64 little heap v10113, v10118 + store.i32 little heap v24488, v10124 + store.i64 little heap v24502, v10136 + store.i64 little heap v24502, v10155 + store.i64 little heap v10167, v10170 + store.i64 little heap v10176, v10181 + store.i32 little heap v24488, v10201 + store.i32 little heap v24488, v10295 + store.i32 little heap v8, v23600 + store.i32 little heap v24488, v10313 + store.i32 little heap v8, v10318 + store.i32 little heap v10338, v1786 + store.i64 little heap v10344, v1786 + store.i64 little heap v24502, v10354 + store.i32 little heap v24488, v10360 + store.i64 little heap v24502, v10372 + store.i32 little heap v24488, v10432 + store.i32 little heap v8, v10443 + store.i32 little heap v10445, v23620 + store.i32 little heap v24488, v10456 + store.i32 little heap v8, v10461 + store.i64 little heap v10462, v10467 + store.i64 little heap v10469, v23629 + store.i32 little heap v24488, v10480 + store.i32 little heap v24488, v10486 + store.i32 little heap v24488, v10492 + store.i64 little heap v10493, v10498 + store.i32 little heap v24488, v10510 + store.i32 little heap v24488, v10516 + store.i32 little heap v24488, v10523 + store.i64 little heap v24502, v10529 + store.i64 little heap v24502, v23638 + store.i32 little heap v24488, v10547 + store.i64 little heap v10562, v23656 + store.i64 little heap v24502, v10573 + store.i64 little heap v10580, v1786 + store.i64 little heap v24502, v17 + store.i32 little heap v24488, v10627 + store.i32 little heap v24488, v10633 + store.i64 little heap v10641, v10646 + store.i64 little heap v10647, v10652 + store.i32 little heap v8, v1786 + store.i64 little heap v24502, v24351 + store.i64 little heap v10693, v10698 + store.i32 little heap v24488, v10718 + store.i64 little heap v10727, v1786 + store.i64 little heap v24502, v10736 + store.i32 little heap v8, v10747 + store.i32 little heap v24488, v17 + store.i64 little heap v10751, v1786 + store.i64 little heap v24502, v10760 + store.i32 little heap v24488, v10766 + store.i64 little heap v24502, v10778 + store.i64 little heap v24502, v23674 + store.i64 little heap v24502, v10797 + store.i64 little heap v24502, v10803 + store.i64 little heap v24502, v17 + store.i64 little heap v24502, v10814 + store.i64 little heap v24502, v17 + store.i64 little heap v24502, v10849 + store.i32 little heap v24488, v10855 + store.i32 little heap v24488, v1786 + store.i64 little heap v24502, v1786 + store.i64 little heap v10883, v10888 + store.i32 little heap v24488, v10894 + store.i32 little heap v8, v1786 + store.i64 little heap v24502, v1786 + store.i64 little heap v24502, v23683 + store.i32 little heap v24488, v10930 + store.i32 little heap v10934, v1786 + store.i64 little heap v24502, v10952 + store.i32 little heap v24488, v17 + store.i64 little heap v10973, v1786 + store.i64 little heap v10982, v10987 + store.i32 little heap v24488, v10993 + store.i64 little heap v24502, v11011 + store.i64 little heap v11038, v11043 + store.i32 little heap v24488, v11069 + store.i32 little heap v24488, v11075 + store.i32 little heap v24488, v11081 + store.i64 little heap v24502, v23692 + store.i64 little heap v24502, v11101 + store.i32 little heap v24488, v11107 + store.i32 little heap v24488, v11113 + store.i32 little heap v24488, v11119 + store.i64 little heap v11120, v11125 + store.i64 little heap v11127, v11132 + store.i32 little heap v24488, v11144 + store.i64 little heap v11158, v11163 + store.i64 little heap v24502, v11181 + store.i64 little heap v24502, v11199 + store.i64 little heap v11215, v17 + store.i64 little heap v11224, v11229 + store.i64 little heap v11235, v11240 + store.i32 little heap v24488, v11255 + store.i32 little heap v11265, v11270 + store.i32 little heap v11292, v11295 + store.i64 little heap v11298, v1786 + store.i32 little heap v8, v1786 + store.i64 little heap v11341, v11346 + store.i32 little heap v24488, v11363 + store.i32 little heap v8, v1786 + store.i64 little heap v24502, v24351 + store.i64 little heap v11398, v11401 + store.i64 little heap v24502, v11444 + store.i64 little heap v24502, v11460 + store.i32 little heap v24488, v17 + store.i32 little heap v11475, v11480 + store.i32 little heap v24488, v11510 + store.i64 little heap v11511, v11516 + store.i64 little heap v11517, v11522 + store.i32 little heap v24488, v11540 + store.i64 little heap v24502, v11546 + store.i64 little heap v11547, v11552 + store.i32 little heap v24488, v11558 + store.i32 little heap v24488, v11564 + store.i32 little heap v8, v11569 + store.i64 little heap v11570, v11575 + store.i32 little heap v24488, v11588 + store.i64 little heap v11596, v23741 + store.i64 little heap v24502, v11613 + store.i64 little heap v24502, v11620 + store.i32 little heap v24488, v1786 + store.i64 little heap v24502, v17 + store.i64 little heap v24502, v11657 + store.i32 little heap v24488, v11678 + store.i64 little heap v24502, v11697 + store.i32 little heap v24488, v11703 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v11711 + store.i64 little heap v24502, v11718 + store.i64 little heap v11722, v1786 + store.i32 little heap v8, v1786 + store.i64 little heap v11734, v11737 + store.i64 little heap v11743, v11748 + store.i32 little heap v24488, v11766 + store.i64 little heap v11779, v11784 + store.i64 little heap v24502, v11791 + store.i64 little heap v11811, v23760 + v11841 = load.i32 little heap v11840 + store.i64 little heap v24502, v11856 + store.i64 little heap v24502, v11875 + store.i32 little heap v24488, v1786 + store.i32 little heap v11890, v11895 + store.i32 little heap v24488, v11919 + store.i64 little heap v11925, v11930 + store.i32 little heap v8, v11954 + store.i64 little heap v24502, v11960 + store.i64 little heap v11961, v11966 + store.i32 little heap v24488, v11972 + store.i32 little heap v24488, v11978 + store.i32 little heap v24488, v11984 + store.i64 little heap v24502, v23770 + store.i64 little heap v11992, v11997 + store.i32 little heap v24488, v12003 + store.i64 little heap v24502, v12009 + store.i32 little heap v24488, v12021 + store.i32 little heap v24488, v12027 + store.i32 little heap v24488, v17 + store.i64 little heap v12043, v17 + store.i64 little heap v24502, v12080 + store.i64 little heap v12086, v12089 + store.i32 little heap v24488, v12113 + store.i32 little heap v24488, v12119 + store.i64 little heap v12126, v12131 + store.i32 little heap v24488, v12149 + store.i64 little heap v12156, v12161 + store.i64 little heap v12162, v12167 + store.i32 little heap v1523, v12177 + store.i32 little heap v24488, v12182 + store.i32 little heap v8, v12187 + store.i64 little heap v12188, v12193 + store.i64 little heap v24502, v12200 + store.i32 little heap v24488, v23793 + store.i32 little heap v24488, v1786 + store.i32 little heap v24488, v12218 + store.i64 little heap v24502, v12224 + store.i32 little heap v24488, v12230 + store.i64 little heap v24502, v12236 + store.i64 little heap v24502, v12242 + store.i64 little heap v12261, v1786 + store.i64 little heap v12270, v12275 + store.i32 little heap v12291, v2901 + store.i32 little heap v24488, v12330 + store.i32 little heap v24488, v12336 + store.i32 little heap v24488, v12342 + store.i64 little heap v12343, v12348 + store.i32 little heap v24488, v12360 + store.i32 little heap v24488, v12366 + store.i64 little heap v12380, v12385 + store.i32 little heap v24477, v23803 + store.i32 little heap v24488, v12398 + store.i32 little heap v8, v12403 + store.i64 little heap v24502, v12410 + store.i64 little heap v12411, v12416 + store.i32 little heap v24488, v12421 + store.i32 little heap v24488, v12436 + store.i64 little heap v24502, v23812 + store.i32 little heap v24488, v12449 + store.i64 little heap v24502, v12455 + store.i64 little heap v24502, v12461 + store.i64 little heap v12475, v17 + store.i64 little heap v24502, v12495 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v12505 + store.i64 little heap v24490, v23822 + store.i32 little heap v24488, v12566 + store.i32 little heap v24488, v12572 + store.i64 little heap v12579, v12584 + store.i64 little heap v12585, v12590 + store.i32 little heap v24488, v23831 + store.i32 little heap v8, v12608 + store.i64 little heap v12609, v12614 + store.i64 little heap v24502, v12620 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v12640 + store.i64 little heap v24502, v12646 + store.i32 little heap v24488, v12652 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v1786 + store.i64 little heap v24502, v12684 + store.i64 little heap v12690, v12695 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v12704 + store.i64 little heap v24502, v17 + istore8.i32 little heap v24488, v12715 + store.i32 little heap v8, v12726 + store.i32 little heap v24488, v12732 + store.i32 little heap v24488, v12738 + store.i32 little heap v24488, v12744 + store.i64 little heap v12745, v12750 + store.i64 little heap v24502, v23840 + store.i32 little heap v8, v12762 + store.i32 little heap v24488, v12768 + store.i32 little heap v8, v12773 + store.i64 little heap v12774, v12779 + store.i64 little heap v12780, v12785 + store.i32 little heap v24488, v12791 + store.i32 little heap v24488, v12797 + store.i32 little heap v8, v12802 + store.i64 little heap v12803, v12808 + store.i64 little heap v12809, v12814 + store.i32 little heap v24488, v12819 + store.i32 little heap v24488, v1786 + store.i32 little heap v24488, v12828 + store.i64 little heap v24502, v12834 + store.i32 little heap v24488, v12840 + store.i64 little heap v24502, v12846 + store.i64 little heap v24502, v12852 + store.i64 little heap v12869, v1786 + store.i64 little heap v24502, v12879 + store.i64 little heap v24502, v23849 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v12896 + store.i64 little heap v24502, v1786 + store.i32 little heap v24488, v12918 + store.i32 little heap v24488, v12931 + store.i32 little heap v8, v12936 + store.i64 little heap v12937, v12942 + store.i64 little heap v24490, v23859 + store.i32 little heap v24488, v12955 + store.i32 little heap v24488, v12961 + store.i32 little heap v8, v12966 + store.i64 little heap v12967, v12972 + store.i32 little heap v24488, v12984 + store.i32 little heap v24488, v12990 + store.i32 little heap v8, v12995 + store.i64 little heap v13003, v13008 + store.i32 little heap v24488, v13013 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v13022 + store.i32 little heap v24488, v13034 + store.i64 little heap v24502, v13046 + store.i64 little heap v13047, v13052 + store.i64 little heap v13061, v1786 + store.i64 little heap v13070, v13075 + store.i64 little heap v24502, v13081 + store.i64 little heap v13090, v13095 + istore8.i32 little heap v8, v13106 + store.i64 little heap v24502, v13112 + store.i32 little heap v24488, v13130 + store.i32 little heap v8, v13135 + store.i64 little heap v13143, v13148 + store.i32 little heap v24488, v13167 + store.i64 little heap v13168, v13173 + store.i64 little heap v24502, v13179 + store.i32 little heap v24488, v13185 + store.i32 little heap v24488, v13191 + store.i32 little heap v8, v13196 + store.i64 little heap v13197, v13202 + store.i64 little heap v24490, v23878 + store.i32 little heap v24488, v13214 + store.i32 little heap v13222, v1786 + store.i32 little heap v24488, v13231 + store.i64 little heap v24502, v13238 + store.i64 little heap v24502, v13250 + store.i64 little heap v24502, v13274 + store.i64 little heap v13280, v13285 + v13291 = load.i32 little heap v24485 + store.i64 little heap v13300, v13305 + store.i64 little heap v13309, v1786 + store.i32 little heap v8, v13330 + store.i32 little heap v24488, v13336 + store.i32 little heap v24488, v13342 + store.i32 little heap v24488, v13348 + store.i64 little heap v24502, v13354 + store.i64 little heap v13355, v13360 + store.i64 little heap v24502, v13390 + store.i32 little heap v24488, v13396 + store.i32 little heap v24488, v13402 + store.i32 little heap v24488, v13408 + store.i64 little heap v13409, v13414 + store.i32 little heap v24488, v1786 + store.i32 little heap v24488, v13435 + store.i64 little heap v24502, v13441 + store.i64 little heap v24502, v13447 + store.i64 little heap v13466, v13469 + store.i64 little heap v13483, v13488 + store.i64 little heap v13497, v13502 + store.i32 little heap v13508, v1786 + store.i32 little heap v24488, v23920 + store.i32 little heap v24488, v13540 + store.i32 little heap v24488, v13570 + store.i32 little heap v24488, v13576 + store.i32 little heap v8, v13581 + store.i64 little heap v13582, v13587 + store.i32 little heap v24488, v13599 + store.i32 little heap v24488, v13609 + store.i32 little heap v24488, v13615 + store.i64 little heap v24502, v13622 + store.i64 little heap v13623, v13628 + store.i32 little heap v13647, v13652 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v13661 + store.i64 little heap v24502, v13667 + store.i64 little heap v24502, v13673 + store.i64 little heap v24502, v13679 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v23969 + store.i32 little heap v24488, v13785 + store.i64 little heap v24502, v13795 + store.i64 little heap v13819, v13822 + store.i64 little heap v24490, v23999 + store.i64 little heap v24502, v24009 + store.i32 little heap v24488, v17 + store.i64 little heap v24502, v1786 + store.i64 little heap v13933, v13938 + store.i64 little heap v24502, v13944 + store.i64 little heap v24502, v13961 + istore8.i32 little heap v8, v13966 + store.i32 little heap v24488, v1786 + store.i32 little heap v13977, v13982 + store.i64 little heap v24502, v17 + store.i64 little heap v24502, v13993 + store.i64 little heap v14013, v17 + store.i64 little heap v24502, v14022 + store.i32 little heap v24488, v14039 + store.i32 little heap v24488, v1786 + store.i64 little heap v14043, v1786 + store.i32 little heap v24488, v14076 + store.i32 little heap v8, v1786 + store.i64 little heap v14084, v24351 + store.i64 little heap v24502, v14093 + store.i32 little heap v8, v14104 + store.i32 little heap v24488, v17 + store.i64 little heap v14109, v14112 + store.i64 little heap v24502, v14119 + store.i32 little heap v24488, v14125 + store.i64 little heap v24502, v14137 + store.i64 little heap v24502, v14155 + store.i64 little heap v24502, v14161 + store.i64 little heap v14173, v14178 + istore8.i32 little heap v8, v14183 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v14191 + store.i64 little heap v24502, v14207 + store.i32 little heap v24488, v14213 + store.i64 little heap v24502, v3731 + store.i64 little heap v14227, v14232 + store.i64 little heap v14238, v1786 + store.i64 little heap v24502, v24021 + store.i32 little heap v24488, v14254 + store.i32 little heap v14258, v1786 + store.i64 little heap v14266, v1786 + store.i64 little heap v24502, v14308 + store.i64 little heap v14314, v14319 + store.i32 little heap v24488, v17 + store.i64 little heap v14348, v14353 + store.i32 little heap v24488, v14359 + store.i64 little heap v24502, v14371 + store.i64 little heap v24502, v14377 + store.i64 little heap v24502, v14389 + store.i64 little heap v24502, v14395 + store.i64 little heap v24502, v1786 + istore8.i32 little heap v24488, v14411 + store.i32 little heap v24488, v14429 + store.i32 little heap v24488, v14442 + store.i64 little heap v14449, v14454 + store.i32 little heap v24488, v14467 + store.i32 little heap v24488, v14473 + store.i64 little heap v24502, v14485 + store.i64 little heap v14486, v14491 + store.i64 little heap v24502, v1786 + store.i64 little heap v24502, v14501 + store.i32 little heap v24488, v14507 + store.i64 little heap v14531, v14536 + store.i32 little heap v8, v14541 + store.i64 little heap v24502, v1786 + store.i64 little heap v24502, v14551 + store.i32 little heap v24488, v14557 + store.i64 little heap v24502, v14569 + store.i64 little heap v24502, v14587 + store.i64 little heap v14597, v1786 + store.i64 little heap v14606, v14611 + istore8.i32 little heap v8, v14616 + store.i32 little heap v24488, v14630 + store.i32 little heap v24488, v14636 + store.i32 little heap v24488, v14642 + store.i64 little heap v24502, v24063 + store.i64 little heap v14656, v14661 + store.i32 little heap v24488, v14680 + store.i64 little heap v14705, v14710 + store.i32 little heap v8, v14733 + store.i64 little heap v14734, v14739 + store.i64 little heap v24502, v14745 + store.i64 little heap v24502, v24351 + store.i64 little heap v14756, v14761 + store.i32 little heap v24488, v14767 + store.i64 little heap v24502, v14779 + store.i64 little heap v24502, v14797 + store.i64 little heap v24502, v17 + store.i64 little heap v24502, v14814 + store.i32 little heap v24488, v17 + store.i32 little heap v24488, v14828 + store.i32 little heap v24488, v14847 + store.i32 little heap v24488, v14853 + store.i32 little heap v24488, v14859 + store.i64 little heap v14860, v14865 + store.i64 little heap v14866, v14871 + store.i32 little heap v24488, v14883 + store.i64 little heap v24502, v24073 + store.i64 little heap v14897, v14902 + store.i32 little heap v24488, v14938 + store.i64 little heap v24502, v14944 + store.i32 little heap v24488, v14950 + store.i64 little heap v24502, v15024 + istore8.i32 little heap v8, v15030 + store.i32 little heap v24488, v15036 + store.i32 little heap v24488, v15042 + store.i64 little heap v15056, v15061 + store.i32 little heap v24488, v15073 + store.i64 little heap v15080, v15085 + store.i64 little heap v15086, v15091 + store.i32 little heap v24488, v15105 + store.i64 little heap v24502, v3731 + store.i64 little heap v24502, v24093 + store.i64 little heap v15168, v17 + store.i32 little heap v24488, v15183 + store.i64 little heap v24502, v15195 + store.i64 little heap v24502, v15202 + store.i64 little heap v24502, v15215 + istore8.i32 little heap v24477, v15241 + store.i32 little heap v24488, v17 + store.i64 little heap v15285, v15290 + store.i64 little heap v15291, v15296 + store.i32 little heap v24488, v15308 + store.i32 little heap v8, v15313 + store.i64 little heap v24502, v15319 + store.i64 little heap v15320, v15325 + store.i32 little heap v8, v15343 + store.i64 little heap v15344, v15349 + store.i32 little heap v24488, v15374 + store.i64 little heap v24502, v15386 + store.i64 little heap v15454, v1786 + istore8.i32 little heap v24488, v15463 + store.i32 little heap v24488, v15476 + store.i32 little heap v24488, v15482 + store.i64 little heap v24490, v24137 + store.i64 little heap v15490, v15495 + store.i32 little heap v24488, v15501 + store.i32 little heap v24488, v15507 + store.i32 little heap v24488, v15513 + store.i64 little heap v15514, v15519 + store.i64 little heap v15520, v15525 + store.i32 little heap v24488, v15535 + store.i64 little heap v24502, v15541 + store.i64 little heap v15547, v15550 + store.i32 little heap v24488, v15556 + store.i32 little heap v8, v1786 + store.i64 little heap v24502, v15570 + store.i64 little heap v15571, v15576 + store.i64 little heap v24502, v1786 + store.i64 little heap v24502, v15594 + store.i32 little heap v24488, v15600 + store.i64 little heap v24502, v15612 + store.i64 little heap v24502, v15630 + store.i64 little heap v24502, v15637 + store.i64 little heap v24502, v1786 + store.i64 little heap v24502, v15647 + store.i32 little heap v24488, v15685 + store.i32 little heap v24488, v15691 + store.i64 little heap v15704, v15709 + store.i32 little heap v24488, v15721 + store.i64 little heap v24502, v15739 + store.i32 little heap v24488, v15745 + store.i32 little heap v24488, v15751 + store.i64 little heap v15758, v15763 + store.i64 little heap v15764, v15769 + store.i32 little heap v24488, v15780 + store.i32 little heap v24488, v15786 + store.i64 little heap v24502, v15792 + store.i64 little heap v24502, v15798 + store.i64 little heap v15799, v15804 + store.i32 little heap v24488, v17 + store.i64 little heap v15824, v1786 + store.i64 little heap v15833, v15838 + store.i64 little heap v15853, v17 + store.i32 little heap v24488, v15867 + brif.i32 v24477, block2, block83 + +block83: + call fn0(v0, v0, v24564) + trap user1 + +block42: + trap user11 + +block41: + trap user11 + +block40: + trap user11 + +block39: + trap user11 + +block38: + trap user11 + +block37: + trap user11 + +block36: + trap user11 + +block35: + trap user11 + +block34: + trap user11 + +block33: + trap user11 + +block32: + trap user11 + +block31: + trap user11 + +block30: + trap user11 + +block29: + trap user11 + +block28: + trap user11 + +block27: + trap user11 + +block26: + trap user11 + +block25: + trap user11 + +block24: + trap user11 + +block23: + trap user11 + +block22: + trap user11 + +block21: + trap user11 + +block20: + trap user11 + +block19: + trap user11 + +block18: + trap user11 + +block17: + trap user11 + +block16: + trap user11 + +block15: + trap user11 + +block14: + trap user11 + +block13: + trap user11 + +block12: + trap user11 + +block11: + trap user11 + +block10: + trap user11 + +block9: + trap user11 + +block8: + trap user11 + +block7: + trap user11 + +block6: + trap user11 + +block5: + trap user11 + +block4: + trap user11 + +block3: + trap user11 + +block2: + call fn1(v0, v0, v24642, v24643) + trap user11 +} + +; VCode: +; addi sp,sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; ld t6,8(a0) +; ld t6,16(t6) +; addi t6,t6,16 +; trap_if stk_ovf##(sp ult t6) +; block0: +; mv t0,a0 +; li a0,0 +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; li a2,4 +; sd a2,0(a0) +; sd zero,0(a0) +; sd a2,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; li a1,1 +; sd a1,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd a2,0(a0) +; sd a1,0(a0) +; li a4,1 +; sw a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw a4,0(a0) +; sd a2,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; li a3,9 +; sd a3,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd a3,0(a0) +; li a3,13 +; sw a3,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd a2,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw a4,0(a0) +; sw zero,0(a0) +; sw a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd a1,0(a0) +; sd a2,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; li a3,8 +; sd a3,0(a0) +; sw a4,0(a0) +; sw zero,0(a0) +; sd a2,0(a0) +; sd a3,0(a0) +; sw a4,0(a0) +; sd a1,0(a0) +; sw a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw a4,0(a0) +; sd a1,0(a0) +; sd a1,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd a2,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sw a4,0(a0) +; sw a4,0(a0) +; sd zero,0(a0) +; sw a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd a1,0(a0) +; sd zero,0(a0) +; sd a3,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; li a5,3 +; sw a5,0(a0) +; sd a1,0(a0) +; sb a4,0(a0) +; sw zero,0(a0) +; sd a2,0(a0) +; sd zero,0(a0) +; sw a4,0(a0) +; sw zero,0(a0) +; sw a4,0(a0) +; sw zero,0(a0) +; sd a3,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw a4,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; sd a2,0(a0) +; sw a4,0(a0) +; sd a1,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; li a5,8 +; sw a5,0(a0) +; sw zero,0(a0) +; sd a3,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; sd a1,0(a0) +; sw a4,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw a5,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd a1,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd a1,0(a0) +; sd a3,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd a2,0(a0) +; sd a2,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd a1,0(a0) +; j label1 +; block1: +; lui a0,435789 +; addi a0,a0,3 +; trap_if user11##(a0 ne zero) +; j label2 +; block2: +; lui a0,154029 +; addi a0,a0,-1574 +; trap_if user11##(a0 ne zero) +; j label3 +; block3: +; j label4 +; block4: +; j label5 +; block5: +; li a0,1 +; trap_if user11##(a0 ne zero) +; j label6 +; block6: +; j label7 +; block7: +; j label8 +; block8: +; j label9 +; block9: +; j label10 +; block10: +; j label11 +; block11: +; j label12 +; block12: +; j label13 +; block13: +; j label14 +; block14: +; j label15 +; block15: +; j label16 +; block16: +; j label17 +; block17: +; lui a0,509913 +; addi a1,a0,-193 +; trap_if user11##(a1 ne zero) +; j label18 +; block18: +; j label19 +; block19: +; j label20 +; block20: +; lui a3,373234 +; addi a5,a3,1763 +; trap_if user11##(a5 ne zero) +; j label21 +; block21: +; j label22 +; block22: +; j label23 +; block23: +; j label24 +; block24: +; j label25 +; block25: +; j label26 +; block26: +; lui a0,456913 +; addi a0,a0,-324 +; trap_if user11##(a0 ne zero) +; j label27 +; block27: +; j label28 +; block28: +; lui a0,296162 +; addi a0,a0,-1144 +; trap_if user11##(a0 ne zero) +; j label29 +; block29: +; j label30 +; block30: +; j label31 +; block31: +; j label32 +; block32: +; lui a0,69861 +; addi a0,a0,-1634 +; trap_if user11##(a0 ne zero) +; j label33 +; block33: +; j label34 +; block34: +; j label35 +; block35: +; lui a0,466898 +; addi a0,a0,1094 +; trap_if user11##(a0 ne zero) +; j label36 +; block36: +; j label37 +; block37: +; j label38 +; block38: +; lui a0,-515203 +; addi a0,a0,1463 +; trap_if user11##(a0 ne zero) +; j label39 +; block39: +; j label40 +; block40: +; ld a1,[const(253)] +; li a0,0 +; sd a1,0(a0) +; ld a1,[const(252)] +; sd a1,0(a0) +; ld a1,[const(251)] +; sd a1,0(a0) +; ld a1,[const(250)] +; sd a1,0(a0) +; ld a1,[const(249)] +; sd a1,0(a0) +; ld a1,[const(248)] +; sd a1,0(a0) +; ld a1,[const(247)] +; sd zero,0(a1) +; ld a1,[const(246)] +; sd a1,0(a0) +; ld a1,[const(245)] +; sd a1,0(a0) +; ld a1,[const(244)] +; sd a1,0(a0) +; ld a1,[const(243)] +; sd a1,0(a0) +; ld a1,[const(242)] +; sd a1,0(a0) +; li a2,1 +; ld a1,[const(241)] +; sd a2,0(a1) +; ld a1,[const(240)] +; sd a1,0(a0) +; ld a1,[const(239)] +; sd a1,0(a0) +; ld a1,[const(238)] +; sd a1,0(a0) +; ld a1,[const(237)] +; sd a1,0(a0) +; ld a1,[const(236)] +; sd zero,0(a1) +; ld a1,[const(235)] +; sd a1,0(a0) +; ld a1,[const(234)] +; sd a1,0(a0) +; ld a1,[const(233)] +; sd a1,0(a0) +; ld a1,[const(232)] +; sd a1,0(a0) +; ld a1,[const(231)] +; sd a1,0(a0) +; ld a1,[const(230)] +; sd a1,0(a0) +; ld a1,[const(229)] +; sd zero,0(a1) +; ld a1,[const(228)] +; sd zero,0(a1) +; ld a1,[const(227)] +; sd a1,0(a0) +; ld a1,[const(226)] +; sd a2,0(a1) +; ld a1,[const(225)] +; sd a1,0(a0) +; ld a1,[const(224)] +; sd a1,0(a0) +; ld a1,[const(223)] +; sd a1,0(a0) +; ld a1,[const(222)] +; sd zero,0(a1) +; ld a1,[const(221)] +; sd a1,0(a0) +; ld a1,[const(220)] +; sd a1,0(a0) +; ld a1,[const(219)] +; sd a1,0(a0) +; ld a3,[const(218)] +; sd a3,0(a0) +; ld a3,[const(217)] +; sd a3,0(a0) +; ld a4,[const(216)] +; sd a4,0(a0) +; ld a5,[const(215)] +; sd a5,0(a0) +; ld a1,[const(214)] +; sd a1,0(a0) +; ld a1,[const(213)] +; sd a1,0(a0) +; li a1,1 +; ld a3,[const(212)] +; sw a1,0(a3) +; ld a3,[const(211)] +; sd a3,0(a0) +; ld a3,[const(210)] +; sd a3,0(a0) +; ld a3,[const(209)] +; sd zero,0(a3) +; ld a3,[const(208)] +; sd a3,0(a0) +; ld a3,[const(207)] +; sd a3,0(a0) +; ld a3,[const(206)] +; sd a3,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; li a3,0 +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(205)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(204)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(203)] +; sd a4,0(a0) +; ld a4,[const(202)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(201)] +; ld a5,[const(200)] +; sd a4,0(a5) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(199)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(198)] +; sd zero,0(a4) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(197)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(196)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(195)] +; sd a4,0(a0) +; ld a4,[const(194)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(193)] +; sd zero,0(a4) +; ld a4,[const(192)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(191)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(190)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(189)] +; sd a4,0(a0) +; ld a4,[const(188)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(187)] +; sd a4,0(a0) +; ld a4,[const(186)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(185)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(184)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(183)] +; sd a4,0(a0) +; ld a4,[const(182)] +; sd a4,0(a0) +; ld a4,[const(181)] +; ld a4,0(a4) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(180)] +; sd a4,0(a0) +; ld a4,[const(179)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(178)] +; sd a4,0(a0) +; ld a4,[const(177)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(176)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(175)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(174)] +; sd a4,0(a0) +; sd zero,0(a0) +; ld a4,[const(173)] +; sd zero,0(a4) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(172)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(171)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(170)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(169)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(168)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(167)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(166)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(165)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(164)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(163)] +; sd a4,0(a0) +; ld a4,[const(162)] +; sd a4,0(a0) +; sd zero,0(a0) +; ld a4,[const(161)] +; ld a4,0(a4) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(160)] +; sd a4,0(a0) +; ld a4,[const(159)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(158)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(157)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(156)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(155)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(154)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(153)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(152)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(151)] +; sd a4,0(a0) +; ld a4,[const(150)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(149)] +; sd a4,0(a0) +; ld a4,[const(148)] +; sd zero,0(a4) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(147)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(146)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(145)] +; sd a4,0(a0) +; ld a4,[const(144)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(143)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(142)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(141)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(140)] +; sd a4,0(a0) +; ld a4,[const(139)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(138)] +; sd a4,0(a0) +; ld a4,[const(137)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(136)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(135)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(134)] +; sd a4,0(a0) +; ld a4,[const(133)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a5,[const(132)] +; sd a5,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(131)] +; sd a4,0(a0) +; ld a4,[const(130)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(129)] +; sd a4,0(a0) +; ld a4,[const(128)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(127)] +; sd a4,0(a0) +; ld a4,[const(126)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(125)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(124)] +; sd a4,0(a0) +; ld a4,[const(123)] +; sd a4,0(a0) +; ld a4,[const(122)] +; sd a4,0(a0) +; ld a4,[const(121)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(120)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(119)] +; sd a4,0(a0) +; ld a4,[const(118)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(117)] +; sd a4,0(a0) +; ld a4,[const(116)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(115)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(114)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(113)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(112)] +; sd a4,0(a0) +; ld a4,[const(111)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(110)] +; sd a4,0(a0) +; ld a4,[const(109)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(108)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(107)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(106)] +; sd a2,0(a4) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(105)] +; sd zero,0(a4) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(104)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(103)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(102)] +; sd zero,0(a4) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(101)] +; sd a4,0(a0) +; ld a4,[const(100)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(99)] +; sd a2,0(a4) +; ld a4,[const(98)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(97)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(96)] +; sd a4,0(a0) +; ld a4,[const(95)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(94)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a5,[const(93)] +; sd a2,0(a5) +; ld a4,[const(92)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(91)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; lui a4,309522 +; addi a4,a4,-111 +; ld a5,[const(90)] +; sw a4,0(a5) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(89)] +; sd a4,0(a0) +; ld a4,[const(88)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(87)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(86)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(85)] +; sd a4,0(a0) +; ld a4,[const(84)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(83)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(82)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(81)] +; sd a4,0(a0) +; ld a4,[const(80)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(79)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(78)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(77)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(76)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(75)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(74)] +; sd a4,0(a0) +; ld a4,[const(73)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(72)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(71)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(70)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(69)] +; sd a4,0(a0) +; sd zero,0(a0) +; ld a4,[const(68)] +; sd a4,0(a0) +; ld a4,[const(67)] +; lw a4,0(a4) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(66)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(65)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(64)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(63)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(62)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(61)] +; sd a4,0(a0) +; ld a4,[const(60)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(59)] +; sd a4,0(a0) +; sd zero,0(a0) +; ld a4,[const(58)] +; sw zero,0(a4) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(57)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(56)] +; sd a4,0(a0) +; ld a4,[const(55)] +; sw a1,0(a4) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(54)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(53)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(52)] +; sd a2,0(a4) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(51)] +; sd a4,0(a0) +; ld a4,[const(50)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(49)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sb a3,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(48)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(47)] +; sd a4,0(a0) +; ld a4,[const(46)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(45)] +; sd a4,0(a0) +; ld a4,[const(44)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(43)] +; sd a4,0(a0) +; ld a4,[const(42)] +; sd a2,0(a4) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(41)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(40)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(39)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sb a3,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(38)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(37)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(36)] +; sd a4,0(a0) +; ld a4,[const(35)] +; sd a2,0(a4) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; lw a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(34)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(33)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(32)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(31)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(30)] +; sd a2,0(a4) +; ld a4,[const(29)] +; sd zero,0(a4) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sb a3,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(28)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(27)] +; sd a4,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sb a3,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sb a3,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(26)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(25)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(24)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sb a3,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(23)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(22)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(21)] +; sd a4,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(20)] +; sd a4,0(a0) +; ld a4,[const(19)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(18)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(17)] +; sd zero,0(a4) +; sb a3,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a4,[const(16)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(15)] +; sd a4,0(a0) +; ld a4,[const(14)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(13)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sb a1,0(a0) +; sw zero,0(a0) +; ld a4,[const(12)] +; sd a4,0(a0) +; ld a4,[const(11)] +; sd a4,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a4,[const(10)] +; sd a4,0(a0) +; sw zero,0(a0) +; ld a4,[const(9)] +; sd a4,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sb a3,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a3,[const(8)] +; sd a2,0(a3) +; ld a2,[const(7)] +; sd a2,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a2,[const(6)] +; sd a2,0(a0) +; ld a2,[const(5)] +; lui a3,4 +; addi a3,a3,-1824 +; sd a2,0(a3) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; ld a2,[const(4)] +; lui a3,4 +; addi a3,a3,-1700 +; sd a2,0(a3) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a3,[const(3)] +; lui a2,4 +; addi a4,a2,-1520 +; sd a3,0(a4) +; sw zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sw zero,0(a0) +; ld a2,[const(2)] +; lui a3,4 +; addi a3,a3,-1592 +; sd a2,0(a3) +; ld a2,[const(1)] +; lui a3,4 +; addi a3,a3,-1600 +; sd a2,0(a3) +; sw zero,0(a0) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; ld a2,[const(0)] +; lui a3,4 +; addi a3,a3,-1436 +; sd a2,0(a3) +; sw zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sd zero,0(a0) +; sw zero,0(a0) +; sext.w a0,a1 +; bne a0,zero,taken(label42),not_taken(label41) +; block41: +; lui a0,1 +; addi a2,a0,-1080 +; mv a1,t0 +; mv a0,a1 +; call userextname0 +; udf##trap_code=user1 +; block42: +; mv a1,t0 +; li a2,1 +; li a3,2 +; mv a0,a1 +; call userextname1 +; udf##trap_code=user11 +; +; Disassembled: +; block0: ; offset 0x0 +; c.addi16sp sp, -0x10 +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) +; c.mv s0, sp +; ld t6, 8(a0) +; ld t6, 0x10(t6) +; c.addi t6, 0x10 +; bgeu sp, t6, 6 +; c.unimp ; trap: stk_ovf +; block1: ; offset 0x18 +; c.mv t0, a0 +; c.li a0, 0 +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.li a2, 4 +; c.sd a2, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.li a1, 1 +; c.sd a1, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.li a4, 1 +; c.sw a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.li a3, 9 +; c.sd a3, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a3, 0(a0) ; trap: heap_oob +; c.li a3, 0xd +; c.sw a3, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.li a3, 8 +; c.sd a3, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; c.sd a3, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sd a3, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.li a5, 3 +; c.sw a5, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sb a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a3, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.li a5, 8 +; c.sw a5, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a3, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.sw a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sw a5, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; c.sd a3, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; c.sd a2, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.sd a1, 0(a0) ; trap: heap_oob +; block2: ; offset 0x576 +; lui a0, 0x6a64d +; c.addi a0, 3 +; beqz a0, 6 +; c.unimp ; trap: user11 +; block3: ; offset 0x582 +; lui a0, 0x259ad +; addi a0, a0, -0x626 +; beqz a0, 6 +; c.unimp ; trap: user11 +; block4: ; offset 0x590 +; c.li a0, 1 +; beqz a0, 6 +; c.unimp ; trap: user11 +; block5: ; offset 0x598 +; lui a0, 0x7c7d9 +; addi a1, a0, -0xc1 +; beqz a1, 6 +; c.unimp ; trap: user11 +; block6: ; offset 0x5a6 +; lui a3, 0x5b1f2 +; addi a5, a3, 0x6e3 +; beqz a5, 6 +; c.unimp ; trap: user11 +; block7: ; offset 0x5b4 +; lui a0, 0x6f8d1 +; addi a0, a0, -0x144 +; beqz a0, 6 +; c.unimp ; trap: user11 +; block8: ; offset 0x5c2 +; lui a0, 0x484e2 +; addi a0, a0, -0x478 +; beqz a0, 6 +; c.unimp ; trap: user11 +; block9: ; offset 0x5d0 +; lui a0, 0x110e5 +; addi a0, a0, -0x662 +; beqz a0, 6 +; c.unimp ; trap: user11 +; block10: ; offset 0x5de +; lui a0, 0x71fd2 +; addi a0, a0, 0x446 +; beqz a0, 6 +; c.unimp ; trap: user11 +; block11: ; offset 0x5ec +; lui a0, 0x8237d +; addi a0, a0, 0x5b7 +; beqz a0, 6 +; c.unimp ; trap: user11 +; block12: ; offset 0x5fa +; c.j 2 +; auipc t6, 0 +; jalr zero, t6, 8 +; block13: ; offset 0x604 +; auipc a1, 1 +; ld a1, 0x704(a1) +; c.li a0, 0 +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x700(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6fe(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6fc(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6fa(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6f8(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6f6(a1) +; sd zero, 0(a1) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6f2(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6f0(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6ee(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6ec(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6ea(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; c.li a2, 1 +; auipc a1, 1 +; ld a1, 0x6e6(a1) +; c.sd a2, 0(a1) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6e4(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6e2(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6e0(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6de(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6dc(a1) +; sd zero, 0(a1) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6d8(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6d6(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6d4(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6d2(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6d0(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6ce(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6cc(a1) +; sd zero, 0(a1) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6c8(a1) +; sd zero, 0(a1) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6c4(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6c2(a1) +; c.sd a2, 0(a1) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6c0(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6be(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6bc(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6ba(a1) +; sd zero, 0(a1) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6b6(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6b4(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6b2(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a3, 1 +; ld a3, 0x6b0(a3) +; c.sd a3, 0(a0) ; trap: heap_oob +; auipc a3, 1 +; ld a3, 0x6ae(a3) +; c.sd a3, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x6ac(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a5, 1 +; ld a5, 0x6aa(a5) +; c.sd a5, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6a8(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; auipc a1, 1 +; ld a1, 0x6a6(a1) +; c.sd a1, 0(a0) ; trap: heap_oob +; c.li a1, 1 +; auipc a3, 1 +; ld a3, 0x6a2(a3) +; c.sw a1, 0(a3) ; trap: heap_oob +; auipc a3, 1 +; ld a3, 0x6a0(a3) +; c.sd a3, 0(a0) ; trap: heap_oob +; auipc a3, 1 +; ld a3, 0x69e(a3) +; c.sd a3, 0(a0) ; trap: heap_oob +; auipc a3, 1 +; ld a3, 0x69c(a3) +; sd zero, 0(a3) ; trap: heap_oob +; auipc a3, 1 +; ld a3, 0x698(a3) +; c.sd a3, 0(a0) ; trap: heap_oob +; auipc a3, 1 +; ld a3, 0x696(a3) +; c.sd a3, 0(a0) ; trap: heap_oob +; auipc a3, 1 +; ld a3, 0x694(a3) +; c.sd a3, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.li a3, 0 +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x680(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x676(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x66c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x66a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x650(a4) +; auipc a5, 1 +; ld a5, 0x650(a5) +; c.sd a4, 0(a5) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x62a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x60c(a4) +; sd zero, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x5fc(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x5ee(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x5c8(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x5c6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x5c0(a4) +; sd zero, 0(a4) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x5bc(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x5b6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x58c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x57e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x57c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x576(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x574(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x556(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x550(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x546(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x544(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x542(a4) +; c.ld a4, 0(a4) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x520(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x51e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x514(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x512(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x508(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x4f6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x4d8(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x4d2(a4) +; sd zero, 0(a4) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x49e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x494(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x47a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x46c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x462(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x45c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x446(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x434(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x42a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x41c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x41a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x414(a4) +; c.ld a4, 0(a4) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x3fe(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x3fc(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x3f2(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x3dc(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x3ca(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x3a8(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x39a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x388(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x372(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x368(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x366(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x35c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x35a(a4) +; sd zero, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x34a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x31c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x312(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x310(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x2e2(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x2d8(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x2c6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x2a0(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x29e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x294(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x292(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x284(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x26a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x258(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x256(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a5, 1 +; ld a5, 0x24c(a5) +; c.sd a5, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x222(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x220(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x216(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x214(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x20a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x208(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x1ea(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x1dc(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x1da(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x1d8(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x1d6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x1b0(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x1a2(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x1a0(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x19a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x198(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x17a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x16c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x15a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x150(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x14e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x12c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x12a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x11c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0xde(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0xd8(a4) +; c.sd a2, 0(a4) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0xb6(a4) +; sd zero, 0(a4) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x76(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x48(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x36(a4) +; sd zero, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x22(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, 0x20(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -2(a4) +; c.sd a2, 0(a4) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -4(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0xe(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x14(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x16(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x3c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a5, 1 +; ld a5, -0x5e(a5) +; c.sd a2, 0(a5) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x60(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x96(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; lui a4, 0x4b912 +; addi a4, a4, -0x6f +; auipc a5, 1 +; ld a5, -0xb4(a5) +; c.sw a4, 0(a5) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0xbe(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0xc0(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0xce(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0xe8(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0xfe(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x100(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x122(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x18c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x1ae(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x1b0(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x1b6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x1c0(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x1d6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x1dc(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x1ea(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x200(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x202(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x20c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x21a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x220(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x262(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x268(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x26a(a4) +; c.lw a4, 0(a4) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x280(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x28a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x29c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x2b2(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x2c4(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x2ca(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x2cc(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x2da(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x2e0(a4) +; sw zero, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x314(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x31e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x320(a4) +; c.sw a1, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x32e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x348(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x356(a4) +; c.sd a2, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x360(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x362(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x36c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sb a3, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x3b2(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x3c4(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x3c6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x3d4(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x3d6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x418(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x41a(a4) +; c.sd a2, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x428(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x436(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x44c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sb a3, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x46e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x474(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x486(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x488(a4) +; c.sd a2, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; c.lw a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x4c4(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x4d6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x50c(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x51e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x54c(a4) +; c.sd a2, 0(a4) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x54e(a4) +; sd zero, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sb a3, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x58a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x5a4(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sb a3, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sb a3, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x622(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x630(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x63e(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sb a3, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x674(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x67a(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x680(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x6b6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x6b8(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x6c2(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x6d0(a4) +; sd zero, 0(a4) ; trap: heap_oob +; sb a3, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x6e0(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x6e6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x6e8(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x6f6(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sb a1, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x710(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x712(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x720(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a4, 1 +; ld a4, -0x726(a4) +; c.sd a4, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sb a3, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a3, 1 +; ld a3, -0x740(a3) +; c.sd a2, 0(a3) ; trap: heap_oob +; auipc a2, 1 +; ld a2, -0x742(a2) +; c.sd a2, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a2, 1 +; ld a2, -0x750(a2) +; c.sd a2, 0(a0) ; trap: heap_oob +; auipc a2, 1 +; ld a2, -0x752(a2) +; c.lui a3, 4 +; addi a3, a3, -0x720 +; c.sd a2, 0(a3) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a2, 1 +; ld a2, -0x772(a2) +; c.lui a3, 4 +; addi a3, a3, -0x6a4 +; c.sd a2, 0(a3) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a3, 1 +; ld a3, -0x7a2(a3) +; c.lui a2, 4 +; addi a4, a2, -0x5f0 +; c.sd a3, 0(a4) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; auipc a2, 1 +; ld a2, -0x7ba(a2) +; c.lui a3, 4 +; addi a3, a3, -0x638 +; c.sd a2, 0(a3) ; trap: heap_oob +; auipc a2, 1 +; ld a2, -0x7c2(a2) +; c.lui a3, 4 +; addi a3, a3, -0x640 +; c.sd a2, 0(a3) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; auipc a2, 1 +; ld a2, -0x7da(a2) +; c.lui a3, 4 +; addi a3, a3, -0x59c +; c.sd a2, 0(a3) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sd zero, 0(a0) ; trap: heap_oob +; sw zero, 0(a0) ; trap: heap_oob +; sext.w a0, a1 +; bnez a0, 6 +; c.j 0xa +; auipc t6, 1 +; jalr zero, t6, -0x7ec +; auipc t6, 0 +; jalr zero, t6, 0x7f8 +; c.fldsp fs5, 0x1e0(sp) +; .byte 0xc3, 0xd1 +; c.bnez a0, -4 +; c.sdsp t6, 0x198(sp) +; c.andi a1, 0xe +; c.addi s3, -0x15 +; .byte 0xa3, 0xe7 +; c.swsp t2, 0x68(sp) +; fnmsub.d ft6, ft9, fs9, fs0, rne +; .byte 0x62, 0x80 +; c.bnez a3, 0xaa +; c.bnez s1, 0x16 +; c.mv t6, s5 +; c.lwsp t1, 0xf4(sp) +; c.slli s2, 0x39 +; c.ld s0, 0x78(a2) +; .byte 0x9b, 0xb8 +; c.bnez a4, 0x2c +; c.j 0x3a2 +; c.lw a2, 0xc(a4) +; c.sd s0, 0x70(s0) +; c.slli a7, 1 +; c.bnez a0, -0xe8 +; c.lui s1, 0xffff8 +; c.sd a1, 0xb0(a0) +; c.unimp +; c.unimp +; c.j 0x4a2 +; .byte 0x2f, 0xc4 +; c.srli s1, 4 +; c.ldsp s2, 0x1c8(sp) +; c.slli s9, 0x15 +; c.lui s5, 0xffffa +; c.lwsp t0, 0xf8(sp) +; c.fsd fa5, 0x98(a1) +; c.lui t5, 0xffff5 +; c.li s11, 0x1c +; c.fld fa0, 0x18(a0) +; c.beqz a2, -0x90 +; c.fld fa1, 0xb0(a1) +; c.j 0x28 +; c.addi a0, -0x10 +; c.addi s7, -0x1b +; .byte 0xcb, 0x5d +; c.addi4spn s0, sp, 0x384 +; c.fldsp fs2, 0x1c0(sp) +; .byte 0xcf, 0xc6 +; c.xor a0, s1 +; c.j 0x6ac +; c.unimp +; c.unimp +; c.jr sp +; c.fsdsp ft1, 0x70(sp) +; c.addi4spn a3, sp, 0x114 +; c.bnez a0, -0x100 +; c.lw s0, 0x48(a2) +; .byte 0xaf, 0x72 +; .byte 0x69, 0x60 +; c.fldsp ft11, 0x78(sp) +; bge a1, s6, -0x786 +; c.ld a1, 8(a0) +; .byte 0x4b, 0x69 +; c.bnez s1, 0x22 +; .byte 0x6b, 0x42 +; c.li a5, 0xc +; c.ld s1, 0xa8(a2) +; .byte 0xd3, 0xf6 +; .byte 0x0f, 0xdb +; c.unimp +; c.unimp +; c.fldsp fa0, 0x98(sp) +; c.fsdsp fs10, 0x140(sp) +; c.beqz a4, 0x16 +; c.sd a5, 0x90(a0) +; .byte 0xcc, 0x9b +; c.mv a1, t5 +; .byte 0xdf, 0xe6 +; fmsub.d fa6, fs10, fs7, fs4, rdn +; .byte 0x1d, 0x40 +; c.sd a0, 0x40(s1) +; c.beqz a3, -0xbe +; c.fsd fa4, 0x80(a2) +; .byte 0x63, 0xbb +; c.addi4spn a1, sp, 0x394 +; .byte 0x8f, 0xf1 +; c.ldsp a2, 0x158(sp) +; c.j 0x6ec +; c.addi4spn a5, sp, 0x348 +; c.addiw a2, -0xb +; c.fldsp ft5, 0x118(sp) +; c.ldsp a6, 0x148(sp) +; .byte 0x0b, 0xf7 +; c.fld fa5, 0x20(s0) +; .byte 0x77, 0x7a +; c.fsd fa1, 0xa0(a2) +; c.unimp +; c.unimp +; c.lui a3, 0x13 +; lh zero, 0(ra) +; c.unimp +; c.sw a0, 0x7c(a5) +; .byte 0xff, 0xa0 +; c.andi a3, 8 +; c.ld s0, 8(a3) +; c.li t6, 9 +; c.swsp t1, 0xa4(sp) +; c.unimp +; c.unimp +; .byte 0xc7, 0x02 +; c.addiw a5, -0xd +; c.fsd fa0, 8(a4) +; c.swsp s0, 0x4c(sp) +; c.lui s0, 0xfffef +; c.fsd fa2, 0xf8(a3) +; c.lw a5, 0x6c(a3) +; c.addi4spn a5, sp, 0x1c4 +; c.addiw s6, 0xd +; c.slli t1, 0x2e +; c.beqz a2, 0x50 +; c.ldsp s3, 0x188(sp) +; c.addiw s1, -3 +; c.fsdsp fs8, 0x148(sp) +; c.unimp +; c.unimp +; c.sdsp s9, 0xe8(sp) +; c.li ra, 0xa +; c.sd a3, 0x58(a0) +; c.sw a4, 0x6c(a0) +; c.swsp s0, 0x6c(sp) +; c.addi s1, 6 +; c.mv a2, t4 +; c.slli s10, 2 +; c.bnez s0, 0xbc +; .byte 0xeb, 0x9c +; c.ldsp tp, 0x1c8(sp) +; fmadd.d ft0, fa4, fs6, ft1 +; c.swsp s2, 0x98(sp) +; c.sub a0, s0 +; c.beqz s0, 0x7c +; c.lw a1, 0x58(a1) +; .byte 0x9f, 0x96 +; c.sdsp s8, 0x90(sp) +; c.addi4spn a1, sp, 0x2cc +; bge s4, a5, -0x9d0 +; c.srli a3, 0x17 +; c.beqz a5, -0xe +; c.addiw a7, 0xc +; c.fsdsp fa1, 0x1b0(sp) +; c.mv s11, a6 +; fnmsub.d ft1, ft8, ft6, ft11 +; c.ld a0, 0x80(a4) +; c.lw a4, 0x50(a3) +; .byte 0xb3, 0xa7 +; c.addi s5, -0x1d +; .byte 0x77, 0x3f +; c.addi s2, -0x1f +; c.mv gp, a4 +; c.lui s7, 0xc +; c.bnez s0, 0xca +; c.unimp +; c.unimp +; c.lui t2, 0xffff0 +; c.beqz a5, 0xce +; c.fldsp fa1, 0xa8(sp) +; c.ld a1, 0x28(a1) +; c.beqz s0, 0x7e +; c.j 0x646 +; c.slli s7, 0xf +; .byte 0xdf, 0x06 +; c.bnez s1, -0x8a +; .byte 0x5f, 0xe2 +; c.unimp +; c.unimp +; andi s7, t4, -0x73b +; c.and s1, a0 +; .byte 0xe7, 0xdc +; c.swsp t2, 0xd0(sp) +; c.fsdsp fa5, 0x30(sp) +; c.li t0, 8 +; c.sw a3, 0x24(a1) +; fmadd.d fs0, fs7, fs11, fs6, rne +; c.lwsp a7, 0x64(sp) +; c.bnez a1, 0xb2 +; c.li t5, -0x11 +; c.li tp, 0x13 +; c.sw s0, 0x14(s1) +; c.sd a4, 0x70(a3) +; .byte 0x53, 0x6e +; .byte 0x32, 0x50 +; auipc s6, 0x751cd +; c.slli t4, 0x2a +; .byte 0xdb, 0x19 +; c.addi4spn a1, sp, 0x2a8 +; c.sd s0, 0x78(a4) +; c.sw a2, 0x54(a1) +; c.addi4spn a0, sp, 0x2b8 +; .byte 0xd3, 0x44 +; c.beqz a5, 0xbc +; c.lwsp ra, 0xdc(sp) +; c.beqz a4, -0xc4 +; bltu t3, t1, 0x970 +; c.beqz a2, -0x88 +; c.sdsp t0, 0x58(sp) +; c.unimp +; c.unimp +; c.sdsp a1, 0x48(sp) +; c.addiw t5, -0x10 +; c.addi4spn a1, sp, 0x88 +; c.fsdsp fa1, 0x90(sp) +; andi s2, s2, -0x1cd +; c.unimp +; c.unimp +; c.bnez a3, 0xb2 +; c.ld a1, 0(s1) +; c.beqz a1, 0xea +; c.beqz a5, 0xd4 +; c.sdsp a2, 0x68(sp) +; c.addi4spn a0, sp, 0x3e0 +; c.addiw a5, 0xd +; c.sd a1, 0xd0(s1) +; c.lui s1, 8 +; c.sdsp t4, 0xa8(sp) +; c.ld s0, 0xe8(s0) +; .byte 0x67, 0x35 +; c.fsdsp ft10, 0x130(sp) +; c.ld a4, 0x28(a5) +; c.sw a5, 0x50(a0) +; c.sd a0, 0x18(a2) +; .byte 0x5b, 0x46 +; xori t3, ra, 0 +; c.unimp +; c.sw a3, 0x64(a2) +; c.lw s0, 0x78(a1) +; c.bnez a1, 0x72 +; c.lwsp ra, 0x7c(sp) +; ld a6, 0x2ac(a7) +; c.ld a5, 0x20(a2) +; c.fld fs0, 0xb8(s0) +; c.slli s5, 0x1b +; c.ld a1, 0xe8(a4) +; c.ld a2, 0x70(a4) +; .byte 0x5b, 0x19 +; jal s10, 0x6b48a +; .byte 0x78, 0x96 +; .byte 0xc3, 0x26 +; c.sdsp a6, 0x1c8(sp) +; .byte 0x8c, 0x9d +; fmsub.d fs3, fa4, ft5, fs4, rne +; c.srai s0, 0x1d +; c.slli s9, 8 +; .byte 0x04, 0x94 +; .byte 0xab, 0x36 +; .byte 0x4f, 0xa4 +; c.fldsp fs9, 0x158(sp) +; c.fld fs1, 0x98(a5) +; c.swsp s3, 0x90(sp) +; c.addi s6, 6 +; c.fldsp fs3, 0x58(sp) +; c.j -0x648 +; c.sdsp s9, 0x160(sp) +; c.fsd fa1, 0x10(a5) +; addi t2, a4, 0x44b +; c.swsp t6, 0x5c(sp) +; .byte 0x33, 0xf7 +; c.lw a2, 0x28(a4) +; ori gp, s2, 0x376 +; c.fld fa3, 0xa0(s1) +; c.sd a4, 0xc8(s1) +; .byte 0xa7, 0x75 +; c.sd s1, 0xd0(a0) +; c.j -0x4be +; .byte 0x23, 0xc2 +; c.unimp +; c.unimp +; c.mv a4, t3 +; .byte 0x0f, 0x41 +; c.addi4spn a0, sp, 0x210 +; c.slli t1, 0x1b +; c.swsp s11, 0x44(sp) +; c.fsdsp fs0, 0x38(sp) +; c.fsdsp ft7, 0x88(sp) +; c.beqz s1, 0x5a +; c.beqz s0, -0x100 +; .byte 0xdf, 0xec +; c.lwsp t0, 0x70(sp) +; c.lui t6, 0xfffe6 +; c.fld fa1, 0x58(a0) +; c.lw a1, 0x24(a5) +; c.fldsp fa2, 0x60(sp) +; c.beqz a2, -0x36 +; c.fsd fs0, 0xf8(s1) +; c.sd s1, 0x40(s0) +; c.sub a5, a4 +; c.swsp a5, 0x78(sp) +; c.fsd fa4, 0x70(a1) +; c.ld a2, 0x60(s0) +; .byte 0xf0, 0x96 +; c.fsdsp fa0, 0x180(sp) +; c.fld fa4, 0x70(a0) +; c.swsp t0, 0x60(sp) +; c.j 0x44c +; c.addi4spn s1, sp, 0x3ac +; c.j -0x648 +; .byte 0x73, 0xca +; c.unimp +; c.unimp +; auipc t6, 0x4769 +; fnmadd.d fa5, ft1, fa6, fs9, rup +; fmsub.s ft4, fs5, fa0, fa6, rdn +; c.ld a1, 0x78(a4) +; c.beqz a1, 0x5c +; csrrw s0, 3434, s3 +; fnmsub.s ft4, ft9, ft5, ft8 +; c.beqz a1, 0xb6 +; .byte 0xbf, 0x7f +; c.fsdsp ft7, 0x180(sp) +; c.bnez a1, 0x3c +; c.slli s10, 0x39 +; c.fldsp ft8, 0x80(sp) +; c.slli t1, 0x3d +; c.bnez a0, -0xb4 +; c.ld s0, 0xb8(s0) +; c.addi4spn a5, sp, 0x3bc +; .byte 0x5f, 0x95 +; c.fldsp ft8, 0x1c8(sp) +; .byte 0x07, 0x00 +; c.unimp +; c.unimp +; .byte 0x00, 0x80 +; c.srai a3, 0x1f +; c.addiw s5, -0xf +; .byte 0xe7, 0xd9 +; c.sdsp t0, 0xc8(sp) +; c.fldsp fa7, 0x1d8(sp) +; c.fld fs1, 0x90(s0) +; csrrs s10, 3729, a1 +; c.lui t3, 0x11 +; c.li tp, 0x16 +; c.sw a4, 0x40(a3) +; c.bnez a3, -0x4a +; c.sw a0, 0x48(a5) +; c.lui t5, 0x1d +; c.lwsp s11, 0x58(sp) +; c.sdsp t0, 0x110(sp) +; c.addi t5, 9 +; c.andi s0, -0x16 +; c.unimp +; c.unimp +; c.lui a0, 0xffff3 +; c.beqz a2, 0x9a +; c.sd a5, 0x50(a1) +; c.fsdsp fa3, 0x1d8(sp) +; .byte 0x33, 0x10 +; c.mv s1, s3 +; c.addiw a7, -0xa +; c.j -0x71e +; c.fld fs1, 0x60(a0) +; c.swsp ra, 0x40(sp) +; .byte 0xab, 0x8f +; c.sd a3, 0x20(s1) +; .byte 0x0f, 0x5a +; c.addiw a1, -0x1b +; .byte 0x69, 0x40 +; c.fld fa3, 0x98(a1) +; .byte 0x6c, 0x84 +; c.addi s11, -2 +; c.beqz s1, 0xb8 +; c.addi4spn a5, sp, 0x3bc +; c.fsd fa4, 0xc8(s0) +; .byte 0xcb, 0xd8 +; c.ldsp s2, 0x68(sp) +; c.fld fs0, 0x88(a5) +; c.ld a2, 0xb8(s0) +; .byte 0xff, 0xb4 +; c.beqz s0, 0xa0 +; fnmadd.d fs9, fa4, fs10, fa3, rne +; fsw ft9, -0x5f3(t6) +; c.addi a6, -0x17 +; c.ld a2, 0xd8(a1) +; c.li s11, -0x19 +; sb gp, 0x3d1(a7) +; .byte 0xfb, 0xc2 +; c.lui s10, 0x1a +; .byte 0x67, 0x2a +; .byte 0x7f, 0xd8 +; c.sd s0, 0x88(a3) +; c.fld fa5, 0x40(a5) +; c.sw a4, 0x3c(s0) +; c.fld fa1, 0x30(a1) +; c.fld fa2, 0xf0(a0) +; c.fsdsp ft3, 0x1f0(sp) +; c.mv s4, a6 +; xori s1, s7, -0x4d0 +; csrrc t3, ustatus, ra +; c.unimp +; c.addi4spn a0, sp, 0x11c +; c.add t4, s9 +; c.sw a5, 0(s1) +; c.fld fa4, 0x60(a2) +; .byte 0xac, 0x88 +; c.mv a7, a0 +; c.addiw a1, 0x1c +; c.addi t4, -0x1d +; c.beqz s1, -0xf4 +; c.slli gp, 0x29 +; c.fldsp fa6, 0x190(sp) +; c.addi t4, -0x18 +; .byte 0xd3, 0xbf +; .byte 0x8f, 0xc1 +; c.lui s6, 0xfffe5 +; jal s2, -0x20d7a +; fnmsub.d fs2, fs11, fs7, fa3, rmm +; c.swsp a7, 0x3c(sp) +; c.sd a1, 0x60(a4) +; .byte 0xc7, 0x21 +; c.swsp ra, 0x6c(sp) +; c.sd a4, 0x20(a5) +; .byte 0xbf, 0x97 +; .byte 0x67, 0x98 +; .byte 0x55, 0x9e +; fnmsub.d ft10, fs11, fa4, fs0, rmm +; c.lwsp s3, 0x40(sp) +; c.fsdsp ft5, 0x38(sp) +; .byte 0xc7, 0xd9 +; c.lui a2, 0xfffe5 +; c.ld s0, 0x80(s0) +; c.addiw s1, -6 +; c.sd a0, 0xd0(a2) +; c.lw a5, 0x44(a2) +; c.addi a1, -0x19 +; .byte 0x7f, 0xda +; c.addiw t5, 3 +; c.li ra, 1 +; c.add a1, s7 +; .byte 0xeb, 0x32 +; c.addi4spn a0, sp, 0x3a0 +; c.sd s1, 0x48(a0) +; c.slli a6, 0x1d +; c.srai a2, 0x19 +; c.addi s6, 0x16 +; c.addi a3, -0x14 +; c.lwsp a2, 0x4c(sp) +; c.fsdsp ft6, 0x18(sp) +; .byte 0x7b, 0x1f +; c.mv s9, a4 +; .byte 0x3f, 0x74 +; c.lui t1, 0xfffea +; c.beqz s1, 0xdc +; c.li tp, -6 +; c.addi4spn a3, sp, 0x1f8 +; c.sdsp s6, 0x90(sp) +; c.addi gp, 0x1b +; c.li a4, 0x1b +; c.sd a2, 8(a5) +; c.ld s0, 0x20(a3) +; c.addi t4, -0x10 +; c.ld s1, 0x30(a5) +; c.li s3, -0x10 +; c.add s8, a0 +; c.fld fa0, 0x38(a4) +; c.fld fs0, 0x30(a4) +; .byte 0xd7, 0x5e +; .byte 0xd7, 0x7b +; c.bnez a3, 0x52 +; c.sdsp s3, 0x190(sp) +; c.j 0x622 +; c.andi a1, 0xa +; flw fa7, -0x6ff(tp) +; c.fldsp fa4, 0x1a0(sp) +; c.lw a1, 0x58(a2) +; c.mv s10, a2 +; c.ld s0, 0x18(a0) +; bge s4, s0, 0x6ca +; .byte 0x4f, 0x3b +; c.swsp s0, 0x1c(sp) +; .byte 0xd7, 0x0b +; c.fldsp fs5, 0x120(sp) +; c.li s8, 0x1d +; c.fsd fa1, 0xb0(a4) +; c.mv s9, s0 +; c.beqz s1, 0x44 +; csrrw s3, 1036, t3 +; c.sd a2, 0x88(a2) +; .byte 0x4f, 0xdc +; c.fld fs0, 0x18(s0) +; c.bnez a4, -0xa8 +; sh sp, 0xc8(a5) +; c.li s7, 3 +; c.ldsp s7, 0x48(sp) +; c.fsdsp fs9, 0xe0(sp) +; .byte 0x8b, 0x5a +; .byte 0x83, 0xf8 +; .byte 0x0f, 0x6a +; xori t0, t6, 0x273 +; c.fsdsp fa7, 0x1a0(sp) +; c.sw s1, 0x28(a3) +; c.bnez a3, -0xce +; c.li s7, 0xb +; c.fld fa1, 0xa0(a5) +; c.lwsp s5, 0x98(sp) +; fmin.d fs7, ft6, ft0 +; .byte 0xeb, 0x2f +; c.sw s0, 0x4c(a1) +; c.ldsp t6, 0x28(sp) +; .byte 0x1f, 0xdb +; c.fld fa0, 0xf0(a0) +; .byte 0x5f, 0x0e +; .byte 0xd7, 0x00 +; c.fld fa0, 0xa0(a1) +; c.fldsp ft2, 0xe8(sp) +; c.xor a5, a3 +; c.lwsp t4, 0xd0(sp) +; .byte 0x4b, 0x75 +; c.beqz s0, 0xce +; c.lui t1, 0x1b +; c.bnez a3, 0x72 +; c.addi s3, -0xf +; .byte 0xe5, 0x9c +; c.sdsp t2, 0xe8(sp) +; c.lui t0, 0xfffea +; c.xor a3, s0 +; c.sd a2, 0x58(a4) +; lui a1, 0xe0c6 +; c.sd s1, 0x48(a3) +; c.ld a4, 0x58(a0) +; .byte 0xcb, 0xc5 +; c.ld a4, 0x38(a2) +; .byte 0x93, 0xd5 +; csrrs t1, 2265, s10 +; c.li tp, -9 +; slti t3, a7, 0x57c +; .byte 0x13, 0x1d +; c.fsdsp ft0, 0x1d8(sp) +; c.fsdsp fs11, 0xe0(sp) +; csrrw s0, 1762, s0 +; c.sw a4, 0x34(s0) +; c.or a5, s0 +; c.sd a4, 0xc8(a1) +; c.beqz a2, -0xe0 +; fnmadd.d fa1, fs5, fs3, ft2 +; .byte 0x23, 0xf3 +; c.sw s0, 8(a0) +; c.lwsp tp, 0xe8(sp) +; c.j -0x662 +; c.li s0, -0x15 +; c.beqz a1, -0x82 +; c.beqz a5, 4 +; c.unimp +; c.unimp +; c.addiw a0, -0x14 +; c.fsdsp fa6, 0x150(sp) +; c.unimp +; c.unimp +; .byte 0x10, 0x00 +; c.unimp +; c.unimp +; .byte 0x00, 0x80 +; c.ld s0, 0x28(a3) +; c.j -0x7de +; c.sw a2, 0x78(a5) +; .byte 0x19, 0x10 +; c.beqz s0, 0x5e +; c.sd a3, 0xb8(a2) +; c.unimp +; c.unimp +; .byte 0xf7, 0x1a +; .byte 0x3b, 0xf0 +; .byte 0xcb, 0x27 +; c.slli s1, 0x3f +; blt t4, t1, -0x9a6 +; c.addi a2, 0x19 +; c.addiw a3, -0xe +; .byte 0x0b, 0xc8 +; fnmadd.s ft2, ft1, ft0, ft0, rtz +; c.unimp +; .byte 0x3b, 0x78 +; fnmadd.d ft10, fs2, ft11, fa7, rup +; c.sd a0, 0x90(a2) +; c.swsp s4, 0xe8(sp) +; c.lw a5, 0x68(s0) +; .byte 0x23, 0x40 +; c.lw a4, 0x48(s0) +; csrrs ra, 2022, s1 +; .byte 0x0d, 0x20 +; c.sd a4, 0x28(s1) +; c.addi tp, 7 +; c.beqz a1, 0x58 +; c.j -0x608 +; c.fsd fa5, 0x80(a3) +; .byte 0xab, 0x89 +; .byte 0x9b, 0x53 +; sw zero, 0x42(s6) +; fmadd.d ft10, fs11, ft10, fs8 +; c.unimp +; c.unimp +; c.ld s1, 0x50(s0) +; c.sw a5, 0x5c(a3) +; .byte 0x90, 0x87 +; c.lw s0, 0(a1) +; .byte 0x54, 0x84 +; c.sdsp s8, 0x150(sp) +; c.lw a4, 0x24(s1) +; c.sd a1, 0xa0(s1) +; .byte 0xdf, 0xbd +; c.beqz s1, -0x54 +; c.unimp +; c.unimp +; c.lui ra, 0xb +; c.lw a0, 0x3c(a2) +; c.beqz s1, -0x86 +; c.fldsp ft11, 0x160(sp) +; c.bnez s1, 0x40 +; c.add t6, s10 +; .byte 0x77, 0x2f +; c.lwsp t1, 0xe8(sp) +; c.sw a4, 0x38(a5) +; .byte 0xb4, 0x92 +; .byte 0xb3, 0x4d +; lh zero, -0x6dd(s6) +; c.sw a1, 0x58(s1) +; c.addi4spn a5, sp, 0x328 +; c.lwsp sp, 0xd4(sp) +; .byte 0xcf, 0x0e +; .byte 0x41, 0x9e +; c.lwsp s9, 0xdc(sp) +; c.swsp gp, 0xcc(sp) +; .byte 0x4f, 0x33 +; c.sd a2, 0x28(a3) +; c.slli a7, 0x3c +; c.slli s6, 8 +; c.lwsp s11, 0x28(sp) +; c.sd a2, 0xf8(a5) +; .byte 0x5b, 0xfd +; .byte 0x7f, 0x29 +; c.sd a1, 0xc0(a1) +; .byte 0x5b, 0xf9 +; c.ldsp ra, 0x118(sp) +; c.sdsp t3, 0x78(sp) +; c.addiw t5, -0xd +; c.addi4spn a1, sp, 0x33c +; c.addi4spn a1, sp, 0x3ac +; c.sd a3, 0x38(s0) +; .byte 0x3b, 0x56 +; c.li s8, 6 +; c.li s6, -3 +; .byte 0x2b, 0x21 +; c.addi s9, 1 +; jal s2, -0x9c9e8 +; c.addi4spn a1, sp, 4 +; c.j -0x322 +; c.beqz a1, -0xd6 +; c.addi s7, -0x12 +; .byte 0x58, 0x8f +; c.fsdsp fa1, 0xd0(sp) +; .byte 0xeb, 0x83 +; c.addi t0, 7 +; c.fsdsp ft2, 0x68(sp) +; .byte 0xdc, 0x9b +; .byte 0x90, 0x99 +; c.lw s1, 0x40(a2) +; .byte 0x43, 0xef +; c.ldsp s9, 0x1e8(sp) +; c.sw s0, 0x44(a2) +; c.fld fa4, 0x58(a5) +; c.addiw s2, 0x15 +; c.swsp s0, 0x44(sp) +; c.sdsp t1, 0x120(sp) +; c.sdsp a3, 0xb0(sp) +; c.lw a3, 0x1c(a2) +; c.ldsp t4, 0x150(sp) +; c.sd a2, 8(s1) +; .byte 0x8f, 0x13 +; .byte 0xcb, 0xe4 +; c.jalr sp +; c.slli a0, 0x28 +; .byte 0x12, 0x80 +; c.sd a4, 0x88(a3) +; c.sub a3, a5 +; c.fsdsp fa1, 0x158(sp) +; csrrs s7, 1905, a3 +; c.fld fa2, 0x10(a0) +; c.fsdsp fs8, 0x98(sp) +; c.ld a0, 0x60(a1) +; c.ld a5, 0x88(a5) +; c.j -0x38c +; .byte 0xcb, 0xe8 +; c.andi a1, 0x16 +; c.ld a4, 0x90(a0) +; c.sw a1, 0x68(a3) +; .byte 0xff, 0x35 +; .byte 0x27, 0xc3 +; .byte 0x4b, 0xd1 +; .byte 0x0f, 0x77 +; c.sd a4, 0xb8(a1) +; c.unimp +; c.unimp +; c.addiw t6, -0xb +; lb a7, 0x579(s1) +; andi s7, t4, -0x5d8 +; lui t2, 0x6bc1f +; c.lui s9, 0x10 +; c.sw a4, 0x48(a5) +; c.lw a1, 0x2c(a5) +; c.or s0, a0 +; c.addi4spn s1, sp, 0x240 +; c.li a5, 0x1a +; c.fldsp fs11, 0x80(sp) +; c.sw s0, 0x20(s1) +; beq s2, a4, 0xde0 +; .byte 0x2b, 0xb5 +; c.lw a4, 0x7c(s0) +; c.mv a7, a1 +; c.sd s0, 0xc0(s1) +; c.ld a4, 0x20(a3) +; c.bnez a1, 0xa4 +; c.slli a2, 0x39 +; c.li sp, -0xb +; c.mv sp, a0 +; c.fld fa0, 0xb8(a3) +; .byte 0xab, 0x2b +; .byte 0xf3, 0x0f +; .byte 0x0b, 0x03 +; c.addi4spn s1, sp, 0x8c +; .byte 0xe3, 0x31 +; c.addiw t1, 0xc +; c.sd a2, 0xf8(a0) +; c.unimp +; c.unimp +; c.addi t3, -0x18 +; c.addi sp, 9 +; c.ld s0, 0x60(a0) +; c.sdsp t6, 0x150(sp) +; .byte 0x8f, 0x9e +; fnmadd.s fs10, fs6, fs4, fs4, rmm +; .byte 0x33, 0xca +; c.lui s9, 3 +; c.j -0x58c +; c.unimp +; c.unimp +; .byte 0xa7, 0x9c +; .byte 0xc5, 0x9c +; c.lui t4, 0xffffa +; c.li a4, -0xc +; c.fsd fa0, 0x80(a0) +; c.lwsp a3, 0x24(sp) +; .byte 0xab, 0x2d +; jal s3, 0x76714 +; c.beqz s1, -0x4a +; c.unimp +; c.unimp +; .byte 0x2f, 0x49 +; c.slli ra, 0x3f +; c.slli gp, 0x3a +; .byte 0xaf, 0x39 +; c.fsd fa3, 0xf0(a0) +; .byte 0x45, 0x70 +; .byte 0xbb, 0x20 +; .byte 0x7b, 0xdf +; c.sw a4, 0x3c(s0) +; .byte 0xff, 0xae +; c.addi s10, 1 +; c.lw a1, 0x2c(a0) +; c.ld s1, 0x90(s0) +; c.sd a0, 0(a5) +; c.sd a0, 0xd0(a4) +; .byte 0x27, 0x8e +; c.sd a1, 0xe0(s1) +; .byte 0x87, 0x61 +; c.ld a5, 0x50(a3) +; .byte 0xa7, 0xf9 +; c.fsd fa1, 0xa8(a0) +; c.addi t1, 0x11 +; c.fsdsp fs0, 0x58(sp) +; .byte 0x4f, 0xc0 +; .byte 0x0f, 0x1f +; c.lw a1, 0x74(s0) +; c.bnez a4, 0xd2 +; auipc a5, 0xa597c +; c.beqz a2, -0xf0 +; c.lwsp s9, 0x80(sp) +; csrrw gp, 4044, t1 +; .byte 0x0f, 0x8a +; c.ld a1, 0x20(a0) +; c.fldsp fa0, 0xa0(sp) +; c.addiw s9, -0x20 +; .byte 0x67, 0xd5 +; c.unimp +; c.unimp +; .byte 0x20, 0x84 +; .byte 0xa7, 0x99 +; c.srli s0, 0x1a +; c.addi4spn a4, sp, 0x1d4 +; .byte 0xd7, 0xa9 +; .byte 0xab, 0x35 +; c.sdsp a4, 0x168(sp) +; .byte 0x67, 0xdf +; c.fsd fa2, 0x70(s0) +; c.li t6, 3 +; c.fld fs0, 0x38(a5) +; c.beqz s0, -0xd4 +; c.fsd fs0, 0xe0(a3) +; c.sdsp t3, 0x128(sp) +; c.sd s0, 0xd8(a2) +; c.swsp a6, 0x90(sp) +; c.bnez a0, -0xfc +; c.srli a4, 0x3c +; c.srai a5, 0x19 +; c.slli t4, 0x2d +; c.fsdsp fa3, 0x20(sp) +; c.li t4, -0xd +; c.sd a1, 0xb0(a4) +; c.fldsp fa4, 0xd0(sp) +; .byte 0x5f, 0x2a +; .byte 0x90, 0x86 +; c.unimp +; c.unimp +; auipc tp, 0x22bce +; c.fsd fa4, 0(s1) +; c.lwsp a0, 0x40(sp) +; c.addi t1, -0x1e +; c.lwsp a3, 0x1c(sp) +; c.srli s1, 0x1b +; c.fsdsp fs10, 0x108(sp) +; c.fld fa4, 0xd8(a3) +; .byte 0x7b, 0x45 +; c.addi s4, 0x1e +; c.li a2, -0x13 +; c.fld fs0, 0x30(s0) +; c.fld fs0, 0x78(s1) +; .byte 0xcb, 0x63 +; c.sdsp t1, 0(sp) +; c.addi t5, -0xa +; c.beqz a3, -0x6c +; c.unimp +; c.unimp +; .byte 0x1f, 0x7b +; .byte 0x60, 0x85 +; c.unimp +; c.unimp +; .byte 0xd7, 0xc0 +; c.sdsp t5, 0x1e0(sp) +; c.fldsp fa1, 0(sp) +; c.sdsp t4, 8(sp) +; lui s10, 0xe1bf6 +; c.fld fa2, 0x50(a4) +; c.swsp s9, 0(sp) +; c.sdsp a7, 0x168(sp) +; c.fsd fs0, 0xd0(a5) +; c.lui t3, 0x11 +; c.lui s4, 0xfffeb +; c.sdsp a2, 0x80(sp) +; c.ldsp gp, 0xc8(sp) +; c.lw a5, 0x40(a2) +; c.slli s8, 0x25 +; jal a5, 0x1ceba +; .byte 0x67, 0x18 +; c.bnez a5, -0x74 +; .byte 0xd7, 0xfc +; c.slli sp, 0x30 +; .byte 0xaf, 0xd4 +; c.ldsp s7, 0xc0(sp) +; c.ld a0, 0x58(a4) +; .byte 0x6b, 0x07 +; csrrci t0, 565, 0x10 +; .byte 0x5f, 0x70 +; c.sw a5, 0x2c(a5) +; c.fld fs0, 0x10(a0) +; c.sd a5, 0x30(a3) +; c.addiw s6, 0x1f +; c.sdsp s2, 0x158(sp) +; c.ldsp t6, 0x180(sp) +; c.addiw tp, 0x1a +; auipc t6, 0xb2974 +; c.fld fa4, 0x98(a0) +; .byte 0x9f, 0xea +; .byte 0x0b, 0xdd +; c.lw a4, 0x4c(a0) +; c.fldsp ft3, 0x170(sp) +; c.li t0, -9 +; c.bnez s0, -0x6e +; .byte 0xab, 0x94 +; c.unimp +; c.unimp +; c.sdsp a7, 8(sp) +; c.ldsp s1, 0x160(sp) +; c.addi4spn s0, sp, 0x1dc +; c.addi4spn a5, sp, 0x358 +; c.sd a1, 8(a3) +; c.fld fa3, 0x68(a1) +; c.mv a1, t0 +; .byte 0x8f, 0x49 +; c.beqz a4, -0x8e +; c.lw s1, 0x10(a5) +; c.ldsp a7, 0x198(sp) +; .byte 0x47, 0xc1 +; .byte 0x07, 0x7c +; .byte 0xec, 0x85 +; .byte 0x2f, 0xb6 +; fdiv.s fs3, ft3, ft4 +; c.fsd fa5, 0xe8(s0) +; c.j -0x730 +; c.swsp a1, 0x8c(sp) +; c.fldsp fs7, 0x1f0(sp) +; c.bnez a5, -0x84 +; c.li t0, -0xe +; c.slli s3, 0x21 +; c.lwsp gp, 0x64(sp) +; c.fsd fs1, 8(a0) +; c.bnez a2, 0x2a +; c.fld fa5, 8(s0) +; c.lw a0, 0x20(a4) +; .byte 0x84, 0x8e +; c.lui s1, 0x19 +; c.ldsp a1, 0x98(sp) +; c.andi a0, -7 +; c.srai a4, 0x21 +; c.unimp +; c.unimp +; c.addi s3, 0xd +; c.ld a4, 0xb8(s1) +; c.addi s8, 6 +; c.addiw s7, 8 +; c.sd a3, 0xf0(a4) +; c.lui tp, 0xffffc +; bge t4, s3, 0x972 +; c.slli t4, 0x11 +; auipc zero, 0x1efb5 +; c.slli s7, 7 +; .byte 0x0b, 0x00 +; c.unimp +; c.unimp +; .byte 0x00, 0x80 +; .byte 0xd0, 0x93 +; c.swsp a7, 0xc(sp) +; c.ldsp a6, 0x50(sp) +; .byte 0x47, 0xdb +; c.lw a0, 0x3c(a1) +; lui t0, 0x5b4 +; c.beqz a1, 0xa2 +; c.add a1, s2 +; .byte 0x7c, 0x9b +; c.sw a5, 0x3c(a3) +; c.j -0x54e +; c.addi4spn s0, sp, 0x84 +; andi a7, s11, -0x6ab +; c.swsp t5, 0xb4(sp) +; block14: ; offset 0x24f8 +; c.lui a0, 1 +; addi a2, a0, -0x438 +; c.mv a1, t0 +; c.mv a0, a1 +; auipc ra, 0 ; reloc_external RiscvCallPlt u0:988 0 +; jalr ra +; c.unimp ; trap: user1 +; block15: ; offset 0x250c +; c.mv a1, t0 +; c.li a2, 1 +; c.li a3, 2 +; c.mv a0, a1 +; auipc ra, 0 ; reloc_external RiscvCallPlt u0:1 0 +; jalr ra +; c.unimp ; trap: user11 + diff --git a/cranelift/filetests/filetests/isa/riscv64/trapz-sign-extend-load32.clif b/cranelift/filetests/filetests/isa/riscv64/trapz-sign-extend-load32.clif new file mode 100644 index 000000000000..e61b7d481bda --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/trapz-sign-extend-load32.clif @@ -0,0 +1,33 @@ +test compile precise-output +target riscv64 + +function %f(i64, i64) { +block0(v0: i64, v1: i64): + v2 = load.i32 notrap aligned readonly v0 + v3 = ireduce.i32 v1 + v4 = icmp ult v2, v3 + trapz v4, user15 + return +} + +; VCode: +; block0: +; lw a0,0(a0) +; slli a0,a0,32 +; srli a0,a0,32 +; slli a1,a1,32 +; srli a1,a1,32 +; trap_if user15##(a0 uge a1) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; lw a0, 0(a0) +; slli a0, a0, 0x20 +; srli a0, a0, 0x20 +; slli a1, a1, 0x20 +; srli a1, a1, 0x20 +; bltu a0, a1, 8 +; .byte 0x00, 0x00, 0x00, 0x00 ; trap: user15 +; ret + diff --git a/cranelift/filetests/filetests/isa/x64/atomic-128.clif b/cranelift/filetests/filetests/isa/x64/atomic-128.clif index fd5796f5b966..261e19b76856 100644 --- a/cranelift/filetests/filetests/isa/x64/atomic-128.clif +++ b/cranelift/filetests/filetests/isa/x64/atomic-128.clif @@ -62,7 +62,7 @@ block0(v0: i128, v1: i64): ; movq %rsi, %rcx ; movq %rdi, %rbx ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%r8); 0(%r8) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%r8):8(%r8); 0(%r8):8(%r8) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -141,7 +141,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax Add %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax Add %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -183,7 +183,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax Sub %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax Sub %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -225,7 +225,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax And %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax And %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -267,7 +267,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax Nand %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax Nand %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -311,7 +311,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax Or %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax Or %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -353,7 +353,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax Xor %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax Xor %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -396,7 +396,7 @@ block0(v0: i64, v1: i128): ; block0: ; movq %rdx, %rcx ; movq %rsi, %rbx -; atomically { %rdx:%rax = 0(%rdi); 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -435,7 +435,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax Umin %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax Umin %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -480,7 +480,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax Umax %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax Umax %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -525,7 +525,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax Smin %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax Smin %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp @@ -570,7 +570,7 @@ block0(v0: i64, v1: i128): ; movq %rbx, (%rsp) ; block0: ; movq %rdx, %r8 -; atomically { %rdx:%rax = 0(%rdi); %rcx:%rbx = %rdx:%rax Smax %r8:%rsi; 0(%rdi) = %rcx:%rbx } +; atomically { %rdx:%rax = 0(%rdi):8(%rdi); %rcx:%rbx = %rdx:%rax Smax %r8:%rsi; 0(%rdi):8(%rdi) = %rcx:%rbx } ; movq (%rsp), %rbx ; addq $0x10, %rsp ; movq %rbp, %rsp diff --git a/cranelift/filetests/filetests/isa/x64/conditional-values.clif b/cranelift/filetests/filetests/isa/x64/conditional-values.clif index 231cbd8d90e5..24f048c1e2b9 100644 --- a/cranelift/filetests/filetests/isa/x64/conditional-values.clif +++ b/cranelift/filetests/filetests/isa/x64/conditional-values.clif @@ -377,7 +377,7 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; notq %rdi +; notl %edi ; movq %rdi, %rax ; shrl $0x1f, %eax ; movq %rbp, %rsp @@ -389,7 +389,7 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; notq %rdi +; notl %edi ; movq %rdi, %rax ; shrl $0x1f, %eax ; movq %rbp, %rsp @@ -437,7 +437,7 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; notq %rdi +; notl %edi ; movq %rdi, %rax ; shrl $0x1f, %eax ; movq %rbp, %rsp @@ -449,7 +449,7 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; notq %rdi +; notl %edi ; movq %rdi, %rax ; shrl $0x1f, %eax ; movq %rbp, %rsp diff --git a/cranelift/filetests/filetests/isa/x64/i128.clif b/cranelift/filetests/filetests/isa/x64/i128.clif index 51777c75c2cc..3b601d4d2420 100644 --- a/cranelift/filetests/filetests/isa/x64/i128.clif +++ b/cranelift/filetests/filetests/isa/x64/i128.clif @@ -2225,3 +2225,33 @@ block0(v0: i64, v1: i64): ; popq %rbp ; retq +function %load_huge_offset(i64) -> i64, i64 { +block0(v0: i64): + v4 = load.i128 v0+0x7fffffff + v5, v6 = isplit v4 + return v5, v6 +} + +; VCode: +; pushq %rbp +; movq %rsp, %rbp +; block0: +; leaq 0x7fffffff(%rdi), %rdx +; movq 0x7fffffff(%rdi), %rax +; movq 8(%rdx), %rdx +; movq %rbp, %rsp +; popq %rbp +; retq +; +; Disassembled: +; block0: ; offset 0x0 +; pushq %rbp +; movq %rsp, %rbp +; block1: ; offset 0x4 +; leaq 0x7fffffff(%rdi), %rdx ; trap: heap_oob +; movq 0x7fffffff(%rdi), %rax ; trap: heap_oob +; movq 8(%rdx), %rdx ; trap: heap_oob +; movq %rbp, %rsp +; popq %rbp +; retq + diff --git a/cranelift/filetests/filetests/pcc/fail/add.clif b/cranelift/filetests/filetests/pcc/fail/add.clif deleted file mode 100644 index 3e5e9313fe7d..000000000000 --- a/cranelift/filetests/filetests/pcc/fail/add.clif +++ /dev/null @@ -1,51 +0,0 @@ -test compile expect-fail -set enable_pcc=true -target aarch64 -;; disabled until PCC is migrated to new assembler: target x86_64 - -function %f0(i32, i32) -> i32 { -block0(v0 ! range(32, 0, 0x100): i32, v1 ! range(32, 0, 0x80): i32): - v2 ! range(32, 0, 0x17f) = iadd.i32 v0, v1 - return v2 -} - -function %f1(i32) -> i32 { -block0(v0 ! range(32, 0, 0x100): i32): - v1 ! range(32, 0, 1) = iconst.i32 1 - v2 ! range(32, 0, 0x100) = iadd.i32 v0, v1 - return v2 -} - -function %f3(i32) -> i64 { -block0(v0: i32): - v1 ! range(32, 0, 1) = iconst.i32 1 - v2 ! range(32, 0, 0xffff_fffe) = iadd.i32 v0, v1 - v3 ! range(64, 0, 0xffff_fffe) = uextend.i64 v2 - return v3 -} - -function %f3(i32) -> i64 { -block0(v0: i32): - v1 ! range(32, 0, 1) = iconst.i32 1 - v2 ! range(32, 0, 0xffff_ffff) = iadd.i32 v0, v1 - v3 ! range(64, 0, 0xffff_ffff) = uextend.i64 v2 - v4 ! range(64, 0, 0x1) = iconst.i64 1 - v5 ! range(64, 0, 0xffff_ffff) = iadd.i64 v3, v4 - return v5 -} - -;; check merged ops: -function %f4(i32, i32) -> i32 { -block0(v0 ! range(32, 0, 0x100): i32, v1 ! range(32, 0, 0x200): i32): - v2 = iconst.i32 2 - v3 ! range(32, 0, 0x400) = ishl.i32 v0, v2 - v4 ! range(32, 0, 0x5ff) = iadd.i32 v1, v3 - return v4 -} - -function %f5(i32, i64) -> i64 { -block0(v0 ! range(32, 0, 0x100): i32, v1 ! range(64, 0, 0x200): i64): - v2 ! range(64, 0, 0x100) = uextend.i64 v0 - v3 ! range(64, 0, 0x2ff) = iadd.i64 v1, v2 - return v3 -} diff --git a/cranelift/filetests/filetests/pcc/fail/blockparams.clif b/cranelift/filetests/filetests/pcc/fail/blockparams.clif deleted file mode 100644 index 3840f9932322..000000000000 --- a/cranelift/filetests/filetests/pcc/fail/blockparams.clif +++ /dev/null @@ -1,20 +0,0 @@ -test compile expect-fail -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i64, i32) -> i64 { -block0(v0 ! range(64, 0, 0x100): i64, v1: i32): - v2 ! range(64, 0, 0x100) = iconst.i64 0x100 - v3 ! range(64, 0, 0x200) = iadd v0, v2 - brif v1, block1(v0), block2(v3) - -block1(v4 ! range(64, 0, 0xff): i64): ;; shrink the range -- should be caught - jump block3(v4) - -block2(v5 ! range(64, 0, 0x1ff): i64): - jump block3(v5) - -block3(v6 ! range(64, 0, 1): i64): - return v6 -} diff --git a/cranelift/filetests/filetests/pcc/fail/extend.clif b/cranelift/filetests/filetests/pcc/fail/extend.clif deleted file mode 100644 index 5503aa23e48d..000000000000 --- a/cranelift/filetests/filetests/pcc/fail/extend.clif +++ /dev/null @@ -1,10 +0,0 @@ -test compile expect-fail -set enable_pcc=true -target aarch64 -;; disabled until PCC is migrated to new assembler: target x86_64 - -function %f0(i32) -> i64 { -block0(v0 ! range(32, 0, 0xffff_ffff): i32): - v1 ! range(64, 0, 0xffff_0000) = uextend.i64 v0 - return v1 -} diff --git a/cranelift/filetests/filetests/pcc/fail/load.clif b/cranelift/filetests/filetests/pcc/fail/load.clif deleted file mode 100644 index 24d4467cd37c..000000000000 --- a/cranelift/filetests/filetests/pcc/fail/load.clif +++ /dev/null @@ -1,65 +0,0 @@ -test compile expect-fail -set enable_pcc=true -target aarch64 -;; disabled until PCC is migrated to new assembler: target x86_64 - -function %f0(i64, i32) -> i64 { - mt0 = memory 0x1000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0x1000): i32): - v2 ! range(64, 0, 0x100) = uextend.i64 v1 - v3 ! mem(mt0, 0, 0x100) = iadd.i64 v0, v2 - v4 = load.i64 checked v3 - return v4 -} - -;; Insufficient guard region: the 8-byte load could go off the end. -function %f1(i64, i32) -> i64 { - mt0 = memory 0x1_0000_0000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0xffff_ffff): i32): - v2 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 - v3 ! mem(mt0, 0, 0xffff_ffff) = iadd.i64 v0, v2 - v4 = load.i64 checked v3 - return v4 -} - -;; RegRegExtend mode on aarch64. -function %f2(i64, i32) -> i8 { - mt0 = memory 0x1000 -block0(v0 ! mem(mt0, 0, 0x1000): i64, v1 ! range(32, 0, 0x1000): i32): - v2 ! range(64, 0, 0x100) = uextend.i64 v1 - v3 ! mem(mt0, 0, 0x100) = iadd.i64 v0, v2 - v4 = load.i8 checked v3 - return v4 -} - -;; RegReg mode on aarch64. -function %f3(i64, i64) -> i8 { - mt0 = memory 0x100 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(64, 0, 0xfff): i64): - v2 ! mem(mt0, 0, 0xfff) = iadd.i64 v0, v1 - v3 = load.i8 checked v2 - return v3 -} - -;; RegScaledExtended mode on aarch64. -function %f4(i64, i32) -> i64 { - mt0 = memory 0x7000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0xfff): i32): - v2 ! range(64, 0, 0xfff) = uextend.i64 v1 - v3 = iconst.i32 3 - v4 ! range(64, 0, 0x7ff8) = ishl.i64 v2, v3 - v5 ! mem(mt0, 0, 0x7ff8) = iadd.i64 v0, v4 - v6 = load.i64 checked v5 - return v6 -} - -;; RegScaled mode on aarch64. -function %f5(i64, i64) -> i64 { - mt0 = memory 0x7000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(64, 0, 0xfff): i64): - v2 = iconst.i32 3 - v3 ! range(64, 0, 0x7ff8) = ishl.i64 v1, v2 - v4 ! mem(mt0, 0, 0x7ff8) = iadd.i64 v0, v3 - v5 = load.i64 checked v4 - return v5 -} diff --git a/cranelift/filetests/filetests/pcc/fail/memtypes.clif b/cranelift/filetests/filetests/pcc/fail/memtypes.clif deleted file mode 100644 index 6f5b12a38307..000000000000 --- a/cranelift/filetests/filetests/pcc/fail/memtypes.clif +++ /dev/null @@ -1,30 +0,0 @@ -test verifier -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i64) -> i32 { - mt0 = struct 8 { 4: i32, 0: i32 } ; error: out-of-order - -block0(v0 ! mem(mt0, 0, 0): i64): - v1 = load.i32 v0+0 - return v1 -} - -function %f1(i64) -> i32 { - ;; out-of-bounds field: - mt0 = struct 8 { 0: i32, 6: i32 } ; error: field at offset 6 of size 4 that overflows - -block0(v0 ! mem(mt0, 0, 0): i64): - v1 = load.i32 v0+0 - return v1 -} - -function %f2(i64) -> i32 { - ;; overflowing offset + field size: - mt0 = struct 8 { 0: i32, 0xffff_ffff_ffff_ffff: i32 } ; error: field at offset 18446744073709551615 of size 4; offset plus size overflows a u64 - -block0(v0 ! mem(mt0, 0, 0): i64): - v1 = load.i32 v0+0 - return v1 -} diff --git a/cranelift/filetests/filetests/pcc/fail/shift.clif b/cranelift/filetests/filetests/pcc/fail/shift.clif deleted file mode 100644 index ab56de3447c3..000000000000 --- a/cranelift/filetests/filetests/pcc/fail/shift.clif +++ /dev/null @@ -1,12 +0,0 @@ -test compile expect-fail -set enable_pcc=true -target aarch64 -;; FIXME: should get pcc working again on x64 -; target x86_64 - -function %f0(i32) -> i32 { -block0(v0 ! range(32, 1, 0x100): i32): - v1 = iconst.i32 2 - v2 ! range(32, 4, 0x3ff) = ishl.i32 v0, v1 - return v2 -} diff --git a/cranelift/filetests/filetests/pcc/fail/simple.clif b/cranelift/filetests/filetests/pcc/fail/simple.clif deleted file mode 100644 index 2db3cae696a5..000000000000 --- a/cranelift/filetests/filetests/pcc/fail/simple.clif +++ /dev/null @@ -1,27 +0,0 @@ -test compile expect-fail -set enable_pcc=true -target aarch64 -;; disabled until PCC is migrated to new assembler: target x86_64 - -;; The `memory` memtype is not large enough here -- the 4GiB-range -;; 32-bit offset could go out of range. PCC should catch this. - -function %simple1(i64 vmctx, i32) -> i8 { - mt0 = memory 0x8000_0000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0xffff_ffff): i32): - v2 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 - v3 ! mem(mt0, 0, 0xffff_ffff) = iadd.i64 v0, v2 - v4 = load.i8 checked v3 - return v4 -} - -;; Check that the offset in the `mem` is validated too. - -function %simple2(i64 vmctx, i32) -> i8 { - mt0 = memory 0x8000_0000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0xffff_ffff): i32): - v2 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 - v3 ! mem(mt0, 0, 0) = iadd.i64 v0, v2 - v4 = load.i8 checked v3 - return v4 -} diff --git a/cranelift/filetests/filetests/pcc/fail/struct.clif b/cranelift/filetests/filetests/pcc/fail/struct.clif deleted file mode 100644 index 09c5f16347e5..000000000000 --- a/cranelift/filetests/filetests/pcc/fail/struct.clif +++ /dev/null @@ -1,29 +0,0 @@ -test compile expect-fail -set enable_pcc=true -target aarch64 -;; disabled until PCC is migrated to new assembler: target x86_64 - -function %f0(i64) -> i64 { - mt0 = struct 8 { 0: i64 ! mem(mt1, 0, 0) } - mt1 = memory 0x1_0000_0000 -block0(v0 ! mem(mt0, 0, 0): i64): - v1 ! mem(mt1, 8, 8) = load.i64 checked v0 - return v1 -} - -function %f1(i64, i64) { - mt0 = struct 8 { 0: i64 ! mem(mt1, 0, 0) } - mt1 = memory 0x1_0000_0000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! mem(mt1, 8, 8): i64): - store.i64 checked v1, v0 - return -} - -function %f2(i64) -> i32 { - mt0 = struct 8 { 0: i32 ! range(32, 0, 1), 4: i32 } - - ;; insufficiently-constrained range: -block0(v0 ! mem(mt0, 0, 8): i64): - v1 ! range(32, 0, 1) = load.i32 checked v0+0 - return v1 -} diff --git a/cranelift/filetests/filetests/pcc/fail/vmctx.clif b/cranelift/filetests/filetests/pcc/fail/vmctx.clif deleted file mode 100644 index fd2cae69ff95..000000000000 --- a/cranelift/filetests/filetests/pcc/fail/vmctx.clif +++ /dev/null @@ -1,38 +0,0 @@ -test compile expect-fail -set enable_pcc=true -target aarch64 -;; disabled until PCC is migrated to new assembler: target x86_64 - -;; Equivalent to a Wasm `i64.load` from a static memory. -function %f0(i64, i32) -> i64 { - ;; mock vmctx struct: - mt0 = struct 8 { 0: i64 readonly ! mem(mt1, 0, 0) } - ;; mock static memory: 4GiB range, *but insufficient guard* - mt1 = memory 0x1_0000_0000 - -block0(v0 ! mem(mt0, 0, 0): i64, v1: i32): - ;; Compute the address: base + offset. Guard region (2GiB) is - ;; sufficient for an 8-byte I64 load. - v2 ! mem(mt1, 0, 0) = load.i64 checked v0+0 ;; base pointer - v3 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 ;; offset - v4 ! mem(mt1, 0, 0xffff_ffff) = iadd.i64 v2, v3 - v5 = load.i64 checked v4 - return v5 -} - -;; Equivalent to a Wasm `i64.load` from a static memory. -function %f1(i64, i32) -> i64 { - ;; mock vmctx struct: - mt0 = struct 16 { 0: i64 readonly ! mem(mt1, 0, 0), 8: i64 readonly } - ;; mock static memory: 4GiB range, *but insufficient guard* - mt1 = memory 0x1_8000_0000 - -block0(v0 ! mem(mt0, 0, 0): i64, v1: i32): - ;; Compute the address: base + offset. Guard region (2GiB) is - ;; sufficient for an 8-byte I64 load. - v2 ! mem(mt1, 0, 0) = load.i64 checked v0+8 ;; base pointer, but the wrong one - v3 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 ;; offset - v4 ! mem(mt1, 0, 0xffff_ffff) = iadd.i64 v2, v3 - v5 = load.i64 checked v4 - return v5 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/add.clif b/cranelift/filetests/filetests/pcc/succeed/add.clif deleted file mode 100644 index 2e2416ce642d..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/add.clif +++ /dev/null @@ -1,61 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i32, i32) -> i32 { -block0(v0 ! range(32, 0, 0x100): i32, v1 ! range(32, 0, 0x80): i32): - v2 ! range(32, 0, 0x180) = iadd.i32 v0, v1 - return v2 -} - -function %f1(i32) -> i32 { -block0(v0 ! range(32, 0, 0x100): i32): - v1 ! range(32, 0, 1) = iconst.i32 1 - v2 ! range(32, 0, 0x101) = iadd.i32 v0, v1 - return v2 -} - -;; a looser but still accurate bound should check too: -function %f2(i32) -> i32 { -block0(v0 ! range(32, 0, 0x100): i32): - v1 ! range(32, 0, 1) = iconst.i32 1 - v2 ! range(32, 0, 0x102) = iadd.i32 v0, v1 - return v2 -} - -;; we should be able to verify a range based on the type alone: -function %f3(i32) -> i64 { -block0(v0: i32): - v1 ! range(32, 0, 1) = iconst.i32 1 - v2 ! range(32, 0, 0xffff_ffff) = iadd.i32 v0, v1 - v3 ! range(64, 0, 0xffff_ffff) = uextend.i64 v2 - return v3 -} - -;; we should be able to verify a range based on the type alone: -function %f3(i32) -> i64 { -block0(v0: i32): - v1 ! range(32, 0, 1) = iconst.i32 1 - v2 ! range(32, 0, 0xffff_ffff) = iadd.i32 v0, v1 - v3 ! range(64, 0, 0xffff_ffff) = uextend.i64 v2 - v4 ! range(64, 0, 0x1) = iconst.i64 1 - v5 ! range(64, 0, 0x1_0000_0000) = iadd.i64 v3, v4 - return v5 -} - -;; check merged ops: -function %f4(i32, i32) -> i32 { -block0(v0 ! range(32, 0, 0x100): i32, v1 ! range(32, 0, 0x200): i32): - v2 = iconst.i32 2 - v3 ! range(32, 0, 0x400) = ishl.i32 v0, v2 - v4 ! range(32, 0, 0x600) = iadd.i32 v1, v3 - return v4 -} - -function %f5(i32, i64) -> i64 { -block0(v0 ! range(32, 0, 0x100): i32, v1 ! range(64, 0, 0x200): i64): - v2 ! range(64, 0, 0x100) = uextend.i64 v0 - v3 ! range(64, 0, 0x300) = iadd.i64 v1, v2 - return v3 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/blockparams.clif b/cranelift/filetests/filetests/pcc/succeed/blockparams.clif deleted file mode 100644 index 7a823e1bf27d..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/blockparams.clif +++ /dev/null @@ -1,20 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i64, i32) -> i64 { -block0(v0 ! range(64, 0, 0x100): i64, v1: i32): - v2 ! range(64, 0, 0x100) = iconst.i64 0x100 - v3 ! range(64, 0, 0x200) = iadd v0, v2 - brif v1, block1(v0), block2(v3) - -block1(v4 ! range(64, 0, 0x1000): i64): ;; broaden the range -- always allowed - jump block3(v4) - -block2(v5 ! range(64, 0, 0x2000): i64): - jump block3(v5) - -block3(v6 ! range(64, 0, 0x2001): i64): - return v6 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/const.clif b/cranelift/filetests/filetests/pcc/succeed/const.clif deleted file mode 100644 index dd1e258d4e1c..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/const.clif +++ /dev/null @@ -1,42 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0() { -block0: - v0 ! range(64, 0, 0) = iconst.i64 0 - v1 ! range(64, 1, 1) = iconst.i64 1 - v2 ! range(64, 0xfff, 0xfff) = iconst.i64 0xfff - v3 ! range(64, 0x10000, 0x10000) = iconst.i64 0x10000 - v4 ! range(64, 0xffffc, 0xffffc) = iconst.i64 0xffffc - v5 ! range(64, 0x1_0000_0000, 0x1_0000_0000) = iconst.i64 0x1_0000_0000 - v6 ! range(64, 0x1_0000_0000_0000, 0x1_0000_0000_0000) = iconst.i64 0x1_0000_0000_0000 - v7 ! range(64, 0xffff_0000_0000_0000, 0xffff_0000_0000_0000) = iconst.i64 0xffff_0000_0000_0000 - v8 ! range(64, 0xffff_0000_0000_ffff, 0xffff_0000_0000_ffff) = iconst.i64 0xffff_0000_0000_ffff - return -} - -function %f1() -> i32 { -block0: - v0 = iconst.i32 0x10_0010 - return v0 -} - -function %f2() -> i64 { -block0: - v0 = iconst.i64 0x9_ffff_ffff - return v0 -} - -function %f3() -> i64 { -block0: - v0 = iconst.i64 0xffff_fff7 - return v0 -} - -function %f4() -> i64 { -block0: - v0 ! range(64, 0x1_0001_ffff_ffff, 0x1_0001_ffff_ffff) = iconst.i64 0x1_0001_ffff_ffff - return v0 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/dynamic.clif b/cranelift/filetests/filetests/pcc/succeed/dynamic.clif deleted file mode 100644 index d05f08c76caf..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/dynamic.clif +++ /dev/null @@ -1,42 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -;; disabled until PCC is migrated to new assembler: target x86_64 - -;; Equivalent to a Wasm `i64.load` from a dynamic memory. -function %f0(i64 vmctx, i32) -> i64 { - gv0 = vmctx - gv1 = load.i64 notrap aligned checked gv0+0 ;; base - gv2 = load.i64 notrap aligned checked gv0+8 ;; size - - ;; mock vmctx struct: - mt0 = struct 16 { - 0: i64 readonly ! dynamic_mem(mt1, 0, 0), - 8: i64 readonly ! dynamic_range(64, gv2, gv2), - } - ;; mock dynamic memory: dynamic range, plus 2GiB guard - mt1 = dynamic_memory gv2 + 0x8000_0000 - -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! dynamic_range(32, v1, v1): i32): - v2 ! dynamic_range(64, v1, v1) = uextend.i64 v1 ;; extended Wasm offset - v3 ! dynamic_mem(mt1, 0, 0) = global_value.i64 gv1 ;; base - v4 ! dynamic_range(64, gv2, gv2) = global_value.i64 gv2 ;; size - v5 ! compare(uge, v1, gv2) = icmp.i64 uge v2, v4 ;; bounds-check compare of extended Wasm offset to size - v6 ! dynamic_mem(mt1, v1, v1) = iadd.i64 v3, v2 ;; compute access address: memory base plus extended Wasm offset - v7 ! dynamic_mem(mt1, 0, 0, nullable) = iconst.i64 0 ;; null pointer for speculative path - v8 ! dynamic_mem(mt1, 0, gv2-1, nullable) = select_spectre_guard v5, v7, v6 ;; if OOB, pick null, otherwise the real address - v9 = load.i64 checked v8 - return v9 -} - -;; select sees: -;; v5 ! compare(uge, v1, gv2) -;; v6 ! dynamic_mem(mt1, v1, v1) -;; v7 ! dynamic_mem(mt0, 0, 0, nullable) -;; -;; preprocess: -;; v6' (assuming compare is false) = dynamic_mem(mt1, 0, gv2-1) -;; v7' (assuming compare is true) = dynamic_mem(mt1, 0, 0, nullable) -;; -;; take the union of range and nullability: -;; dynamic_mem(mt1, 0, gv2-1, nullable) diff --git a/cranelift/filetests/filetests/pcc/succeed/extend.clif b/cranelift/filetests/filetests/pcc/succeed/extend.clif deleted file mode 100644 index 2926aa65bd7d..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/extend.clif +++ /dev/null @@ -1,17 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i32) -> i64 { -block0(v0 ! range(32, 42, 0xffff_fffe): i32): - ;; we're allowed to broaden the range on either end: - v1 ! range(64, 1, 0xffff_ffff) = uextend.i64 v0 - return v1 -} - -function %f1(i32) -> i64 { -block0(v0 ! range(16, 0, 0xffff): i32): - v1 ! range(64, 0, 0xffff_ffff) = uextend.i64 v0 - return v1 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/fuzz-float-loads.clif b/cranelift/filetests/filetests/pcc/succeed/fuzz-float-loads.clif deleted file mode 100644 index 1edaa92b0c43..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/fuzz-float-loads.clif +++ /dev/null @@ -1,86 +0,0 @@ -test compile -set enable_pcc=true -set opt_level=speed -target aarch64 -;; disabled until PCC is migrated to new assembler: target x86_64 - -function u0:0(i64 vmctx, i64) fast { - gv0 = vmctx - gv1 = load.i64 notrap aligned readonly gv0+8 - gv2 = load.i64 notrap aligned gv1 - gv3 ! mem(mt0, 0x0, 0x0) = vmctx - gv4 ! mem(mt1, 0x0, 0x0) = load.i64 notrap aligned readonly checked gv3+80 - mt0 = struct 88 { 80: i64 readonly ! mem(mt1, 0x0, 0x0) } - mt1 = memory 0x180000000 - sig0 = (i64 vmctx) system_v - sig1 = (i64 vmctx, i32 uext) system_v - sig2 = (i64 vmctx, i32 uext, i32 uext, i32 uext) -> i32 uext system_v - const0 = 0x02030077ff00ff04030077ff6cff5503 - stack_limit = gv2 - -block0(v0: i64, v1: i64): - v2 = iconst.i32 0 - v25 -> v2 - v45 -> v2 - v24 -> v25 - v3 = iconst.i64 0 - v4 = global_value.i64 gv3 - v5 = load.i64 notrap aligned v4+8 - v17 -> v5 - v44 -> v5 - v42 -> v17 - v6 = load.i64 notrap aligned v5+8 - v7 = iadd_imm v6, 1 - v8 = iconst.i64 0 - v9 = icmp sge v7, v8 ; v8 = 0 - brif v9, block2, block3(v7) - -block2: - store.i64 notrap aligned v7, v5+8 - v10 = global_value.i64 gv3 - v11 = load.i64 notrap aligned readonly v10+56 - v12 = load.i64 notrap aligned readonly v11+120 - call_indirect sig0, v12(v10) - v13 = load.i64 notrap aligned v5+8 - jump block3(v13) - -block3(v43: i64): - v15 -> v43 - jump block4 - -block4: - v14 = iconst.i64 0 - v16 = icmp.i64 sge v15, v14 ; v14 = 0 - brif v16, block6, block7(v15) - -block6: - store.i64 notrap aligned v15, v17+8 - v18 = global_value.i64 gv3 - v19 = load.i64 notrap aligned readonly v18+56 - v20 = load.i64 notrap aligned readonly v19+120 - call_indirect sig0, v20(v18) - v21 = load.i64 notrap aligned v17+8 - jump block7(v21) - -block7(v40: i64): - v22 = vconst.i8x16 const0 - v23 = uwiden_high v22 ; v22 = const0 - v26 = bitcast.i8x16 little v23 - v27 = sshr v26, v25 ; v25 = 0 - v28 = bitcast.i64x2 little v27 - v29 = extractlane v28, 1 - v30 = iconst.i32 1 - v31 = global_value.i64 gv3 - v32 = load.i64 notrap aligned readonly v31+56 - v33 = load.i64 notrap aligned readonly v32+24 - call_indirect sig1, v33(v31, v30) ; v30 = 1 - v34 = ireduce.i32 v29 - v35 ! range(64, 0x0, 0xffffffff) = uextend.i64 v34 - v36 ! mem(mt1, 0x0, 0x0) = global_value.i64 gv4 - v37 ! mem(mt1, 0x0, 0xffffffff) = iadd v36, v35 - v38 = load.f64 little checked heap v37 - v39 = f64const 0x0.0000000005b28p-1022 - v41 = iadd_imm v40, 9 - store notrap aligned v41, v17+8 - trap user1 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/gv_fact.clif b/cranelift/filetests/filetests/pcc/succeed/gv_fact.clif deleted file mode 100644 index 329b8d444edc..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/gv_fact.clif +++ /dev/null @@ -1,58 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i64 vmctx) -> i64 { - mt0 = struct 16 { 8: i64 ! mem(mt1, 0, 0) } - mt1 = memory 0x1_0000_0000 - gv0 ! mem(mt0, 0, 0) = vmctx - gv1 ! mem(mt1, 0, 0) = load.i64 notrap aligned checked gv0+8 - -block0(v0 ! mem(mt0, 0, 0): i64): - v1 ! mem(mt1, 0, 0) = global_value.i64 gv1 - return v1 -} - -function %f1(i64 vmctx) -> i64 { - mt0 = struct 16 { 8: i64 ! mem(mt1, 0, 0) } - mt1 = struct 8 { 0: i64 ! mem(mt2, 0, 0) } - mt2 = memory 0x1_0000_0000 - gv0 ! mem(mt0, 0, 0) = vmctx - gv1 ! mem(mt1, 0, 0) = load.i64 notrap aligned checked gv0+8 - gv2 ! mem(mt2, 0, 0) = load.i64 notrap aligned checked gv1+0 - -block0(v0 ! mem(mt0, 0, 0): i64): - v1 ! mem(mt2, 0, 0) = global_value.i64 gv2 - return v1 -} - -function %f2(i64 vmctx) -> i64 { - mt0 = struct 16 { 8: i64 ! mem(mt1, 0, 0) } - mt1 = struct 8 { 0: i64 ! mem(mt2, 0, 0) } - mt2 = memory 0x1_0000_0000 - gv0 ! mem(mt0, 0, 0) = vmctx - gv1 ! mem(mt1, 0, 0) = load.i64 notrap aligned checked gv0+8 - gv2 ! mem(mt2, 0, 0) = load.i64 notrap aligned checked gv1+0 - gv3 ! mem(mt2, 8, 8) = iadd_imm.i64 gv2, 8 - -block0(v0 ! mem(mt0, 0, 0): i64): - v1 ! mem(mt2, 8, 8) = global_value.i64 gv3 - return v1 -} - -function %f3(i64 vmctx) -> i64 { - mt0 = struct 16 { 8: i64 ! mem(mt1, 0, 0) } - mt1 = struct 8 { 0: i64 ! mem(mt2, 0, 0) } - mt2 = memory 0x1_0000_0000 - gv0 ! mem(mt0, 0, 0) = vmctx - gv1 ! mem(mt1, 0, 0) = load.i64 notrap aligned checked gv0+8 - gv2 ! mem(mt2, 0, 0) = load.i64 notrap aligned checked gv1+0 - gv3 ! mem(mt2, 8, 8) = iadd_imm.i64 gv2, 8 - - ;; like the above, but with no fact provided on `v0`; it should - ;; get copied from the GV. -block0(v0: i64): - v1 ! mem(mt2, 8, 8) = global_value.i64 gv3 - return v1 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/imul-fuzzbug.clif b/cranelift/filetests/filetests/pcc/succeed/imul-fuzzbug.clif deleted file mode 100644 index 7bc77f03ce15..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/imul-fuzzbug.clif +++ /dev/null @@ -1,282 +0,0 @@ -test compile -set enable_pcc=true -set opt_level=speed -target x86_64 - -function u0:3(i64 vmctx, i64) fast { - gv0 = vmctx - gv1 = load.i64 notrap aligned readonly gv0+8 - gv2 = load.i64 notrap aligned gv1 - gv3 ! mem(mt0, 0x0, 0x0) = vmctx - mt0 = struct 0 { } - sig0 = (i64 vmctx, i32 uext) system_v - sig1 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v - sig2 = (i64 vmctx, i32 uext) -> i32 uext system_v - stack_limit = gv2 - -block0(v0 ! mem(mt0, 0x0, 0x0): i64, v1: i64): - v13 -> v0 - v17 -> v0 - v21 -> v0 - v22 -> v0 - v43 -> v0 - v51 -> v0 - v155 -> v0 - v160 -> v0 - v163 -> v0 - v164 -> v0 - v167 -> v0 - v168 -> v0 - v171 -> v0 - v173 -> v0 - v176 -> v0 - v177 -> v0 - v180 -> v0 - v182 -> v0 - v185 -> v0 - v2 = f64const 0.0 - v3 = iconst.i64 0 - v4 = iconst.i64 0 - v5 = iconst.i64 0 - v6 = iconst.i64 0 - v7 = iconst.i64 0 - v8 = iconst.i64 0 - v9 = iconst.i64 0 - v10 = iconst.i64 0 - v11 = iconst.i64 0 - v12 = iconst.i64 0 - v14 = load.i32 notrap aligned table v13+192 - v186 = iconst.i32 0 - v15 = icmp eq v14, v186 ; v186 = 0 - v16 = uextend.i32 v15 - brif v16, block2, block3 - -block2: - trap user1 - -block3: - v18 = load.i32 notrap aligned table v17+192 - v19 = iconst.i32 1 - v20 = isub v18, v19 ; v19 = 1 - store notrap aligned table v20, v21+192 - v23 = load.i32 notrap aligned table v22+128 - v24 = f64const -0x1.3090455030609p194 - v25 = fneg v24 ; v24 = -0x1.3090455030609p194 - v26 = fneg v25 - v27 = fneg v26 - v28 = fneg v27 - v29 = fneg v28 - v30 = fneg v29 - v31 = fneg v30 - v32 = fneg v31 - v33 = fneg v32 - v34 = fneg v33 - v35 = fneg v34 - v36 = fneg v35 - v37 = floor v36 - v38 = f64const +NaN - v39 = fcmp eq v37, v37 - v40 = uextend.i32 v39 - v41 = select v40, v37, v38 ; v38 = +NaN - v42 = iconst.i32 0x1309_0455 - v44 = load.i32 notrap aligned table v43+128 - v45 = rotl v42, v44 ; v42 = 0x1309_0455 - v46 = fcvt_from_sint.f32 v45 - v47 = iconst.i64 0x2001_8113_0904_5503 - v48 = iconst.i64 0x2001_8113_0904_5503 - v187 = iconst.i64 0 - v49 = icmp eq v48, v187 ; v48 = 0x2001_8113_0904_5503, v187 = 0 - v50 = uextend.i32 v49 - store notrap aligned table v50, v51+128 - v52 = iconst.i32 0x2001_8113 - v53 = iconst.i32 0x0904_5503 - v54 = rotl v52, v53 ; v52 = 0x2001_8113, v53 = 0x0904_5503 - v55 = fcvt_from_sint.f32 v54 - v56 = iconst.i64 -1 - v70 -> v56 - v71 -> v56 - v57 = iconst.i32 0xffff_ffff - v58 = uextend.i64 v57 ; v57 = 0xffff_ffff - v59 = iconst.i64 1 - v188 = iconst.i64 0 - v60 = icmp eq v58, v188 ; v188 = 0 - v61 = uextend.i32 v60 - v62 = select v61, v59, v58 ; v59 = 1 - v73 -> v62 - v63 = iconst.i64 0x8000_0000_0000_0000 - v64 = icmp ne v56, v63 ; v56 = -1, v63 = 0x8000_0000_0000_0000 - v65 = uextend.i32 v64 - brif v65, block5, block6 - -block6: - v66 = iconst.i64 -1 - v67 = icmp.i64 ne v62, v66 ; v66 = -1 - v68 = uextend.i32 v67 - brif v68, block5, block7 - -block7: - v69 = iconst.i64 1 - jump block4(v69) ; v69 = 1 - -block5: - jump block4(v62) - -block4(v72: i64): - v74 = srem.i64 v56, v72 ; v56 = -1 - v88 -> v74 - v89 -> v74 - v75 = iconst.i32 0xffff_ffff - v76 = uextend.i64 v75 ; v75 = 0xffff_ffff - v77 = iconst.i64 1 - v189 = iconst.i64 0 - v78 = icmp eq v76, v189 ; v189 = 0 - v79 = uextend.i32 v78 - v80 = select v79, v77, v76 ; v77 = 1 - v91 -> v80 - v81 = iconst.i64 0x8000_0000_0000_0000 - v82 = icmp ne v74, v81 ; v81 = 0x8000_0000_0000_0000 - v83 = uextend.i32 v82 - brif v83, block9, block10 - -block10: - v84 = iconst.i64 -1 - v85 = icmp.i64 ne v80, v84 ; v84 = -1 - v86 = uextend.i32 v85 - brif v86, block9, block11 - -block11: - v87 = iconst.i64 1 - jump block8(v87) ; v87 = 1 - -block9: - jump block8(v80) - -block8(v90: i64): - v92 = srem.i64 v74, v90 - v106 -> v92 - v107 -> v92 - v93 = iconst.i32 0xffff_ffff - v94 = uextend.i64 v93 ; v93 = 0xffff_ffff - v95 = iconst.i64 1 - v190 = iconst.i64 0 - v96 = icmp eq v94, v190 ; v190 = 0 - v97 = uextend.i32 v96 - v98 = select v97, v95, v94 ; v95 = 1 - v109 -> v98 - v99 = iconst.i64 0x8000_0000_0000_0000 - v100 = icmp ne v92, v99 ; v99 = 0x8000_0000_0000_0000 - v101 = uextend.i32 v100 - brif v101, block13, block14 - -block14: - v102 = iconst.i64 -1 - v103 = icmp.i64 ne v98, v102 ; v102 = -1 - v104 = uextend.i32 v103 - brif v104, block13, block15 - -block15: - v105 = iconst.i64 1 - jump block12(v105) ; v105 = 1 - -block13: - jump block12(v98) - -block12(v108: i64): - v110 = srem.i64 v92, v108 - v124 -> v110 - v125 -> v110 - v111 = iconst.i32 0xffff_ffff - v112 = uextend.i64 v111 ; v111 = 0xffff_ffff - v113 = iconst.i64 1 - v191 = iconst.i64 0 - v114 = icmp eq v112, v191 ; v191 = 0 - v115 = uextend.i32 v114 - v116 = select v115, v113, v112 ; v113 = 1 - v127 -> v116 - v117 = iconst.i64 0x8000_0000_0000_0000 - v118 = icmp ne v110, v117 ; v117 = 0x8000_0000_0000_0000 - v119 = uextend.i32 v118 - brif v119, block17, block18 - -block18: - v120 = iconst.i64 -1 - v121 = icmp.i64 ne v116, v120 ; v120 = -1 - v122 = uextend.i32 v121 - brif v122, block17, block19 - -block19: - v123 = iconst.i64 1 - jump block16(v123) ; v123 = 1 - -block17: - jump block16(v116) - -block16(v126: i64): - v128 = srem.i64 v110, v126 - v145 -> v128 - v146 -> v128 - v129 = iconst.i32 0x0944_0909 - v130 = popcnt v129 ; v129 = 0x0944_0909 - v131 = iconst.i32 0x3482_3582 - v132 = imul v130, v131 ; v131 = 0x3482_3582 - v133 = uextend.i64 v132 - v134 = iconst.i64 1 - v192 = iconst.i64 0 - v135 = icmp eq v133, v192 ; v192 = 0 - v136 = uextend.i32 v135 - v137 = select v136, v134, v133 ; v134 = 1 - v148 -> v137 - v138 = iconst.i64 0x8000_0000_0000_0000 - v139 = icmp ne v128, v138 ; v138 = 0x8000_0000_0000_0000 - v140 = uextend.i32 v139 - brif v140, block21, block22 - -block22: - v141 = iconst.i64 -1 - v142 = icmp.i64 ne v137, v141 ; v141 = -1 - v143 = uextend.i32 v142 - brif v143, block21, block23 - -block23: - v144 = iconst.i64 1 - jump block20(v144) ; v144 = 1 - -block21: - jump block20(v137) - -block20(v147: i64): - v149 = srem.i64 v128, v147 - v150 = iconst.i32 0xb7b7_746e - v151 = fcvt_from_uint.f32 v150 ; v150 = 0xb7b7_746e - v152 = fcvt_to_sint_sat.i64 v151 - v153 = icmp eq v149, v152 - v154 = uextend.i32 v153 - v156 = load.i32 notrap aligned table v155+128 - v157 = ctz v156 - v158 = f32const 0x1.bc0402p-124 - v159 = bitcast.i32 v158 ; v158 = 0x1.bc0402p-124 - v161 = load.i32 notrap aligned table v160+144 - v162 = bxor v159, v161 - store notrap aligned table v162, v163+144 - v165 = load.i32 notrap aligned table v164+160 - v166 = bxor v157, v165 - store notrap aligned table v166, v167+160 - v169 = load.i32 notrap aligned table v168+160 - v170 = bxor v154, v169 - store notrap aligned table v170, v171+160 - v172 = bitcast.i32 v55 - v174 = load.i32 notrap aligned table v173+144 - v175 = bxor v172, v174 - store notrap aligned table v175, v176+144 - v178 = load.i64 notrap aligned table v177+176 - v179 = bxor.i64 v47, v178 ; v47 = 0x2001_8113_0904_5503 - store notrap aligned table v179, v180+176 - v181 = bitcast.i32 v46 - v183 = load.i32 notrap aligned table v182+144 - v184 = bxor v181, v183 - store notrap aligned table v184, v185+144 - jump block1 - -block1: - return -} diff --git a/cranelift/filetests/filetests/pcc/succeed/load.clif b/cranelift/filetests/filetests/pcc/succeed/load.clif deleted file mode 100644 index 59996ef05fae..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/load.clif +++ /dev/null @@ -1,85 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i64, i32) -> i64 { - mt0 = memory 0x1_0000_0000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0x100): i32): - v2 ! range(64, 0, 0x100) = uextend.i64 v1 - v3 ! mem(mt0, 0, 8) = iadd.i64 v0, v2 - v4 = load.i64 checked v3 - return v4 -} - -function %f1(i64, i32) -> i64 { - ;; Note the guard region of 8 bytes -- just enough for the below! - mt0 = memory 0x1_0000_0008 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0xffff_ffff): i32): - v2 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 - v3 ! mem(mt0, 0, 0xffff_ffff) = iadd.i64 v0, v2 - v4 = load.i64 checked v3 - return v4 -} - -;; RegRegExtend mode on aarch64. -function %f2(i64, i32) -> i8 { - mt0 = memory 0x1000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0xfff): i32): - v2 ! range(64, 0, 0xfff) = uextend.i64 v1 - v3 ! mem(mt0, 0, 0xfff) = iadd.i64 v0, v2 - v4 = load.i8 checked v3 - return v4 -} - -;; RegReg mode on aarch64. -function %f3(i64, i64) -> i8 { - mt0 = memory 0x1000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(64, 0, 0xfff): i64): - v2 ! mem(mt0, 0, 0xfff) = iadd.i64 v0, v1 - v3 = load.i8 checked v2 - return v3 -} - -;; RegScaledExtended mode on aarch64. -function %f4(i64, i32) -> i64 { - mt0 = memory 0x8000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0xfff): i32): - v2 ! range(64, 0, 0xfff) = uextend.i64 v1 - v3 = iconst.i32 3 - v4 ! range(64, 0, 0x7ff8) = ishl.i64 v2, v3 - v5 ! mem(mt0, 0, 0x7ff8) = iadd.i64 v0, v4 - v6 = load.i64 checked v5 - return v6 -} - -;; RegScaled mode on aarch64. -function %f5(i64, i64) -> i64 { - mt0 = memory 0x8000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(64, 0, 0xfff): i64): - v2 = iconst.i32 3 - v3 ! range(64, 0, 0x7ff8) = ishl.i64 v1, v2 - v4 ! mem(mt0, 0, 0x7ff8) = iadd.i64 v0, v3 - v5 = load.i64 checked v4 - return v5 -} - -;; UnsignedOffset mode on aarch64. -function %f6(i64) -> i64 { - mt0 = memory 0x8000 -block0(v0 ! mem(mt0, 0, 0): i64): - v2 = iconst.i64 8 - v3 ! mem(mt0, 8, 8) = iadd.i64 v0, v2 - v4 = load.i64 checked v3 - return v4 -} - -;; Unscaled mode on aarch64. -function %f6(i64) -> i64 { - mt0 = memory 0x8000 -block0(v0 ! mem(mt0, 8, 8): i64): - v2 = iconst.i64 8 - v3 ! mem(mt0, 0, 0) = isub.i64 v0, v2 - v4 = load.i64 checked v3 - return v4 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/memtypes.clif b/cranelift/filetests/filetests/pcc/succeed/memtypes.clif deleted file mode 100644 index dc4805c90cc5..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/memtypes.clif +++ /dev/null @@ -1,24 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i64) -> i32 { - mt0 = struct 8 { 0: i32, 4: i32 readonly } - -block0(v0 ! mem(mt0, 0, 0): i64): ;; v0 points to an instance of mt0, at offset 0 - v1 = load.i32 v0+0 - v2 = load.i32 v0+4 - v3 = iadd.i32 v1, v2 - return v3 -} - -function %f1(i64) -> i32 { - mt0 = struct 8 { 0: i64 readonly ! mem(mt1, 0, 0) } - mt1 = memory 0x1_0000_0000 - -block0(v0 ! mem(mt0, 0, 0): i64): - v1 ! mem(mt1, 0, 0) = load.i64 v0 - v2 = load.i32 v1+0x1000 - return v2 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/opt.clif b/cranelift/filetests/filetests/pcc/succeed/opt.clif deleted file mode 100644 index 8fda923223c4..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/opt.clif +++ /dev/null @@ -1,85 +0,0 @@ -test compile -set enable_pcc=true -set opt_level=speed -target aarch64 -target x86_64 - -;; Equivalent to a Wasm `i64.load` from a static memory, but with some -;; redundant stuff that should be optimized away (x+0 -> x). -function %f0(i64, i32) -> i64 { - ;; mock vmctx struct: - mt0 = struct 8 { 0: i64 readonly ! mem(mt1, 0, 0) } - ;; mock static memory: 4GiB range, plus 2GiB guard - mt1 = memory 0x1_8000_0000 - -block0(v0 ! mem(mt0, 0, 0): i64, v1: i32): - v2 ! mem(mt1, 0, 0) = load.i64 checked v0+0 - v3 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 - v4 = iconst.i64 0 - v5 = iadd.i64 v3, v4 - v6 ! mem(mt1, 0, 0xffff_ffff) = iadd.i64 v2, v5 - v7 = load.i64 checked v6 - return v7 -} - -;; GVN opportunity. -function %f1(i64, i32) -> i64 { - ;; mock vmctx struct: - mt0 = struct 8 { 0: i64 readonly ! mem(mt1, 0, 0) } - ;; mock static memory: 4GiB range, plus 2GiB guard - mt1 = memory 0x1_8000_0000 - -block0(v0 ! mem(mt0, 0, 0): i64, v1: i32): - v2 ! mem(mt1, 0, 0) = load.i64 checked notrap readonly v0+0 - v3 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 - v4 = iconst.i64 0 - v5 = iadd.i64 v3, v4 - v6 ! mem(mt1, 0, 0xffff_ffff) = iadd.i64 v2, v5 - v7 = load.i64 checked v6 - - v8 = load.i64 checked notrap readonly v0+0 - v9 = uextend.i64 v1 - v10 ! mem(mt1, 0, 0xffff_ffff) = iadd.i64 v8, v9 - v11 = load.i64 checked v10 - - v12 = iadd.i64 v7, v11 - - return v12 -} - -;; RLE opportunity. -function %f2(i64, i32) -> i64 { - ;; mock vmctx struct: - mt0 = struct 8 { 0: i64 readonly ! mem(mt1, 0, 0) } - ;; mock static memory: 4GiB range, plus 2GiB guard - mt1 = memory 0x1_8000_0000 - -block0(v0 ! mem(mt0, 0, 0): i64, v1: i32): - v2 ! mem(mt1, 0, 0) = load.i64 checked notrap readonly aligned v0+0 - brif v1, block1, block2 - -block1: - v3 ! mem(mt1, 0, 0) = load.i64 checked notrap readonly aligned v0+0 - return v3 - -block2: - v4 ! mem(mt1, 0, 0) = load.i64 checked notrap readonly aligned v0+0 - return v4 -} - -function %f3(i64, i32) { - mt0 = struct 4 { 0: i32 ! range(32, 1, 3) } - -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 1): i32): - v2 ! range(32, 1, 1) = iconst.i32 1 - v3 ! range(32, 1, 2) = iadd.i32 v1, v2 - - v4 ! range(32, 1, 1) = iconst.i32 1 - v5 ! range(32, 1, 3) = iadd.i32 v1, v4 ;; should GVN onto v3. - - ;; v3/v5's facts should merge to the narrower range. - store.i32 checked v3, v0 - store.i32 checked v5, v0 - - return -} diff --git a/cranelift/filetests/filetests/pcc/succeed/shift.clif b/cranelift/filetests/filetests/pcc/succeed/shift.clif deleted file mode 100644 index 0fcf627464ef..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/shift.clif +++ /dev/null @@ -1,18 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i32) -> i32 { -block0(v0 ! range(32, 1, 0x100): i32): - v1 = iconst.i32 2 - v2 ! range(32, 4, 0x400) = ishl.i32 v0, v1 - return v2 -} - -function %f0(i32) -> i32 { -block0(v0: i32): - v1 = iconst.i32 2 - v2 ! range(32, 0, 0xffff_ffff) = ishl.i32 v0, v1 - return v2 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/simple.clif b/cranelift/filetests/filetests/pcc/succeed/simple.clif deleted file mode 100644 index 80dc769899ef..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/simple.clif +++ /dev/null @@ -1,13 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %simple1(i64 vmctx, i32) -> i8 { - mt0 = memory 0x1_0000_0000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! range(32, 0, 0xffff_ffff): i32): - v2 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 - v3 ! mem(mt0, 0, 0xffff_ffff) = iadd.i64 v0, v2 - v4 = load.i8 checked v3 - return v4 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/struct.clif b/cranelift/filetests/filetests/pcc/succeed/struct.clif deleted file mode 100644 index f53bdaff54d4..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/struct.clif +++ /dev/null @@ -1,20 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -function %f0(i64) -> i64 { - mt0 = struct 8 { 0: i64 ! mem(mt1, 0, 0) } - mt1 = memory 0x1_0000_0000 -block0(v0 ! mem(mt0, 0, 0): i64): - v1 ! mem(mt1, 0, 0) = load.i64 checked v0 - return v1 -} - -function %f1(i64, i64) { - mt0 = struct 8 { 0: i64 ! mem(mt1, 0, 0) } - mt1 = memory 0x1_0000_0000 -block0(v0 ! mem(mt0, 0, 0): i64, v1 ! mem(mt1, 0, 0): i64): - store.i64 checked v1, v0 - return -} diff --git a/cranelift/filetests/filetests/pcc/succeed/uextend-add-iconst.clif b/cranelift/filetests/filetests/pcc/succeed/uextend-add-iconst.clif deleted file mode 100644 index f7a62d9d765a..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/uextend-add-iconst.clif +++ /dev/null @@ -1,120 +0,0 @@ -test compile -set enable_multi_ret_implicit_sret -set enable_pcc=true -set opt_level=speed -target x86_64 - -;; v110 is a uextend'd iconst; v111 is a memtype base; v112 is that -;; base plus that fixed offset, and should be able to verify that its -;; offset is in 0..4GiB. In the fuzzbug that inspired this test, we -;; had a bare `iconst` with no range fact on it after optimization. - -function u0:2(i64 vmctx, i64, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) -> i32, i32, i32, i32, i32, i32 fast { - gv0 = vmctx - gv1 = load.i64 notrap aligned readonly gv0+8 - gv2 = load.i64 notrap aligned gv1 - gv3 ! mem(mt0, 0x0, 0x0) = vmctx - gv4 ! mem(mt1, 0x0, 0x0) = load.i64 notrap aligned readonly checked gv3+96 - mt0 = struct 104 { 96: i64 readonly ! mem(mt1, 0x0, 0x0) } - mt1 = memory 0x180000000 - sig0 = (i64 vmctx, i32 uext) system_v - sig1 = (i64 vmctx, i32 uext, i32 uext, i32 uext) -> i32 uext system_v - sig2 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v - sig3 = (i64 vmctx, i32 uext) -> i32 uext system_v - stack_limit = gv2 - -block0(v0 ! mem(mt0, 0x0, 0x0): i64, v1: i64, v2: i32, v3: i32, v4: i32, v5: i32, v6: i32, v7: i32, v8: i32, v9: i32, v10: i32, v11: i32, v12: i32, v13: i32, v14: i32, v15: i32, v16: i32, v17: i32, v18: i32, v19: i32, v20: i32, v21: i32): - v77 -> v0 - v81 -> v0 - v85 -> v0 - v87 -> v0 - v91 -> v0 - v95 -> v0 - v97 -> v0 - v130 -> v0 - v78 = load.i32 notrap aligned table v77+176 - v127 = iconst.i32 0 - v79 = icmp eq v78, v127 ; v127 = 0 - v80 = uextend.i32 v79 - brif v80, block2, block3 - -block2: - trap user1 - -block3: - v82 = load.i32 notrap aligned table v81+176 - v83 = iconst.i32 1 - v84 = isub v82, v83 ; v83 = 1 - store notrap aligned table v84, v85+176 - jump block4 - -block4: - v88 = load.i32 notrap aligned table v87+176 - v128 = iconst.i32 0 - v89 = icmp eq v88, v128 ; v128 = 0 - v90 = uextend.i32 v89 - brif v90, block6, block7 - -block6: - trap user1 - -block7: - v92 = load.i32 notrap aligned table v91+176 - v93 = iconst.i32 1 - v94 = isub v92, v93 ; v93 = 1 - store notrap aligned table v94, v95+176 - v96 = iconst.i32 0xffff_3234 - v98 = load.i64 notrap aligned v97+104 - v129 = iconst.i64 0x0001_0000 - v99 = udiv v98, v129 ; v129 = 0x0001_0000 - v100 = ireduce.i32 v99 - v101 = iconst.i32 0x0001_0000 - v102 = imul v100, v101 ; v101 = 0x0001_0000 - v103 = iconst.i32 0x0005_ffee - v104 = iadd v103, v96 ; v103 = 0x0005_ffee, v96 = 0xffff_3234 - v105 = icmp ule v102, v104 - v106 = uextend.i32 v105 - brif v106, block9, block10 - -block10: - v107 = iconst.i32 0 - v108 = icmp.i32 sle v96, v107 ; v96 = 0xffff_3234, v107 = 0 - v109 = uextend.i32 v108 - brif v109, block9, block11 - -block11: - v110 ! range(64, 0x0, 0xffffffff) = uextend.i64 v96 ; v96 = 0xffff_3234 - v111 ! mem(mt1, 0x0, 0x0) = load.i64 notrap aligned readonly checked v130+96 - v112 ! mem(mt1, 0x0, 0xffffffff) = iadd v111, v110 - v113 ! range(64, 0x5ffe6, 0x5ffe6) = iconst.i64 0x0005_ffe6 - v114 ! mem(mt1, 0x5ffe6, 0x10005ffe5) = iadd v112, v113 ; v113 = 0x0005_ffe6 - v115 = uload8.i64 little checked heap v114 - jump block8(v115) - -block9: - v116 = iconst.i64 0 - jump block8(v116) ; v116 = 0 - -block8(v117: i64): - v118 = fcvt_from_uint.f32 v117 - v86 -> v118 - jump block5 - -block5: - v121 = iconst.i32 0 - v22 -> v121 - v122 = iconst.i32 0 - v23 -> v122 - v123 = iconst.i32 0 - v24 -> v123 - v124 = iconst.i32 0 - v25 -> v124 - v125 = iconst.i32 0 - v26 -> v125 - v126 = iconst.i32 0 - v27 -> v126 - jump block1 - -block1: - return v22, v23, v24, v25, v26, v27 ; v22 = 0, v23 = 0, v24 = 0, v25 = 0, v26 = 0, v27 = 0 -} diff --git a/cranelift/filetests/filetests/pcc/succeed/vmctx.clif b/cranelift/filetests/filetests/pcc/succeed/vmctx.clif deleted file mode 100644 index ec05f22fa6c0..000000000000 --- a/cranelift/filetests/filetests/pcc/succeed/vmctx.clif +++ /dev/null @@ -1,21 +0,0 @@ -test compile -set enable_pcc=true -target aarch64 -target x86_64 - -;; Equivalent to a Wasm `i64.load` from a static memory. -function %f0(i64, i32) -> i64 { - ;; mock vmctx struct: - mt0 = struct 8 { 0: i64 readonly ! mem(mt1, 0, 0) } - ;; mock static memory: 4GiB range, plus 2GiB guard - mt1 = memory 0x1_8000_0000 - -block0(v0 ! mem(mt0, 0, 0): i64, v1: i32): - ;; Compute the address: base + offset. Guard region (2GiB) is - ;; sufficient for an 8-byte I64 load. - v2 ! mem(mt1, 0, 0) = load.i64 checked v0+0 ;; base pointer - v3 ! range(64, 0, 0xffff_ffff) = uextend.i64 v1 ;; offset - v4 ! mem(mt1, 0, 0xffff_ffff) = iadd.i64 v2, v3 - v5 = load.i64 checked v4 - return v5 -} diff --git a/cranelift/filetests/filetests/runtests/arithmetic.clif b/cranelift/filetests/filetests/runtests/arithmetic.clif index 7c33293ed474..e20d73109912 100644 --- a/cranelift/filetests/filetests/runtests/arithmetic.clif +++ b/cranelift/filetests/filetests/runtests/arithmetic.clif @@ -632,3 +632,193 @@ block0(v0: i32): ; run: %ne_mul_odd(6) == 0 ; run: %ne_mul_odd(7) == 1 + +function %fold_iadd_iadd_ineg_i32_rt(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + v3 = ineg v1 + v4 = iadd v2, v3 + return v4 +} +; run: %fold_iadd_iadd_ineg_i32_rt(42, 5) == 42 +; run: %fold_iadd_iadd_ineg_i32_rt(-7, 9) == -7 +; run: %fold_iadd_iadd_ineg_i32_rt(0x7fffffff, 0x80000000) == 0x7fffffff + +function %test_isub_bor_band_to_bxor_rt(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = band v0, v1 + v4 = isub v2, v3 + return v4 +} +; run: %test_isub_bor_band_to_bxor_rt(4, 5) == 1 +; run: %test_isub_bor_band_to_bxor_rt(7, 7) == 0 +; run: %test_isub_bor_band_to_bxor_rt(-1, 1) == -2 + +function %fold_isub_iadd_band_to_bor_i32_rt(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + v3 = band v0, v1 + v4 = isub v2, v3 + return v4 +} +; run: %fold_isub_iadd_band_to_bor_i32_rt(4, 5) == 5 +; run: %fold_isub_iadd_band_to_bor_i32_rt(7, 7) == 7 +; run: %fold_isub_iadd_band_to_bor_i32_rt(-1, 1) == -1 + +function %fold_isub_bor_bxor_to_band_i32_rt(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bxor v0, v1 + v4 = isub v2, v3 + return v4 +} +; run: %fold_isub_bor_bxor_to_band_i32_rt(4, 5) == 4 +; run: %fold_isub_bor_bxor_to_band_i32_rt(7, 7) == 7 +; run: %fold_isub_bor_bxor_to_band_i32_rt(-1, 1) == 1 + +function %fold_iadd_bnot_x_to_allones_i32_rt(i32) -> i32 { +block0(v0: i32): + v1 = bnot v0 + v2 = iadd v1, v0 + return v2 +} +; run: %fold_iadd_bnot_x_to_allones_i32_rt(0) == -1 +; run: %fold_iadd_bnot_x_to_allones_i32_rt(1) == -1 +; run: %fold_iadd_bnot_x_to_allones_i32_rt(0x12345678) == -1 + +function %test_isub_iadd_iadd_cancel_rt(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = iadd v0, v1 + v4 = iadd v0, v2 + v5 = isub v3, v4 + return v5 +} + +; run: %test_isub_iadd_iadd_cancel_rt(0, 0, 0) == 0 +; run: %test_isub_iadd_iadd_cancel_rt(5, 7, 3) == 4 +; run: %test_isub_iadd_iadd_cancel_rt(10, 1, 9) == -8 + +function %test_isub_isub_isub_cancel_rt(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = isub v0, v2 + v4 = isub v1, v2 + v5 = isub v3, v4 + return v5 +} + +; run: %test_isub_isub_isub_cancel_rt(0, 0, 0) == 0 +; run: %test_isub_isub_isub_cancel_rt(7, 3, 1) == 4 +; run: %test_isub_isub_isub_cancel_rt(3, 7, 1) == -4 + +function %test_isub_isub_isub_swap_cancel_rt(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = isub v0, v1 + v4 = isub v0, v2 + v5 = isub v3, v4 + return v5 +} + +; run: %test_isub_isub_isub_swap_cancel_rt(0, 0, 0) == 0 +; run: %test_isub_isub_isub_swap_cancel_rt(7, 3, 1) == -2 +; run: %test_isub_isub_isub_swap_cancel_rt(7, 1, 3) == 2 + +function %test_iadd_umin_umax(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = umin v0, v1 + v3 = umax v0, v1 + v4 = iadd v2, v3 + return v4 +} + +; run: %test_iadd_umin_umax(1, 2) == 3 +; run: %test_iadd_umin_umax(2, 1) == 3 +; run: %test_iadd_umin_umax(7, 7) == 14 +; run: %test_iadd_umin_umax(-1, 1) == 0 + +function %test_iadd_smin_smax(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = smin v0, v1 + v3 = smax v0, v1 + v4 = iadd v2, v3 + return v4 +} + +; run: %test_iadd_smin_smax(1, 2) == 3 +; run: %test_iadd_smin_smax(2, 1) == 3 +; run: %test_iadd_smin_smax(7, 7) == 14 +; run: %test_iadd_smin_smax(-1, 1) == 0 + +function %test_isub_iadd_iadd_common_rhs_rt(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = iadd v0, v2 + v4 = iadd v1, v2 + v5 = isub v3, v4 + return v5 +} + +; run: %test_isub_iadd_iadd_common_rhs_rt(0, 0, 0) == 0 +; run: %test_isub_iadd_iadd_common_rhs_rt(7, 3, 1) == 4 +; run: %test_isub_iadd_iadd_common_rhs_rt(3, 7, 1) == -4 + +function %test_iadd_isub_iadd_cancel_rt(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = isub v0, v1 + v4 = iadd v1, v2 + v5 = iadd v3, v4 + return v5 +} + +; run: %test_iadd_isub_iadd_cancel_rt(0, 0, 0) == 0 +; run: %test_iadd_isub_iadd_cancel_rt(7, 3, 1) == 8 +; run: %test_iadd_isub_iadd_cancel_rt(7, 1, 3) == 10 + +function %test_isub_iadd_to_ineg_rt(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + v3 = isub v0, v2 + return v3 +} + +; run: %test_isub_iadd_to_ineg_rt(0, 0) == 0 +; run: %test_isub_iadd_to_ineg_rt(7, 3) == -3 +; run: %test_isub_iadd_to_ineg_rt(7, -3) == 3 + +function %test_iadd_iadd_isub_cancel_rt(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = isub v2, v0 + v4 = iadd v1, v3 + v5 = iadd v0, v4 + return v5 +} + +; run: %test_iadd_iadd_isub_cancel_rt(0, 0, 0) == 0 +; run: %test_iadd_iadd_isub_cancel_rt(7, 3, 1) == 4 +; run: %test_iadd_iadd_isub_cancel_rt(7, 1, 3) == 4 +; run: %test_iadd_iadd_isub_cancel_rt(-4, 6, 2) == 8 +; run: %test_iadd_iadd_isub_cancel_rt(0x1234, 0x34, 0x10) == 0x44 + +;; (eq ty (iadd cty x y) (iadd cty y x)) -> 1 +function %simplify_icmp_eq_iadd_commute(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = iadd v0, v1 + v3 = iadd v1, v0 + v4 = icmp eq v2, v3 + return v4 +} + +; run: %simplify_icmp_eq_iadd_commute(0, 0) == 1 +; run: %simplify_icmp_eq_iadd_commute(1, 2) == 1 +; run: %simplify_icmp_eq_iadd_commute(-1, 1) == 1 + +;; (x - y) != x --> y != 0 +function %simplify_icmp_ne_isub_x(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = isub v0, v1 + v3 = icmp ne v2, v0 + return v3 +} + +; run: %simplify_icmp_ne_isub_x(0, 0) == 0 +; run: %simplify_icmp_ne_isub_x(1, 0) == 0 +; run: %simplify_icmp_ne_isub_x(1, 1) == 1 \ No newline at end of file diff --git a/cranelift/filetests/filetests/runtests/bitops.clif b/cranelift/filetests/filetests/runtests/bitops.clif index b79dc40ada55..d7ea0a22e4e8 100644 --- a/cranelift/filetests/filetests/runtests/bitops.clif +++ b/cranelift/filetests/filetests/runtests/bitops.clif @@ -224,8 +224,8 @@ block0(v0: i32, v1: i32): function %test_bxor_band_band(i32, i32, i32) -> i32 fast { block0(v0: i32, v1: i32, v2: i32): - v3 = band v0, v1 - v4 = band v0, v2 + v3 = band v0, v1 + v4 = band v0, v2 v5 = bxor v3, v4 return v5 } @@ -257,4 +257,489 @@ block0(v0: i32, v1: i32): ; run: %test_iadd_bor_ineg(0, 0) == 0 ; run: %test_iadd_bor_ineg(1, 1) == 0 ; run: %test_iadd_bor_ineg(2, 1) == 2 -; run: %test_iadd_bor_ineg(0xffffffff, 0) == -1 \ No newline at end of file +; run: %test_iadd_bor_ineg(0xffffffff, 0) == -1 + +function %test_band_bor_absorb_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = band v2, v0 + return v3 +} + +; run: %test_band_bor_absorb_x(0, 0) == 0 +; run: %test_band_bor_absorb_x(1, 0) == 1 +; run: %test_band_bor_absorb_x(1, 1) == 1 +; run: %test_band_bor_absorb_x(2, 1) == 2 +; run: %test_band_bor_absorb_x(0xf0f0f0f0, 0x0f0f0f0f) == 0xf0f0f0f0 + +function %test_band_bor_bor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bor v0, v2 + v4 = bor v1, v2 + v5 = band v3, v4 + return v5 +} + +; run: %test_band_bor_bor(0, 0, 0) == 0 +; run: %test_band_bor_bor(1, 0, 0) == 0 +; run: %test_band_bor_bor(1, 0, 1) == 1 + +function %test_bor_band_band(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = band v0, v2 + v4 = band v1, v2 + v5 = bor v3, v4 + return v5 +} + +; run: %test_bor_band_band(0, 0, 0) == 0 +; run: %test_bor_band_band(1, 0, 0) == 0 +; run: %test_bor_band_band(1, 0, 1) == 1 +; run: %test_bor_band_band(2, 1, 1) == 1 +; run: %test_bor_band_band(0xf0, 0x0f, 0xaa) == 0xaa + +function %test_bxor_bor_bor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bnot v1 + v4 = bor v0, v3 + v5 = bxor v2, v4 + return v5 +} + +; run: %test_bxor_bor_bor_bnot(0, 0) == 0xffffffff +; run: %test_bxor_bor_bor_bnot(1, 0) == 0xfffffffe +; run: %test_bxor_bor_bor_bnot(1, 1) == 0xfffffffe +; run: %test_bxor_bor_bor_bnot(0x12345678, 0x9abcdef0) == 0xedcba987 +; run: %test_bxor_bor_bor_bnot(0xffffffff, 0) == 0 + + +function %test_band_bnot_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bnot v1 + v4 = band v2, v3 + return v4 +} + +; run: %test_band_bnot_bnot(0, 0) == 0xffffffff +; run: %test_band_bnot_bnot(1, 0) == 0xfffffffe +; run: %test_band_bnot_bnot(1, 1) == 0xfffffffe + +function %test_bor_bnot_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bnot v1 + v4 = bor v2, v3 + return v4 +} + +; run: %test_bor_bnot_bnot(0, 0) == 0xffffffff +; run: %test_bor_bnot_bnot(1, 0) == 0xffffffff +; run: %test_bor_bnot_bnot(1, 1) == 0xfffffffe + +function %test_bor_bxor_bxor_x(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v1 + v4 = bxor v3, v2 + v5 = bor v4, v0 + return v5 +} + +; run: %test_bor_bxor_bxor_x(0, 0, 0) == 0 +; run: %test_bor_bxor_bxor_x(1, 0, 0) == 1 +; run: %test_bor_bxor_bxor_x(1, 1, 0) == 1 + +function %simplify_icmp_eq_bxor_shared_rhs(i32, i32, i32) -> i8 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v2 + v4 = bxor v1, v2 + v5 = icmp eq v3, v4 + return v5 +} + +; run: %simplify_icmp_eq_bxor_shared_rhs(0, 0, 0) == 1 +; run: %simplify_icmp_eq_bxor_shared_rhs(1, 1, 7) == 1 +; run: %simplify_icmp_eq_bxor_shared_rhs(1, 2, 7) == 0 + +function %test_bor_bnot_bor_absorb(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bnot v2 + v4 = bor v1, v3 + return v4 +} + +; run: %test_bor_bnot_bor_absorb(0, 0) == 0xffffffff +; run: %test_bor_bnot_bor_absorb(1, 0) == 0xfffffffe + +function %test_band_bnot_band_absorb(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = band v0, v1 + v3 = bnot v2 + v4 = band v1, v3 + return v4 +} + +; run: %test_band_bnot_band_absorb(0, 0) == 0 +; run: %test_band_bnot_band_absorb(1, 0) == 0 +; run: %test_band_bnot_band_absorb(0, 1) == 1 + +function %test_bxor_bnot_bor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bor v0, v1 + v4 = bxor v2, v3 + return v4 +} + +; run: %test_bxor_bnot_bor(0, 0) == 0xffffffff +; run: %test_bxor_bnot_bor(1, 0) == 0xffffffff + +;; (bor ty (slt ty x y) (sgt ty x y)) -> (ne ty x y) +function %test_bor_slt_sgt(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp slt v0, v1 + v3 = icmp sgt v0, v1 + v4 = bor v2, v3 + return v4 +} + +; run: %test_bor_slt_sgt(1, 1) == 0 +; run: %test_bor_slt_sgt(1, 3) == 1 +; run: %test_bor_slt_sgt(3, 1) == 1 + +;; (bor ty (ult ty x y) (ugt ty x y)) -> (ne ty x y) +function %test_bor_ult_ugt(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp ult v0, v1 + v3 = icmp ugt v0, v1 + v4 = bor v2, v3 + return v4 +} + +; run: %test_bor_ult_ugt(1, 1) == 0 +; run: %test_bor_ult_ugt(1, 3) == 1 +; run: %test_bor_ult_ugt(3, 1) == 1 + +function %test_bnot_band_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = band v2, v1 + v4 = bnot v3 + return v4 +} + +; run: %test_bnot_band_bnot(0, 0) == 0xffffffff +; run: %test_bnot_band_bnot(1, 0) == 0xffffffff +; run: %test_bnot_band_bnot(0, 1) == 0xfffffffe + +function %test_bnot_bor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bor v2, v1 + v4 = bnot v3 + return v4 +} + +; run: %test_bnot_bor_bnot(0, 0) == 0 +; run: %test_bnot_bor_bnot(1, 0) == 1 +; run: %test_bnot_bor_bnot(0, 1) == 0 + +;; (bor ty (ult ty x y) (eq ty x y)) -> (ule ty x y) +function %test_bor_ult_eq(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp ult v0, v1 + v3 = icmp eq v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_ult_eq(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp ule v0, v1 +; return v5 +; } + +;; (bor ty (ugt ty y x) (eq ty x y)) -> (ule ty x y) +function %test_bor_ugt_eq_swap(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp ugt v1, v0 + v3 = icmp eq v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_ugt_eq_swap(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp ule v0, v1 +; return v5 +; } + +;; (bor ty (slt ty x y) (eq ty x y)) -> (sle ty x y) +function %test_bor_slt_eq(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp slt v0, v1 + v3 = icmp eq v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_slt_eq(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp sle v0, v1 +; return v5 +; } + +;; (bor ty (sgt ty y x) (eq ty x y)) -> (sle ty x y) +function %test_bor_sgt_eq_swap(i32, i32) -> i8 fast { +block0(v0: i32, v1: i32): + v2 = icmp sgt v1, v0 + v3 = icmp eq v0, v1 + v4 = bor v2, v3 + return v4 +} + +; function %test_bor_sgt_eq_swap(i32, i32) -> i8 fast { +; block0(v0: i32, v1: i32): +; v5 = icmp sle v0, v1 +; return v5 +; } + +function %test_bor_bxor_bxor_chain(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v1 + v4 = bxor v3, v2 + v5 = bor v3, v4 + return v5 +} + +; run: %test_bor_bxor_bxor_chain(0, 0, 0) == 0 +; run: %test_bor_bxor_bxor_chain(1, 0, 0) == 1 +; run: %test_bor_bxor_bxor_chain(1, 1, 0) == 0 + +function %test_bor_bxor_self(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bor v0, v2 + return v3 +} + +; run: %test_bor_bxor_self(0, 0) == 0 +; run: %test_bor_bxor_self(1, 0) == 1 +; run: %test_bor_bxor_self(1, 1) == 1 +; run: %test_bor_bxor_self(0xf0, 0x0f) == 0xff + +function %test_bnot_iadd_bnot_to_isub_rt(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = iadd v2, v1 + v4 = bnot v3 + return v4 +} + +; run: %test_bnot_iadd_bnot_to_isub_rt(0, 0) == 0 +; run: %test_bnot_iadd_bnot_to_isub_rt(4, 5) == -1 +; run: %test_bnot_iadd_bnot_to_isub_rt(0x12345678, 0x11111111) == 0x01234567 + +function %test_bor_bxor_bor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v2 + v4 = bor v1, v0 + v5 = bor v3, v4 + return v5 +} + +; run: %test_bor_bxor_bor(0, 0, 0) == 0 +; run: %test_bor_bxor_bor(1, 0, 0) == 1 +; run: %test_bor_bxor_bor(0xf0, 0x0f, 0xaa) == 0xff + +function %test_bxor_bor_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bxor v0, v1 + v4 = bxor v2, v3 + return v4 +} + +; run: %test_bxor_bor_bxor(0, 0) == 0 +; run: %test_bxor_bor_bxor(1, 0) == 0 +; run: %test_bxor_bor_bxor(0x12345678, 0x9abcdef0) == 0x12345670 + +function %test_bor_band_bxor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v1 + v4 = band v3, v2 + v5 = bor v4, v0 + return v5 +} + +; run: %test_bor_band_bxor(0, 0, 0) == 0 +; run: %test_bor_band_bxor(1, 0, 0) == 1 +; run: %test_bor_band_bxor(1, 1, 1) == 1 +; run: %test_bor_band_bxor(2, 1, 1) == 3 +; run: %test_bor_band_bxor(0xf0, 0x0f, 0xaa) == 0xfa + +function %test_bor_bor_bxor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bor v0, v1 + v4 = bxor v0, v2 + v5 = bor v3, v4 + return v5 +} + +; run: %test_bor_bor_bxor(0, 0, 0) == 0 +; run: %test_bor_bor_bxor(1, 0, 0) == 1 +; run: %test_bor_bor_bxor(1, 1, 0) == 1 +; run: %test_bor_bor_bxor(1, 0, 1) == 1 +; run: %test_bor_bor_bxor(0xf0, 0x0f, 0xaa) == 0xff + +;; (ne ty (bxor cty x z) (bxor cty y z)) -> (ne ty x y) +function %simplify_icmp_ne_bxor_shared_rhs(i32, i32, i32) -> i8 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v2 + v4 = bxor v1, v2 + v5 = icmp ne v3, v4 + return v5 +} + +; run: %simplify_icmp_ne_bxor_shared_rhs(0, 0, 0) == 0 +; run: %simplify_icmp_ne_bxor_shared_rhs(1, 1, 7) == 0 +; run: %simplify_icmp_ne_bxor_shared_rhs(0x12345678, 0x87654321, 0x9abcdef0) == 1 + +function %test_bor_band_band_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = band v0, v1 + v3 = bnot v1 + v4 = band v0, v3 + v5 = bor v2, v4 + return v5 +} + +; run: %test_bor_band_band_bnot(0, 0) == 0 +; run: %test_bor_band_band_bnot(1, 0) == 1 +; run: %test_bor_band_band_bnot(0xf0, 0x0f) == 0xf0 + +function %test_band_bor_bor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bnot v1 + v4 = bor v0, v3 + v5 = band v2, v4 + return v5 +} + +; run: %test_band_bor_bor_bnot(0, 0) == 0 +; run: %test_band_bor_bor_bnot(1, 0) == 1 +; run: %test_band_bor_bor_bnot(0xf0, 0x0f) == 0xf0 + +function %test_bor_bxor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bnot v0 + v4 = bor v2, v3 + return v4 +} + +; run: %test_bor_bxor_bnot(1, 1) == 0xfffffffe +; run: %test_bor_bxor_bnot(0xf0, 0x0f) == 0xffffffff +; run: %test_bor_bxor_bnot(0x12345678, 0x9abcdef0) == 0xedcba98f + +function %test_bxor_bxor_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bxor v2, v0 + return v3 +} + +; run: %test_bxor_bxor_x(0, 0) == 0 +; run: %test_bxor_bxor_x(1, 0) == 0 +; run: %test_bxor_bxor_x(1, 1) == 1 + +function %test_bor_band_bor(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bor v1, v2 + v4 = band v0, v3 + v5 = bor v4, v1 + return v5 +} + +; run: %test_bor_band_bor(0, 0, 0) == 0 +; run: %test_bor_band_bor(1, 0, 0) == 0 +; run: %test_bor_band_bor(1, 1, 0) == 1 +; run: %test_bor_band_bor(2, 1, 1) == 1 +; run: %test_bor_band_bor(0xf0, 0x0f, 0xaa) == 0xaf + +function %test_band_bor_band(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = band v1, v2 + v4 = bor v0, v3 + v5 = band v4, v1 + return v5 +} + +; run: %test_band_bor_band(0, 0, 0) == 0 +; run: %test_band_bor_band(1, 0, 0) == 0 +; run: %test_band_bor_band(1, 1, 0) == 1 +; run: %test_band_bor_band(2, 1, 1) == 1 +; run: %test_band_bor_band(0xf0, 0x0f, 0xaa) == 0x0a + +function %test_bor_bxor_band(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = band v1, v2 + v4 = bxor v0, v3 + v5 = bor v4, v1 + return v5 +} + +; run: %test_bor_bxor_band(0, 0, 0) == 0 +; run: %test_bor_bxor_band(1, 0, 0) == 1 +; run: %test_bor_bxor_band(2, 1, 1) == 3 +; run: %test_bor_bxor_band(0xf0, 0x0f, 0xaa) == 0xff + +function %test_iadd_bxor_band_to_isub_rt(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = band v0, v1 + v4 = bxor v3, v1 + v5 = iadd v4, v2 + return v5 +} + +; run: %test_iadd_bxor_band_to_isub_rt(0, 0, 0) == 0 +; run: %test_iadd_bxor_band_to_isub_rt(2, 1, 3) == 4 +; run: %test_iadd_bxor_band_to_isub_rt(0xf0, 0x0f, 0xaa) == 0xb9 + +function %test_band_bor_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = bor v2, v0 + v4 = band v1, v3 + return v4 +} + +; run: %test_band_bor_bnot(0, 0) == 0 +; run: %test_band_bor_bnot(1, 0) == 0 +; run: %test_band_bor_bnot(0xf0, 0x0f) == 0 + +function %test_bor_band_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v2, v0 + v4 = bor v1, v3 + return v4 +} + +; run: %test_bor_band_bnot(0, 0) == 0 +; run: %test_bor_band_bnot(1, 0) == 1 +; run: %test_bor_band_bnot(0xf0, 0x0f) == 0xff + +function %test_aarch64_extr(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = ishl_imm v0, 32 + v3 = ushr_imm v1, 0 + v4 = bor v2, v3 + return v4 +} + +; run: %test_aarch64_extr(1, 0) == 1 +; run: %test_aarch64_extr(0, 1) == 1 diff --git a/cranelift/filetests/filetests/runtests/fold-bitops.clif b/cranelift/filetests/filetests/runtests/fold-bitops.clif index 03806b2896fa..4ecc459df692 100644 --- a/cranelift/filetests/filetests/runtests/fold-bitops.clif +++ b/cranelift/filetests/filetests/runtests/fold-bitops.clif @@ -63,4 +63,230 @@ block0(v0: i32, v1: i32): } ; run: %simplify_bor_xyy(1, 2) == 3 -; run: %simplify_bor_xyy(54, 9) == 63 \ No newline at end of file +; run: %simplify_bor_xyy(54, 9) == 63 + + +function %test_isub_band_band_bnot_rt(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v0, v2 + v4 = band v0, v1 + v5 = isub v3, v4 + return v5 +} + +; run: %test_isub_band_band_bnot_rt(0, 0) == 0 +; run: %test_isub_band_band_bnot_rt(1, 0) == 1 +; run: %test_isub_band_band_bnot_rt(1, 1) == -1 + +function %test_bor_band_bnot_to_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v0, v2 + v4 = bnot v0 + v5 = band v4, v1 + v6 = bor v3, v5 + return v6 +} + +; run: %test_bor_band_bnot_to_bxor(0, 0) == 0 +; run: %test_bor_band_bnot_to_bxor(1, 0) == 1 +; run: %test_bor_band_bnot_to_bxor(0, 1) == 1 + +function %test_bor_band_bnot_bxor_absorb(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v0, v2 + v4 = bxor v0, v1 + v5 = bor v3, v4 + return v5 +} + +; run: %test_bor_band_bnot_bxor_absorb(0, 0) == 0 +; run: %test_bor_band_bnot_bxor_absorb(1, 0) == 1 +; run: %test_bor_band_bnot_bxor_absorb(0, 1) == 1 + +function %test_bxor_band_bnot_to_bnot_band(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v1 + v3 = band v0, v2 + v4 = bnot v0 + v5 = bxor v3, v4 + return v5 +} + +; run: %test_bxor_band_bnot_to_bnot_band(0, 0) == 0xffffffff +; run: %test_bxor_band_bnot_to_bnot_band(1, 0) == 0xffffffff +; run: %test_bxor_band_bnot_to_bnot_band(1, 1) == 0xfffffffe + +function %test_bxor_band_bnot_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = band v2, v1 + v4 = bxor v3, v0 + return v4 +} + +; run: %test_bxor_band_bnot_x(0, 0) == 0 +; run: %test_bxor_band_bnot_x(1, 0) == 1 +; run: %test_bxor_band_bnot_x(0, 1) == 1 + +function %test_band_bor_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bxor v0, v1 + v4 = bnot v3 + v5 = band v2, v4 + return v5 +} + +; run: %test_band_bor_bnot_bxor(0, 0) == 0 +; run: %test_band_bor_bnot_bxor(1, 0) == 0 +; run: %test_band_bor_bnot_bxor(0, 1) == 0 +; run: %test_band_bor_bnot_bxor(1, 1) == 1 + +function %test_bor_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bnot v2 + v4 = bor v0, v3 + return v4 +} + +; run: %test_bor_bnot_bxor(0, 0) == 0xffffffff +; run: %test_bor_bnot_bxor(1, 0) == 0xffffffff + +function %test_bor_bxor_bnot_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bxor v2, v1 + v4 = bor v0, v3 + return v4 +} + +; run: %test_bor_bxor_bnot_x(0, 0) == 0xffffffff +; run: %test_bor_bxor_bnot_x(1, 0) == 0xffffffff +; run: %test_bor_bxor_bnot_x(0, 1) == 0xfffffffe + +function %test_band_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bxor v0, v1 + v3 = bnot v2 + v4 = band v0, v3 + return v4 +} + +; run: %test_band_bnot_bxor(0, 0) == 0 +; run: %test_band_bnot_bxor(1, 0) == 0 +; run: %test_band_bnot_bxor(0, 1) == 0 + +function %test_band_bxor_bnot_x(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bxor v2, v1 + v4 = band v0, v3 + return v4 +} + +; run: %test_band_bxor_bnot_x(0, 0) == 0 +; run: %test_band_bxor_bnot_x(1, 0) == 0 +; run: %test_band_bxor_bnot_x(0, 1) == 0 + +function %test_bor_bor_bxor_same(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bor v0, v1 + v3 = bxor v0, v1 + v4 = bor v2, v3 + return v4 +} + +; run: %test_bor_bor_bxor_same(0, 0) == 0 +; run: %test_bor_bor_bxor_same(1, 0) == 1 +; run: %test_bor_bor_bxor_same(0, 1) == 1 + +function %test_band_bxor_bxor_bnot(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bxor v0, v1 + v4 = bxor v1, v2 + v5 = bxor v4, v0 + v6 = band v3, v5 + return v6 +} + +; run: %test_band_bxor_bxor_bnot(0, 0, 0) == 0 +; run: %test_band_bxor_bxor_bnot(1, 0, 0) == 1 +; run: %test_band_bxor_bxor_bnot(1, 0, 1) == 0 + +function %test_bxor_band_bnot_general(i32, i32, i32) -> i32 fast { +block0(v0: i32, v1: i32, v2: i32): + v3 = bnot v0 + v4 = band v3, v1 + v5 = bxor v4, v2 + return v5 +} + +; run: %test_bxor_band_bnot_general(0, 0, 0) == 0 +; run: %test_bxor_band_bnot_general(1, 0, 0) == 0 +; run: %test_bxor_band_bnot_general(0, 1, 0) == 1 + +function %test_isub_bnot_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bnot v1 + v4 = isub v2, v3 + return v4 +} + +; run: %test_isub_bnot_bnot(0, 0) == 0 +; run: %test_isub_bnot_bnot(1, 0) == -1 +; run: %test_isub_bnot_bnot(0, 1) == 1 + +function %test_bor_band_bnot_bor_to_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = band v2, v1 + v4 = bor v0, v1 + v5 = bnot v4 + v6 = bor v3, v5 + return v6 +} + +; run: %test_bor_band_bnot_bor_to_bnot(0, 0) == 0xffffffff +; run: %test_bor_band_bnot_bor_to_bnot(1, 0) == 0xfffffffe + +function %test_band_bor_bnot_band_to_bnot(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bor v2, v1 + v4 = band v0, v1 + v5 = bnot v4 + v6 = band v3, v5 + return v6 +} + +; run: %test_band_bor_bnot_band_to_bnot(0, 0) == 0xffffffff +; run: %test_band_bor_bnot_band_to_bnot(1, 0) == 0xfffffffe + +function %test_bor_band_bnot_bor_to_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = band v0, v1 + v3 = bor v0, v1 + v4 = bnot v3 + v5 = bor v2, v4 + return v5 +} + +; run: %test_bor_band_bnot_bor_to_bnot_bxor(0, 0) == 0xffffffff +; run: %test_bor_band_bnot_bor_to_bnot_bxor(1, 0) == 0xfffffffe + +function %test_bxor_bor_bnot_bxor(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = bnot v0 + v3 = bor v2, v1 + v4 = bxor v0, v1 + v5 = bxor v3, v4 + return v5 +} + +; run: %test_bxor_bor_bnot_bxor(0, 0) == 0xffffffff +; run: %test_bxor_bor_bnot_bxor(1, 0) == 0xffffffff \ No newline at end of file diff --git a/cranelift/filetests/filetests/runtests/i128-arithmetic-extends.clif b/cranelift/filetests/filetests/runtests/i128-arithmetic-extends.clif index 8e339797a79d..4c42bc9bb550 100644 --- a/cranelift/filetests/filetests/runtests/i128-arithmetic-extends.clif +++ b/cranelift/filetests/filetests/runtests/i128-arithmetic-extends.clif @@ -1,6 +1,7 @@ test interpret test run set enable_llvm_abi_extensions=true +set enable_multi_ret_implicit_sret target aarch64 target s390x target x86_64 @@ -17,3 +18,13 @@ block0(v0: i32, v1: i128): ; run: %sext_sshr_i32_i128(0x8000_0000, 0) == 0xFFFFFFFF80000000 ; run: %sext_sshr_i32_i128(0x8000_0000, 32) == 0xFFFFFFFF80000000 ; run: %sext_sshr_i32_i128(0x8000_0000, 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFF20) == 0xFFFFFFFF80000000 + +function %fold_iadd_bnot_x_to_allones_i128_rt(i128) -> i128 { +block0(v0: i128): + v1 = bnot v0 + v2 = iadd v1, v0 + return v2 +} +; run: %fold_iadd_bnot_x_to_allones_i128_rt(0) == -1 +; run: %fold_iadd_bnot_x_to_allones_i128_rt(1) == -1 +; run: %fold_iadd_bnot_x_to_allones_i128_rt(0x12345678_9ABCDEF0_11111111_22222222) == -1 diff --git a/cranelift/filetests/filetests/runtests/rotr.clif b/cranelift/filetests/filetests/runtests/rotr.clif index 2065f0220ef5..f23152dfd4d7 100644 --- a/cranelift/filetests/filetests/runtests/rotr.clif +++ b/cranelift/filetests/filetests/runtests/rotr.clif @@ -231,6 +231,30 @@ block0(v0: i8, v1: i8): ; run: %rotr_i8_i8(0xe0, 65) == 0x70 ; run: %rotr_i8_i8(0xe0, 66) == 0x38 +function %rotr_iconst_select_iconsts_i8(i8) -> i8 { +block0(v0: i8): + v1 = iconst.i8 1 + v2 = iconst.i8 1 + v3 = iconst.i8 2 + v4 = select v0, v2, v3 + v5 = rotr.i8 v1, v4 + return v5 +} +; run: %rotr_iconst_select_iconsts_i8(0) == 0x40 +; run: %rotr_iconst_select_iconsts_i8(1) == 0x80 + +function %rotr_iconst_select_iconsts_i32(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 1 + v2 = iconst.i32 1 + v3 = iconst.i32 2 + v4 = select v0, v2, v3 + v5 = rotr.i32 v1, v4 + return v5 +} +; run: %rotr_iconst_select_iconsts_i32(0) == 0x40000000 +; run: %rotr_iconst_select_iconsts_i32(1) == 0x80000000 + ;; This is a regression test for rotates on x64 diff --git a/cranelift/filetests/filetests/runtests/select.clif b/cranelift/filetests/filetests/runtests/select.clif index 4b6c121c6e7d..d15c3b2d8117 100644 --- a/cranelift/filetests/filetests/runtests/select.clif +++ b/cranelift/filetests/filetests/runtests/select.clif @@ -462,3 +462,106 @@ block0(v0: i32, v1: i32): ; run: %test_ugt_select_ult_min(234234, 5544) == 0 +function %select_eq_xy_xy_to_y_rt(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = icmp eq v0, v1 + v3 = select v2, v0, v1 + return v3 +} +; run: %select_eq_xy_xy_to_y_rt(42, 35) == 35 +; run: %select_eq_xy_xy_to_y_rt(42, 42) == 42 + +function %select_bor_ne_to_y_rt(i8, i8, i8) -> i8 fast { +block0(v0: i8, v1: i8, v2: i8): + v3 = icmp ne v1, v2 + v4 = bor v0, v3 + v5 = select v4, v1, v2 + return v5 +} +; run: %select_bor_ne_to_y_rt(0, 7, 7) == 7 +; run: %select_bor_ne_to_y_rt(0, 7, 9) == 7 +; run: %select_bor_ne_to_y_rt(1, 7, 7) == 7 + +function %select_eq_xy_x_borxy_to_borxy_rt(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = icmp eq v0, v1 + v3 = bor v0, v1 + v4 = select v2, v0, v3 + return v4 +} +; run: %select_eq_xy_x_borxy_to_borxy_rt(7, 7) == 7 +; run: %select_eq_xy_x_borxy_to_borxy_rt(6, 10) == 14 +; run: %select_eq_xy_x_borxy_to_borxy_rt(0, 5) == 5 + +function %select_ne_xy_bandxy_y_to_bandxy_rt(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = icmp ne v0, v1 + v3 = band v0, v1 + v4 = select v2, v3, v1 + return v4 +} +; run: %select_ne_xy_bandxy_y_to_bandxy_rt(6, 10) == 2 +; run: %select_ne_xy_bandxy_y_to_bandxy_rt(7, 7) == 7 +; run: %select_ne_xy_bandxy_y_to_bandxy_rt(0, 5) == 0 + +function %select_ult_one_eq_to_ule_rt(i8, i8) -> i8 fast { +block0(v0: i8, v1: i8): + v2 = icmp ult v0, v1 + v3 = iconst.i8 1 + v4 = icmp eq v0, v1 + v5 = select v2, v3, v4 + return v5 +} +; run: %select_ult_one_eq_to_ule_rt(1, 2) == 1 +; run: %select_ult_one_eq_to_ule_rt(2, 2) == 1 +; run: %select_ult_one_eq_to_ule_rt(3, 2) == 0 + +function %select_slt_one_sgt_to_ne_rt(i8, i8) -> i8 fast { +block0(v0: i8, v1: i8): + v2 = icmp slt v0, v1 + v3 = iconst.i8 1 + v4 = icmp sgt v0, v1 + v5 = select v2, v3, v4 + return v5 +} +; run: %select_slt_one_sgt_to_ne_rt(1, 2) == 1 +; run: %select_slt_one_sgt_to_ne_rt(2, 1) == 1 +; run: %select_slt_one_sgt_to_ne_rt(2, 2) == 0 + +function %select_x_one_borxy_to_select_x_one_y_rt(i8, i8) -> i8 fast { +block0(v0: i8, v1: i8): + v2 = iconst.i8 1 + v3 = bor v0, v1 + v4 = select v0, v2, v3 + return v4 +} +; run: %select_x_one_borxy_to_select_x_one_y_rt(0, 5) == 5 +; run: %select_x_one_borxy_to_select_x_one_y_rt(1, 5) == 1 +; run: %select_x_one_borxy_to_select_x_one_y_rt(2, 5) == 1 + +function %select_eq_xn_yminusn_yminusx_to_yminusx_rt(i32, i32) -> i32 fast { +block0(v0: i32, v1: i32): + v2 = iconst.i32 7 + v3 = icmp eq v0, v2 + v4 = isub v1, v2 + v5 = isub v1, v0 + v6 = select v3, v4, v5 + return v6 +} +; run: %select_eq_xn_yminusn_yminusx_to_yminusx_rt(7, 10) == 3 +; run: %select_eq_xn_yminusn_yminusx_to_yminusx_rt(3, 10) == 7 +; run: %select_eq_xn_yminusn_yminusx_to_yminusx_rt(0, 10) == 10 + +function %select_eq_eq_to_eq_select_rt(i8, i32, i32) -> i8 fast { +block0(v0: i8, v1: i32, v2: i32): + v3 = icmp eq v2, v1 + v4 = iconst.i32 7 + v5 = icmp eq v4, v1 + v6 = select v0, v3, v5 + return v6 +} +; run: %select_eq_eq_to_eq_select_rt(0, 5, 5) == 0 +; run: %select_eq_eq_to_eq_select_rt(0, 7, 5) == 1 +; run: %select_eq_eq_to_eq_select_rt(1, 5, 5) == 1 +; run: %select_eq_eq_to_eq_select_rt(1, 5, 7) == 0 + diff --git a/cranelift/filetests/filetests/runtests/simd-splat.clif b/cranelift/filetests/filetests/runtests/simd-splat.clif index f1ad9224aec4..8e953921243d 100644 --- a/cranelift/filetests/filetests/runtests/simd-splat.clif +++ b/cranelift/filetests/filetests/runtests/simd-splat.clif @@ -212,3 +212,13 @@ block0(v0: f64): ; run: %load_splat_f64x2(0x0.0) == [0x0.0 0x0.0] ; run: %load_splat_f64x2(0x2.0) == [0x2.0 0x2.0] ; run: %load_splat_f64x2(NaN) == [NaN NaN] + +function %splat_ireduce() -> i32 { +block0: + v0 = iconst.i64 0x000000FF_00FFFF00 + v1 = ireduce.i32 v0 + v2 = splat.i32x4 v1 + v3 = extractlane v2, 1 + return v3 +} +; run: %splat_ireduce() == 0x00ffff00 diff --git a/cranelift/filetests/filetests/runtests/uadd_overflow.clif b/cranelift/filetests/filetests/runtests/uadd_overflow.clif index 1ab73adf4721..7488d6129a6f 100644 --- a/cranelift/filetests/filetests/runtests/uadd_overflow.clif +++ b/cranelift/filetests/filetests/runtests/uadd_overflow.clif @@ -40,3 +40,4 @@ block0(v0: i32, v1: i32): ; run: %uaddof_i32(100, 28) == [128, 0] ; run: %uaddof_i32(3000000000, 1294967295) == [-1, 0] ; run: %uaddof_i32(3000000000, 1294967296) == [0, 1] +; run: %uaddof_i32(0x80000001, -1) == [0x80000000, 1] diff --git a/cranelift/filetests/filetests/verifier/exceptions.clif b/cranelift/filetests/filetests/verifier/exceptions.clif index f2e97b774058..7566f015b23e 100644 --- a/cranelift/filetests/filetests/verifier/exceptions.clif +++ b/cranelift/filetests/filetests/verifier/exceptions.clif @@ -150,7 +150,7 @@ function %f5() windows_fastcall { return } -;; Out-of-bounds `exnN` paylaod +;; Out-of-bounds `exnN` payload function %f5() { sig0 = () tail fn0 = %g() tail diff --git a/cranelift/filetests/src/function_runner.rs b/cranelift/filetests/src/function_runner.rs index 115081eb5960..17d34e608cac 100644 --- a/cranelift/filetests/src/function_runner.rs +++ b/cranelift/filetests/src/function_runner.rs @@ -446,7 +446,7 @@ impl<'a> Trampoline<'a> { | Architecture::Pulley64 | Architecture::Pulley32be | Architecture::Pulley64be => { - let mut state = pulley::Vm::new(); + let mut state = pulley::Vm::new().unwrap(); unsafe { state.call( NonNull::new(trampoline_ptr.cast_mut()).unwrap(), diff --git a/cranelift/frontend/Cargo.toml b/cranelift/frontend/Cargo.toml index 6efe2702dc1c..e602b3ebdaeb 100644 --- a/cranelift/frontend/Cargo.toml +++ b/cranelift/frontend/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-frontend" -version = "0.130.0" +version = "0.131.2" description = "Cranelift IR builder helper" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-frontend" diff --git a/cranelift/frontend/src/frontend/safepoints.rs b/cranelift/frontend/src/frontend/safepoints.rs index d089677ee7c1..40027b1f8df7 100644 --- a/cranelift/frontend/src/frontend/safepoints.rs +++ b/cranelift/frontend/src/frontend/safepoints.rs @@ -795,6 +795,7 @@ impl SafepointSpiller { mod tests { use super::*; use alloc::string::ToString; + use cranelift_codegen::ir::{BlockCall, ExceptionTableData}; use cranelift_codegen::isa::CallConv; #[test] @@ -2769,4 +2770,110 @@ block10: "#, ); } + + #[test] + fn needs_stack_map_across_try_call() { + let _ = env_logger::try_init(); + + // Test that values needing stack maps get stack_map entries on + // try_call instructions, not just regular call instructions. + // + // block0: + // v0 = call fn0() ;; returns a gc ref + // v1 = call fn0() ;; returns another gc ref + // try_call fn0(), sig0, block1(), [default: block2()] + // ;; v0 and v1 should be in the stack map here + // block1: + // call fn1(v0) ;; uses v0, v1 is dead + // return + // block2(v2: i64): + // return + + let sig = Signature::new(CallConv::SystemV); + + let mut fn_ctx = FunctionBuilderContext::new(); + let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig); + let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx); + + // fn0: () -> i32 (returns a gc ref) + let name0 = builder + .func + .declare_imported_user_function(ir::UserExternalName { + namespace: 0, + index: 0, + }); + let mut sig0 = Signature::new(CallConv::SystemV); + sig0.returns.push(AbiParam::new(ir::types::I32)); + let signature0 = builder.func.import_signature(sig0); + let func_ref0 = builder.import_function(ir::ExtFuncData { + name: ir::ExternalName::user(name0), + signature: signature0, + colocated: true, + patchable: false, + }); + + // fn1: (i32) -> () (consumes a gc ref) + let name1 = builder + .func + .declare_imported_user_function(ir::UserExternalName { + namespace: 0, + index: 1, + }); + let mut sig1 = Signature::new(CallConv::SystemV); + sig1.params.push(AbiParam::new(ir::types::I32)); + let signature1 = builder.func.import_signature(sig1); + let func_ref1 = builder.import_function(ir::ExtFuncData { + name: ir::ExternalName::user(name1), + signature: signature1, + colocated: true, + patchable: false, + }); + + let block0 = builder.create_block(); + let block1 = builder.create_block(); + let block2 = builder.create_block(); + + builder.switch_to_block(block0); + + // v0 = call fn0() -- a gc ref that's live across the try_call + let call0 = builder.ins().call(func_ref0, &[]); + let v0 = builder.func.dfg.inst_results(call0)[0]; + builder.declare_value_needs_stack_map(v0); + + // v1 = call fn0() -- another gc ref, also live across the try_call + let call1 = builder.ins().call(func_ref0, &[]); + let v1 = builder.func.dfg.inst_results(call1)[0]; + builder.declare_value_needs_stack_map(v1); + + // try_call fn0() -> block1, exception -> block2 + let normal_return = BlockCall::new(block1, [], &mut builder.func.dfg.value_lists); + let exception_table = builder + .func + .dfg + .exception_tables + .push(ExceptionTableData::new(signature0, normal_return, [])); + builder.ins().try_call(func_ref0, &[], exception_table); + + // block1: use v0 (so it's live across the try_call) + builder.switch_to_block(block1); + builder.ins().call(func_ref1, &[v0]); + builder.ins().return_(&[]); + + // block2: exception handler + builder.switch_to_block(block2); + builder.ins().return_(&[]); + + builder.seal_all_blocks(); + builder.finalize(); + + // The try_call should have a stack_map with v0 (and v1 should NOT + // be in it since v1 is not used after the try_call). + // The second call (which produces v1) should have v0 in its stack_map + // since v0 is live across that call too. + let output = func.display().to_string(); + assert!( + output.contains("try_call fn0(), sig0, block1, [], stack_map=[i32 @ ss0+0]"), + "try_call should have stack_map entry for v0 (spilled to ss0), got:\n{output}" + ); + } } diff --git a/cranelift/interpreter/Cargo.toml b/cranelift/interpreter/Cargo.toml index 66b9343dedf2..122e3e27c017 100644 --- a/cranelift/interpreter/Cargo.toml +++ b/cranelift/interpreter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-interpreter" -version = "0.130.0" +version = "0.131.2" authors = ["The Cranelift Project Developers"] description = "Interpret Cranelift IR" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/isle/isle/Cargo.toml b/cranelift/isle/isle/Cargo.toml index c2003ba3f9b4..5fdcc72aeae6 100644 --- a/cranelift/isle/isle/Cargo.toml +++ b/cranelift/isle/isle/Cargo.toml @@ -7,7 +7,7 @@ license = "Apache-2.0 WITH LLVM-exception" name = "cranelift-isle" readme = "../README.md" repository = "https://github.com/bytecodealliance/wasmtime/tree/main/cranelift/isle" -version = "0.130.0" +version = "0.131.2" [lints] workspace = true diff --git a/cranelift/isle/isle/src/codegen.rs b/cranelift/isle/isle/src/codegen.rs index 3d1cb1089ac3..4427972dc4aa 100644 --- a/cranelift/isle/isle/src/codegen.rs +++ b/cranelift/isle/isle/src/codegen.rs @@ -2,7 +2,7 @@ use crate::files::Files; use crate::sema::{ - BuiltinType, ExternalSig, ReturnKind, Term, TermEnv, TermId, Type, TypeEnv, TypeId, + BuiltinType, ExternalSig, IntType, ReturnKind, Term, TermEnv, TermId, Type, TypeEnv, TypeId, }; use crate::serialize::{Block, ControlFlow, EvalStep, MatchArm}; use crate::stablemapset::StableSet; @@ -20,7 +20,7 @@ pub struct CodegenOptions { /// source. Useful if it must be include!()'d elsewhere. pub exclude_global_allow_pragmas: bool, - /// Prefixes to remove when printing file names in generaed files. This + /// Prefixes to remove when printing file names in generated files. This /// helps keep codegen deterministic. pub prefixes: Vec, @@ -1068,9 +1068,55 @@ impl Length for ContextIterWrapper {{ ) -> Result<(), std::fmt::Error> { let ty_data = &self.typeenv.types[ty.index()]; match ty_data { - Type::Builtin(BuiltinType::Int(ty)) if ty.is_signed() => write!(ctx.out, "{val}_{ty}"), - Type::Builtin(BuiltinType::Int(ty)) => write!(ctx.out, "{val:#x}_{ty}"), + Type::Builtin(BuiltinType::Int(ty)) => { + write!(ctx.out, "{}", rust_int_literal(*ty, val)) + } _ => write!(ctx.out, "{val:#x}"), } } } + +fn rust_int_literal(ty: IntType, val: i128) -> String { + match ty { + IntType::U8 => format!("{:#x}_u8", val as u8), + IntType::U16 => format!("{:#x}_u16", val as u16), + IntType::U32 => format!("{:#x}_u32", val as u32), + IntType::U64 => format!("{:#x}_u64", val as u64), + IntType::U128 => format!("{:#x}_u128", val as u128), + IntType::USize => format!("{val:#x}_usize"), + IntType::I8 => format!("{}_i8", val as i8), + IntType::I16 => format!("{}_i16", val as i16), + IntType::I32 => format!("{}_i32", val as i32), + IntType::I64 => format!("{}_i64", val as i64), + IntType::I128 => format!("{val}_i128"), + IntType::ISize => format!("{val}_isize"), + } +} + +#[cfg(test)] +mod tests { + use super::rust_int_literal; + use crate::sema::IntType; + + #[test] + fn formats_wrapped_unsigned_literals() { + assert_eq!(rust_int_literal(IntType::U8, -2), "0xfe_u8"); + assert_eq!(rust_int_literal(IntType::U64, -1), "0xffffffffffffffff_u64"); + assert_eq!( + rust_int_literal(IntType::U128, -1), + "0xffffffffffffffffffffffffffffffff_u128" + ); + } + + #[test] + fn formats_wrapped_signed_literals() { + assert_eq!(rust_int_literal(IntType::I8, 255), "-1_i8"); + assert_eq!(rust_int_literal(IntType::I64, -1), "-1_i64"); + assert_eq!(rust_int_literal(IntType::I16, 65535), "-1_i16"); + } + + #[test] + fn preserves_positive_unsigned_literals() { + assert_eq!(rust_int_literal(IntType::U64, 5), "0x5_u64"); + } +} diff --git a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_with_imm_rotl_to_rotr.isle b/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_with_imm_rotl_to_rotr.isle index 834665a54f81..fd1ddf64b5ed 100644 --- a/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_with_imm_rotl_to_rotr.isle +++ b/cranelift/isle/veri/veri_engine/examples/broken/broken_fits_in_16_with_imm_rotl_to_rotr.isle @@ -96,7 +96,7 @@ ;; ImmShift::maybe_from_u64(imm_value) ;; } -;; Add an assertion that the value fits, using the constand with 56 1's then 6 0's (~63) +;; Add an assertion that the value fits, using the constant with 56 1's then 6 0's (~63) (spec (imm_shift_from_imm64 ty x) (provide (= result (extract 5 0 (bvand x (bvsub (int2bv 64 ty) #x0000000000000001))))) (require (bvult (bvand x (bvsub (int2bv 64 ty) #x0000000000000001)) #x0000000000000040))) diff --git a/cranelift/isle/veri/veri_engine/examples/x86/amode_add_shl.isle b/cranelift/isle/veri/veri_engine/examples/x86/amode_add_shl.isle index 94e56c767d2a..990c4d275ac3 100644 --- a/cranelift/isle/veri/veri_engine/examples/x86/amode_add_shl.isle +++ b/cranelift/isle/veri/veri_engine/examples/x86/amode_add_shl.isle @@ -57,7 +57,7 @@ (extern constructor gpr_new gpr_new) (convert Reg Gpr gpr_new) -;; To make this case study specific to Wasm, contrain to 32 or 64 +;; To make this case study specific to Wasm, constrain to 32 or 64 (spec (uextend arg) (provide (= result (zero_ext (widthof result) arg))) (require diff --git a/cranelift/isle/veri/veri_engine/examples/x86/amode_add_uextend_shl.isle b/cranelift/isle/veri/veri_engine/examples/x86/amode_add_uextend_shl.isle index 5e8aa8652bbf..1b4c5f253858 100644 --- a/cranelift/isle/veri/veri_engine/examples/x86/amode_add_uextend_shl.isle +++ b/cranelift/isle/veri/veri_engine/examples/x86/amode_add_uextend_shl.isle @@ -61,7 +61,7 @@ (extern constructor gpr_new gpr_new) (convert Reg Gpr gpr_new) -;; To make this case study specific to Wasm, contrain the widths of +;; To make this case study specific to Wasm, constrain the widths of ;; the argument and returned value to 32 or 64 (spec (uextend arg) (provide (= result (zero_ext (widthof result) arg))) diff --git a/cranelift/isle/veri/veri_engine/src/annotations.rs b/cranelift/isle/veri/veri_engine/src/annotations.rs index 6b6c3823f01c..f7a70886aced 100644 --- a/cranelift/isle/veri/veri_engine/src/annotations.rs +++ b/cranelift/isle/veri/veri_engine/src/annotations.rs @@ -235,7 +235,7 @@ fn spec_op_to_expr(s: &SpecOp, args: &[SpecExpr], pos: &Pos, env: &ParsingEnv) - args.len() > 1, "Unexpected number of args for switch operator {pos:?}", ); - let swith_on = spec_to_expr(&args[0], env); + let switch_on = spec_to_expr(&args[0], env); let arms: Vec<(Expr, Expr)> = args[1..] .iter() .map(|a| match a { @@ -247,7 +247,7 @@ fn spec_op_to_expr(s: &SpecOp, args: &[SpecExpr], pos: &Pos, env: &ParsingEnv) - _ => unreachable!(), }) .collect(); - Expr::Switch(Box::new(swith_on), arms) + Expr::Switch(Box::new(switch_on), arms) } SpecOp::LoadEffect => { assert_eq!( diff --git a/cranelift/isle/veri/veri_engine/src/solver.rs b/cranelift/isle/veri/veri_engine/src/solver.rs index c66340007782..39dcb9df4a3f 100644 --- a/cranelift/isle/veri/veri_engine/src/solver.rs +++ b/cranelift/isle/veri/veri_engine/src/solver.rs @@ -1180,7 +1180,7 @@ impl SolverCtx { load_ret } else { if self.rhs_load_args.is_some() { - panic!("Only one load on the RHS currently supported, found miltiple.") + panic!("Only one load on the RHS currently supported, found multiple.") } self.rhs_load_args = Some(vec![ex, ey, ez]); self.load_return.unwrap() @@ -1218,7 +1218,7 @@ impl SolverCtx { self.smt.push().unwrap(); for (i, a) in assumptions.iter().enumerate() { self.smt - .assert(self.smt.named(format!("assum{i}"), *a)) + .assert(self.smt.named(format!("assume{i}"), *a)) .unwrap(); } @@ -1784,7 +1784,7 @@ fn resolve_dynamic_widths( } // Otherwise, try again, but adding the assertion that some width is - // different than our current assigment + // different than our current assignment let not_equals = width_resolutions.iter().map(|(s, w)| { ctx.smt.not( ctx.smt diff --git a/cranelift/jit/Cargo.toml b/cranelift/jit/Cargo.toml index 4fc6e79a2d37..db16eefa6eb6 100644 --- a/cranelift/jit/Cargo.toml +++ b/cranelift/jit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-jit" -version = "0.130.0" +version = "0.131.2" authors = ["The Cranelift Project Developers"] description = "A JIT library backed by Cranelift" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/module/Cargo.toml b/cranelift/module/Cargo.toml index 6157146cef84..e31d81f13c5c 100644 --- a/cranelift/module/Cargo.toml +++ b/cranelift/module/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-module" -version = "0.130.0" +version = "0.131.2" authors = ["The Cranelift Project Developers"] description = "Support for linking functions and data with Cranelift" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/native/Cargo.toml b/cranelift/native/Cargo.toml index db3eebf90a02..c4ee7bff8208 100644 --- a/cranelift/native/Cargo.toml +++ b/cranelift/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-native" -version = "0.130.0" +version = "0.131.2" authors = ["The Cranelift Project Developers"] description = "Support for targeting the host with Cranelift" documentation = "https://docs.rs/cranelift-native" diff --git a/cranelift/object/Cargo.toml b/cranelift/object/Cargo.toml index 37b5c9a330db..52c8a20b2371 100644 --- a/cranelift/object/Cargo.toml +++ b/cranelift/object/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-object" -version = "0.130.0" +version = "0.131.2" authors = ["The Cranelift Project Developers"] description = "Emit Cranelift output to native object files with `object`" repository = "https://github.com/bytecodealliance/wasmtime" @@ -25,4 +25,4 @@ log = { workspace = true } [dev-dependencies] cranelift-frontend = { workspace = true } cranelift-entity = { workspace = true } -cranelift-codegen = { workspace = true, features = ["x86"] } +cranelift-codegen = { workspace = true, features = ["x86", "arm64"] } diff --git a/cranelift/object/src/backend.rs b/cranelift/object/src/backend.rs index 0d0eaefb7b8b..3821a1c223f7 100644 --- a/cranelift/object/src/backend.rs +++ b/cranelift/object/src/backend.rs @@ -847,6 +847,28 @@ impl ObjectModule { }, _ => unimplemented!("Aarch64Ld64GotLo12Nc is not supported for this file format"), }, + Reloc::Aarch64AdrPrelPgHi21 => match self.object.format() { + object::BinaryFormat::Elf => RelocationFlags::Elf { + r_type: object::elf::R_AARCH64_ADR_PREL_PG_HI21, + }, + object::BinaryFormat::MachO => RelocationFlags::MachO { + r_type: object::macho::ARM64_RELOC_PAGE21, + r_pcrel: true, + r_length: 2, + }, + _ => unimplemented!("Aarch64AdrPrelPgHi21 is not supported for this file format"), + }, + Reloc::Aarch64AddAbsLo12Nc => match self.object.format() { + object::BinaryFormat::Elf => RelocationFlags::Elf { + r_type: object::elf::R_AARCH64_ADD_ABS_LO12_NC, + }, + object::BinaryFormat::MachO => RelocationFlags::MachO { + r_type: object::macho::ARM64_RELOC_PAGEOFF12, + r_pcrel: false, + r_length: 2, + }, + _ => unimplemented!("Aarch64AddAbsLo12Nc is not supported for this file format"), + }, Reloc::S390xPCRel32Dbl => RelocationFlags::Generic { kind: RelocationKind::Relative, encoding: RelocationEncoding::S390xDbl, diff --git a/cranelift/object/tests/basic.rs b/cranelift/object/tests/basic.rs index 89f05faaa208..bdc03886a474 100644 --- a/cranelift/object/tests/basic.rs +++ b/cranelift/object/tests/basic.rs @@ -258,3 +258,54 @@ fn reject_nul_byte_symbol_for_data() { ) .unwrap(); } + +#[test] +fn aarch64_colocated_data_symbol_reloc() { + let flag_builder = settings::builder(); + let isa_builder = cranelift_codegen::isa::lookup_by_name("aarch64-unknown-linux-gnu").unwrap(); + let isa = isa_builder + .finish(settings::Flags::new(flag_builder)) + .unwrap(); + let mut module = + ObjectModule::new(ObjectBuilder::new(isa, "test", default_libcall_names()).unwrap()); + + let data_id = module + .declare_data("my_data", Linkage::Local, true, false) + .unwrap(); + + let mut data_desc = DataDescription::new(); + data_desc.define_zeroinit(64); + module.define_data(data_id, &data_desc).unwrap(); + + let sig = Signature { + params: vec![], + returns: vec![AbiParam::new(types::I64)], + call_conv: CallConv::SystemV, + }; + + let func_id = module + .declare_function("load_data_addr", Linkage::Local, &sig) + .unwrap(); + + let mut ctx = Context::new(); + ctx.func = Function::with_name_signature(UserFuncName::user(0, func_id.as_u32()), sig); + let mut func_ctx = FunctionBuilderContext::new(); + { + let mut bcx: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); + let block = bcx.create_block(); + bcx.switch_to_block(block); + + let gv = module.declare_data_in_func(data_id, &mut bcx.func); + let ptr = module.target_config().pointer_type(); + let addr = bcx.ins().global_value(ptr, gv); + bcx.ins().return_(&[addr]); + + bcx.seal_all_blocks(); + bcx.finalize(); + } + + module.define_function(func_id, &mut ctx).unwrap(); + + let product = module.finish(); + product.emit().expect("emit object file"); +} diff --git a/cranelift/reader/Cargo.toml b/cranelift/reader/Cargo.toml index f8381c253b71..dce4952d7fc4 100644 --- a/cranelift/reader/Cargo.toml +++ b/cranelift/reader/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-reader" -version = "0.130.0" +version = "0.131.2" description = "Cranelift textual IR reader" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-reader" diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 99ee3ad41852..bbd2d08828d7 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -9,21 +9,19 @@ use crate::testcommand::TestCommand; use crate::testfile::{Comment, Details, Feature, TestFile}; use cranelift_codegen::data_value::DataValue; use cranelift_codegen::entity::{EntityRef, PrimaryMap}; -use cranelift_codegen::ir::entities::{AnyEntity, DynamicType, MemoryType}; +use cranelift_codegen::ir::entities::{AnyEntity, DynamicType}; use cranelift_codegen::ir::immediates::{ Ieee16, Ieee32, Ieee64, Ieee128, Imm64, Offset32, Uimm32, Uimm64, }; use cranelift_codegen::ir::instructions::{InstructionData, InstructionFormat, VariableArgs}; -use cranelift_codegen::ir::pcc::{BaseExpr, Expr, Fact}; use cranelift_codegen::ir::{self, StackSlotKey, UserExternalNameRef}; use cranelift_codegen::ir::{DebugTag, types::*}; use cranelift_codegen::ir::{ AbiParam, ArgumentExtension, ArgumentPurpose, Block, BlockArg, Constant, ConstantData, DynamicStackSlot, DynamicStackSlotData, DynamicTypeData, ExtFuncData, ExternalName, FuncRef, - Function, GlobalValue, GlobalValueData, JumpTableData, MemFlags, MemoryTypeData, - MemoryTypeField, Opcode, SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, - UserFuncName, Value, types, + Function, GlobalValue, GlobalValueData, JumpTableData, MemFlags, Opcode, SigRef, Signature, + StackSlot, StackSlotData, StackSlotKind, UserFuncName, Value, types, }; use cranelift_codegen::isa::{self, CallConv}; use cranelift_codegen::packed_option::ReservedValue; @@ -309,13 +307,7 @@ impl Context { } // Allocate a global value slot. - fn add_gv( - &mut self, - gv: GlobalValue, - data: GlobalValueData, - maybe_fact: Option, - loc: Location, - ) -> ParseResult<()> { + fn add_gv(&mut self, gv: GlobalValue, data: GlobalValueData, loc: Location) -> ParseResult<()> { self.map.def_gv(gv, loc)?; while self.function.global_values.next_key().index() <= gv.index() { self.function.create_global_value(GlobalValueData::Symbol { @@ -326,19 +318,6 @@ impl Context { }); } self.function.global_values[gv] = data; - if let Some(fact) = maybe_fact { - self.function.global_value_facts[gv] = Some(fact); - } - Ok(()) - } - - // Allocate a memory-type slot. - fn add_mt(&mut self, mt: MemoryType, data: MemoryTypeData, loc: Location) -> ParseResult<()> { - self.map.def_mt(mt, loc)?; - while self.function.memory_types.next_key().index() <= mt.index() { - self.function.create_memory_type(MemoryTypeData::default()); - } - self.function.memory_types[mt] = data; Ok(()) } @@ -646,17 +625,6 @@ impl<'a> Parser<'a> { err!(self.loc, err_msg) } - // Match and consume a memory-type reference. - fn match_mt(&mut self, err_msg: &str) -> ParseResult { - if let Some(Token::MemoryType(mt)) = self.token() { - self.consume(); - if let Some(mt) = MemoryType::with_number(mt) { - return Ok(mt); - } - } - err!(self.loc, err_msg) - } - // Match and consume a constant reference. fn match_constant(&mut self) -> ParseResult { if let Some(Token::Constant(c)) = self.token() { @@ -1497,12 +1465,7 @@ impl<'a> Parser<'a> { Some(Token::GlobalValue(..)) => { self.start_gathering_comments(); self.parse_global_value_decl() - .and_then(|(gv, dat, maybe_fact)| ctx.add_gv(gv, dat, maybe_fact, self.loc)) - } - Some(Token::MemoryType(..)) => { - self.start_gathering_comments(); - self.parse_memory_type_decl() - .and_then(|(mt, dat)| ctx.add_mt(mt, dat, self.loc)) + .and_then(|(gv, dat)| ctx.add_gv(gv, dat, self.loc)) } Some(Token::SigRef(..)) => { self.start_gathering_comments(); @@ -1635,25 +1598,16 @@ impl<'a> Parser<'a> { // Parse a global value decl. // - // global-val-decl ::= * GlobalValue(gv) [ "!" fact ] "=" global-val-desc + // global-val-decl ::= * GlobalValue(gv) "=" global-val-desc // global-val-desc ::= "vmctx" // | "load" "." type "notrap" "aligned" GlobalValue(base) [offset] // | "iadd_imm" "(" GlobalValue(base) ")" imm64 // | "symbol" ["colocated"] name + imm64 // | "dyn_scale_target_const" "." type // - fn parse_global_value_decl( - &mut self, - ) -> ParseResult<(GlobalValue, GlobalValueData, Option)> { + fn parse_global_value_decl(&mut self) -> ParseResult<(GlobalValue, GlobalValueData)> { let gv = self.match_gv("expected global value number: gv«n»")?; - let fact = if self.token() == Some(Token::Bang) { - self.consume(); - Some(self.parse_fact()?) - } else { - None - }; - self.match_token(Token::Equal, "expected '=' in global value declaration")?; let data = match self.match_any_identifier("expected global value kind")? { @@ -1724,114 +1678,7 @@ impl<'a> Parser<'a> { self.token(); self.claim_gathered_comments(gv); - Ok((gv, data, fact)) - } - - // Parse one field definition in a memory-type struct decl. - // - // memory-type-field ::= offset ":" type ["readonly"] [ "!" fact ] - // offset ::= uimm64 - fn parse_memory_type_field(&mut self) -> ParseResult { - let offset: u64 = self - .match_uimm64( - "expected u64 constant value for field offset in struct memory-type declaration", - )? - .into(); - self.match_token( - Token::Colon, - "expected colon after field offset in struct memory-type declaration", - )?; - let ty = self.match_type("expected type for field in struct memory-type declaration")?; - let readonly = if self.token() == Some(Token::Identifier("readonly")) { - self.consume(); - true - } else { - false - }; - let fact = if self.token() == Some(Token::Bang) { - self.consume(); - let fact = self.parse_fact()?; - Some(fact) - } else { - None - }; - Ok(MemoryTypeField { - offset, - ty, - readonly, - fact, - }) - } - - // Parse a memory-type decl. - // - // memory-type-decl ::= MemoryType(mt) "=" memory-type-desc - // memory-type-desc ::= "struct" size "{" memory-type-field,* "}" - // | "memory" size - // | "dynamic_memory" GlobalValue "+" offset - // | "empty" - // size ::= uimm64 - // offset ::= uimm64 - fn parse_memory_type_decl(&mut self) -> ParseResult<(MemoryType, MemoryTypeData)> { - let mt = self.match_mt("expected memory type number: mt«n»")?; - self.match_token(Token::Equal, "expected '=' in memory type declaration")?; - - let data = match self.token() { - Some(Token::Identifier("struct")) => { - self.consume(); - let size: u64 = self.match_uimm64("expected u64 constant value for struct size in struct memory-type declaration")?.into(); - self.match_token(Token::LBrace, "expected opening brace to start struct fields in struct memory-type declaration")?; - let mut fields = vec![]; - while self.token() != Some(Token::RBrace) { - let field = self.parse_memory_type_field()?; - fields.push(field); - if self.token() == Some(Token::Comma) { - self.consume(); - } else { - break; - } - } - self.match_token( - Token::RBrace, - "expected closing brace after struct fields in struct memory-type declaration", - )?; - MemoryTypeData::Struct { size, fields } - } - Some(Token::Identifier("memory")) => { - self.consume(); - let size: u64 = self.match_uimm64("expected u64 constant value for size in static-memory memory-type declaration")?.into(); - MemoryTypeData::Memory { size } - } - Some(Token::Identifier("dynamic_memory")) => { - self.consume(); - let gv = self.match_gv( - "expected a global value for `dynamic_memory` memory-type declaration", - )?; - self.match_token( - Token::Plus, - "expected `+` after global value in `dynamic_memory` memory-type declaration", - )?; - let size: u64 = self.match_uimm64("expected u64 constant value for size offset in `dynamic_memory` memory-type declaration")?.into(); - MemoryTypeData::DynamicMemory { gv, size } - } - Some(Token::Identifier("empty")) => { - self.consume(); - MemoryTypeData::Empty - } - other => { - return err!( - self.loc, - "Unknown memory type declaration kind '{:?}'", - other - ); - } - }; - - // Collect any trailing comments. - self.token(); - self.claim_gathered_comments(mt); - - Ok((mt, data)) + Ok((gv, data)) } // Parse a signature decl. @@ -2160,7 +2007,7 @@ impl<'a> Parser<'a> { // between the parsing of value aliases and the parsing of instructions. // // inst-results ::= Value(v) { "," Value(v) } - let results = self.parse_inst_results(ctx)?; + let results = self.parse_inst_results()?; for result in &results { while ctx.function.dfg.num_values() <= result.index() { @@ -2215,24 +2062,15 @@ impl<'a> Parser<'a> { // Parse a single block parameter declaration, and append it to `block`. // - // block-param ::= * Value(v) [ "!" fact ] ":" Type(t) arg-loc? + // block-param ::= * Value(v) ":" Type(t) arg-loc? // arg-loc ::= "[" value-location "]" // fn parse_block_param(&mut self, ctx: &mut Context, block: Block) -> ParseResult<()> { - // block-param ::= * Value(v) [ "!" fact ] ":" Type(t) arg-loc? + // block-param ::= * Value(v) ":" Type(t) arg-loc? let v = self.match_value("block argument must be a value")?; let v_location = self.loc; - // block-param ::= Value(v) * [ "!" fact ] ":" Type(t) arg-loc? - let fact = if self.token() == Some(Token::Bang) { - self.consume(); - // block-param ::= Value(v) [ "!" * fact ] ":" Type(t) arg-loc? - Some(self.parse_fact()?) - } else { - None - }; self.match_token(Token::Colon, "expected ':' after block argument")?; - // block-param ::= Value(v) [ "!" fact ] ":" * Type(t) arg-loc? - + // block-param ::= Value(v) ":" * Type(t) arg-loc? while ctx.function.dfg.num_values() <= v.index() { ctx.function.dfg.make_invalid_value_for_parser(); } @@ -2241,246 +2079,15 @@ impl<'a> Parser<'a> { // Allocate the block argument. ctx.function.dfg.append_block_param_for_parser(block, t, v); ctx.map.def_value(v, v_location)?; - ctx.function.dfg.facts[v] = fact; Ok(()) } - // Parse a "fact" for proof-carrying code, attached to a value. - // - // fact ::= "range" "(" bit-width "," min-value "," max-value ")" - // | "dynamic_range" "(" bit-width "," expr "," expr ")" - // | "mem" "(" memory-type "," mt-offset "," mt-offset [ "," "nullable" ] ")" - // | "dynamic_mem" "(" memory-type "," expr "," expr [ "," "nullable" ] ")" - // | "conflict" - // bit-width ::= uimm64 - // min-value ::= uimm64 - // max-value ::= uimm64 - // valid-range ::= uimm64 - // mt-offset ::= uimm64 - fn parse_fact(&mut self) -> ParseResult { - match self.token() { - Some(Token::Identifier("range")) => { - self.consume(); - self.match_token(Token::LPar, "`range` fact needs an opening `(`")?; - let bit_width: u64 = self - .match_uimm64("expected a bit-width value for `range` fact")? - .into(); - self.match_token(Token::Comma, "expected a comma")?; - let min: u64 = self - .match_uimm64("expected a min value for `range` fact")? - .into(); - self.match_token(Token::Comma, "expected a comma")?; - let max: u64 = self - .match_uimm64("expected a max value for `range` fact")? - .into(); - self.match_token(Token::RPar, "`range` fact needs a closing `)`")?; - let bit_width_max = match bit_width { - x if x > 64 => { - return Err(self.error("bitwidth must be <= 64 bits on a `range` fact")); - } - 64 => u64::MAX, - x => (1u64 << x) - 1, - }; - if min > max { - return Err(self.error( - "min value must be less than or equal to max value on a `range` fact", - )); - } - if max > bit_width_max { - return Err( - self.error("max value is out of range for bitwidth on a `range` fact") - ); - } - Ok(Fact::Range { - bit_width: u16::try_from(bit_width).unwrap(), - min, - max, - }) - } - Some(Token::Identifier("dynamic_range")) => { - self.consume(); - self.match_token(Token::LPar, "`dynamic_range` fact needs an opening `(`")?; - let bit_width: u64 = self - .match_uimm64("expected a bit-width value for `dynamic_range` fact")? - .into(); - self.match_token(Token::Comma, "expected a comma")?; - let min = self.parse_expr()?; - self.match_token(Token::Comma, "expected a comma")?; - let max = self.parse_expr()?; - self.match_token(Token::RPar, "`dynamic_range` fact needs a closing `)`")?; - Ok(Fact::DynamicRange { - bit_width: u16::try_from(bit_width).unwrap(), - min, - max, - }) - } - Some(Token::Identifier("mem")) => { - self.consume(); - self.match_token(Token::LPar, "expected a `(`")?; - let ty = self.match_mt("expected a memory type for `mem` fact")?; - self.match_token( - Token::Comma, - "expected a comma after memory type in `mem` fact", - )?; - let min_offset: u64 = self - .match_uimm64("expected a uimm64 minimum pointer offset for `mem` fact")? - .into(); - self.match_token(Token::Comma, "expected a comma after offset in `mem` fact")?; - let max_offset: u64 = self - .match_uimm64("expected a uimm64 maximum pointer offset for `mem` fact")? - .into(); - let nullable = if self.token() == Some(Token::Comma) { - self.consume(); - self.match_token( - Token::Identifier("nullable"), - "expected `nullable` in last optional field of `dynamic_mem`", - )?; - true - } else { - false - }; - self.match_token(Token::RPar, "expected a `)`")?; - Ok(Fact::Mem { - ty, - min_offset, - max_offset, - nullable, - }) - } - Some(Token::Identifier("dynamic_mem")) => { - self.consume(); - self.match_token(Token::LPar, "expected a `(`")?; - let ty = self.match_mt("expected a memory type for `dynamic_mem` fact")?; - self.match_token( - Token::Comma, - "expected a comma after memory type in `dynamic_mem` fact", - )?; - let min = self.parse_expr()?; - self.match_token( - Token::Comma, - "expected a comma after offset in `dynamic_mem` fact", - )?; - let max = self.parse_expr()?; - let nullable = if self.token() == Some(Token::Comma) { - self.consume(); - self.match_token( - Token::Identifier("nullable"), - "expected `nullable` in last optional field of `dynamic_mem`", - )?; - true - } else { - false - }; - self.match_token(Token::RPar, "expected a `)`")?; - Ok(Fact::DynamicMem { - ty, - min, - max, - nullable, - }) - } - Some(Token::Identifier("def")) => { - self.consume(); - self.match_token(Token::LPar, "expected a `(`")?; - let value = self.match_value("expected a value number in `def` fact")?; - self.match_token(Token::RPar, "expected a `)`")?; - Ok(Fact::Def { value }) - } - Some(Token::Identifier("compare")) => { - self.consume(); - self.match_token(Token::LPar, "expected a `(`")?; - let kind = self.match_enum("expected intcc condition code in `compare` fact")?; - self.match_token( - Token::Comma, - "expected comma in `compare` fact after condition code", - )?; - let lhs = self.parse_expr()?; - self.match_token(Token::Comma, "expected comma in `compare` fact after LHS")?; - let rhs = self.parse_expr()?; - self.match_token(Token::RPar, "expected a `)`")?; - Ok(Fact::Compare { kind, lhs, rhs }) - } - Some(Token::Identifier("conflict")) => { - self.consume(); - Ok(Fact::Conflict) - } - _ => Err(self.error( - "expected a `range`, 'dynamic_range', `mem`, `dynamic_mem`, `def`, `compare` or `conflict` fact", - )), - } - } - - // Parse a dynamic expression used in some kinds of PCC facts. - // - // expr ::= base-expr - // | base-expr + uimm64 // but in-range for imm64 - // | base-expr - uimm64 // but in-range for imm64 - // | imm64 - fn parse_expr(&mut self) -> ParseResult { - if let Some(Token::Integer(_)) = self.token() { - let offset: i64 = self - .match_imm64("expected imm64 for dynamic expression")? - .into(); - Ok(Expr { - base: BaseExpr::None, - offset, - }) - } else { - let base = self.parse_base_expr()?; - match self.token() { - Some(Token::Plus) => { - self.consume(); - let offset: u64 = self - .match_uimm64( - "expected uimm64 in imm64 range for offset in dynamic expression", - )? - .into(); - let offset: i64 = i64::try_from(offset).map_err(|_| { - self.error("integer offset in dynamic expression is out of range") - })?; - Ok(Expr { base, offset }) - } - Some(Token::Integer(x)) if x.starts_with("-") => { - let offset: i64 = self - .match_imm64("expected an imm64 range for offset in dynamic expression")? - .into(); - Ok(Expr { base, offset }) - } - _ => Ok(Expr { base, offset: 0 }), - } - } - } - - // Parse the base part of a dynamic expression, used in some PCC facts. - // - // base-expr ::= GlobalValue(base) - // | Value(base) - // | "max" - // | (epsilon) - fn parse_base_expr(&mut self) -> ParseResult { - match self.token() { - Some(Token::Identifier("max")) => { - self.consume(); - Ok(BaseExpr::Max) - } - Some(Token::GlobalValue(..)) => { - let gv = self.match_gv("expected global value")?; - Ok(BaseExpr::GlobalValue(gv)) - } - Some(Token::Value(..)) => { - let value = self.match_value("expected value")?; - Ok(BaseExpr::Value(value)) - } - _ => Ok(BaseExpr::None), - } - } - // Parse instruction results and return them. // // inst-results ::= Value(v) { "," Value(v) } // - fn parse_inst_results(&mut self, ctx: &mut Context) -> ParseResult> { + fn parse_inst_results(&mut self) -> ParseResult> { // Result value numbers. let mut results = SmallVec::new(); @@ -2491,29 +2098,11 @@ impl<'a> Parser<'a> { results.push(v); - let fact = if self.token() == Some(Token::Bang) { - self.consume(); - // block-param ::= Value(v) [ "!" * fact ] ":" Type(t) arg-loc? - Some(self.parse_fact()?) - } else { - None - }; - ctx.function.dfg.facts[v] = fact; - // inst-results ::= Value(v) * { "," Value(v) } while self.optional(Token::Comma) { // inst-results ::= Value(v) { "," * Value(v) } let v = self.match_value("expected result value")?; results.push(v); - - let fact = if self.token() == Some(Token::Bang) { - self.consume(); - // block-param ::= Value(v) [ "!" * fact ] ":" Type(t) arg-loc? - Some(self.parse_fact()?) - } else { - None - }; - ctx.function.dfg.facts[v] = fact; } } diff --git a/cranelift/reader/src/sourcemap.rs b/cranelift/reader/src/sourcemap.rs index 2c79de5ea471..38c578c40135 100644 --- a/cranelift/reader/src/sourcemap.rs +++ b/cranelift/reader/src/sourcemap.rs @@ -10,8 +10,7 @@ use crate::error::{Location, ParseResult}; use crate::lexer::split_entity_name; use cranelift_codegen::ir::entities::{AnyEntity, DynamicType}; use cranelift_codegen::ir::{ - Block, Constant, DynamicStackSlot, FuncRef, GlobalValue, JumpTable, MemoryType, SigRef, - StackSlot, Value, + Block, Constant, DynamicStackSlot, FuncRef, GlobalValue, JumpTable, SigRef, StackSlot, Value, }; use std::collections::HashMap; @@ -170,11 +169,6 @@ impl SourceMap { self.def_entity(entity.into(), loc) } - /// Define the memory type `entity`. - pub fn def_mt(&mut self, entity: MemoryType, loc: Location) -> ParseResult<()> { - self.def_entity(entity.into(), loc) - } - /// Define the signature `entity`. pub fn def_sig(&mut self, entity: SigRef, loc: Location) -> ParseResult<()> { self.def_entity(entity.into(), loc) diff --git a/cranelift/serde/Cargo.toml b/cranelift/serde/Cargo.toml index 5903a71d2cdc..445a590ca3d2 100644 --- a/cranelift/serde/Cargo.toml +++ b/cranelift/serde/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-serde" -version = "0.130.0" +version = "0.131.2" authors = ["The Cranelift Project Developers"] description = "Serializer/Deserializer for Cranelift IR" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/srcgen/Cargo.toml b/cranelift/srcgen/Cargo.toml index 54340a810adc..cfddd6b6b91f 100644 --- a/cranelift/srcgen/Cargo.toml +++ b/cranelift/srcgen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-srcgen" -version = "0.130.0" +version = "0.131.2" authors = ["The Wasmtime Project Developers"] description = "Helper functions for generating Rust and ISLE files" license = "Apache-2.0 WITH LLVM-exception" diff --git a/cranelift/umbrella/Cargo.toml b/cranelift/umbrella/Cargo.toml index 0553e9e1838d..84df4d1dbdb9 100644 --- a/cranelift/umbrella/Cargo.toml +++ b/cranelift/umbrella/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift" -version = "0.130.0" +version = "0.131.2" description = "Umbrella for commonly-used cranelift crates" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift" diff --git a/crates/c-api/CMakeLists.txt b/crates/c-api/CMakeLists.txt index 3ca05cd48cb5..a6e0bd01641f 100644 --- a/crates/c-api/CMakeLists.txt +++ b/crates/c-api/CMakeLists.txt @@ -32,7 +32,13 @@ else() endif() endif() -set(WASMTIME_TARGET_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_TARGET}/${WASMTIME_BUILD_TYPE}) +# Respect CARGO_TARGET_DIR if set, allowing users to customize where Cargo +# outputs build artifacts +if(DEFINED ENV{CARGO_TARGET_DIR}) + set(WASMTIME_TARGET_DIR $ENV{CARGO_TARGET_DIR}/${WASMTIME_TARGET}/${WASMTIME_BUILD_TYPE}) +else() + set(WASMTIME_TARGET_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_TARGET}/${WASMTIME_BUILD_TYPE}) +endif() if(WASMTIME_TARGET MATCHES "apple") set(WASMTIME_SHARED_FILES libwasmtime.dylib) diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml index 4ec29b483020..eccea9cc7602 100644 --- a/crates/c-api/Cargo.toml +++ b/crates/c-api/Cargo.toml @@ -33,6 +33,7 @@ wat = { workspace = true, optional = true } cap-std = { workspace = true, optional = true } tokio = { workspace = true, optional = true, features = ["fs"] } wasmtime-wasi = { workspace = true, optional = true, features = ["p1"] } +wasmtime-wasi-http = { workspace = true, optional = true, features = ["p2", "default-send-request"] } wasmtime-wasi-io = { workspace = true, optional = true, features = ["std"] } async-trait = { workspace = true, optional = true } bytes = { workspace = true, optional = true } @@ -47,6 +48,7 @@ profiling = ["wasmtime/profiling"] cache = ["wasmtime/cache"] parallel-compilation = ['wasmtime/parallel-compilation'] wasi = ['cap-std', 'wasmtime-wasi', 'wasmtime-wasi-io', 'tokio', 'async-trait', 'bytes'] +wasi-http = ['wasi', 'wasmtime-wasi-http'] logging = ['dep:env_logger'] disable-logging = ["log/max_level_off", "tracing/max_level_off"] coredump = ["wasmtime/coredump"] diff --git a/crates/c-api/artifact/Cargo.toml b/crates/c-api/artifact/Cargo.toml index d02aaf2f62f3..bf23c85472fd 100644 --- a/crates/c-api/artifact/Cargo.toml +++ b/crates/c-api/artifact/Cargo.toml @@ -26,6 +26,7 @@ default = [ 'profiling', 'wat', 'wasi', + 'wasi-http', 'cache', 'parallel-compilation', 'async', @@ -51,6 +52,7 @@ profiling = ["wasmtime-c-api/profiling"] cache = ["wasmtime-c-api/cache"] parallel-compilation = ['wasmtime-c-api/parallel-compilation'] wasi = ['wasmtime-c-api/wasi'] +wasi-http = ['wasmtime-c-api/wasi-http'] logging = ['wasmtime-c-api/logging'] disable-logging = ["wasmtime-c-api/disable-logging"] coredump = ["wasmtime-c-api/coredump"] diff --git a/crates/c-api/build.rs b/crates/c-api/build.rs index 016c6188b71f..211f2c4d0efc 100644 --- a/crates/c-api/build.rs +++ b/crates/c-api/build.rs @@ -8,6 +8,7 @@ const FEATURES: &[&str] = &[ "CACHE", "PARALLEL_COMPILATION", "WASI", + "WASI_HTTP", "LOGGING", "DISABLE_LOGGING", "COREDUMP", diff --git a/crates/c-api/cmake/features.cmake b/crates/c-api/cmake/features.cmake index 2ea8bad995ba..c487ea1eb693 100644 --- a/crates/c-api/cmake/features.cmake +++ b/crates/c-api/cmake/features.cmake @@ -31,6 +31,7 @@ feature(wat ON) feature(cache ON) feature(parallel-compilation ON) feature(wasi ON) +feature(wasi-http ON) feature(logging ON) feature(disable-logging OFF) feature(coredump ON) diff --git a/crates/c-api/doxygen.conf.in b/crates/c-api/doxygen.conf.in index 6ee6fe7b74d1..1c9f33a54843 100644 --- a/crates/c-api/doxygen.conf.in +++ b/crates/c-api/doxygen.conf.in @@ -466,7 +466,7 @@ LOOKUP_CACHE_SIZE = 0 # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you +# which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. diff --git a/crates/c-api/include/doc-wasm.h b/crates/c-api/include/doc-wasm.h index d7911f0e9a8e..4a92d15d77bf 100644 --- a/crates/c-api/include/doc-wasm.h +++ b/crates/c-api/include/doc-wasm.h @@ -187,7 +187,7 @@ * \fn wasm_name_new_empty * \brief Convenience alias * - * \fn wasm_name_new_new_uninitialized + * \fn wasm_name_new_uninitialized * \brief Convenience alias * * \fn wasm_name_new_from_string @@ -611,11 +611,77 @@ * #wasmtime_memorytype_maximum or #wasmtime_memorytype_minimum instead. */ +/** + * \struct wasm_tagtype_t + * \brief An opaque object representing the type of a tag. + * + * \typedef wasm_tagtype_t + * \brief Convenience alias for #wasm_tagtype_t + * + * \struct wasm_tagtype_vec_t + * \brief A list of #wasm_tagtype_t values. + * + * \var wasm_tagtype_vec_t::size + * \brief Length of this vector. + * + * \var wasm_tagtype_vec_t::data + * \brief Pointer to the base of this vector + * + * \typedef wasm_tagtype_vec_t + * \brief Convenience alias for #wasm_tagtype_vec_t + * + * \fn void wasm_tagtype_delete(wasm_tagtype_t *); + * \brief Deletes a type. + * + * \fn void wasm_tagtype_vec_new_empty(wasm_tagtype_vec_t *out); + * \brief Creates an empty vector. + * + * See #wasm_byte_vec_new_empty for more information. + * + * \fn void wasm_tagtype_vec_new_uninitialized(wasm_tagtype_vec_t *out, size_t); + * \brief Creates a vector with the given capacity. + * + * See #wasm_byte_vec_new_uninitialized for more information. + * + * \fn void wasm_tagtype_vec_new(wasm_tagtype_vec_t *out, size_t, wasm_tagtype_t *const[]); + * \brief Creates a vector with the provided contents. + * + * See #wasm_byte_vec_new for more information. + * + * \fn void wasm_tagtype_vec_copy(wasm_tagtype_vec_t *out, const wasm_tagtype_vec_t *) + * \brief Copies one vector to another + * + * See #wasm_byte_vec_copy for more information. + * + * \fn void wasm_tagtype_vec_delete(wasm_tagtype_vec_t *out) + * \brief Deallocates memory for a vector. + * + * See #wasm_byte_vec_delete for more information. + * + * \fn wasm_tagtype_t* wasm_tagtype_copy(const wasm_tagtype_t *) + * \brief Creates a new value which matches the provided one. + * + * The caller is responsible for deleting the returned value. + * + * \fn wasm_tagtype_t* wasm_tagtype_new(wasm_functype_t *) + * \brief Creates a new tag type. + * + * This function takes ownership of the #wasm_functype_t argument. + * + * The caller is responsible for deallocating the returned type. + * + * \fn const wasm_functype_t* wasm_tagtype_functype(const wasm_tagtype_t *); + * \brief Returns the function type of this tag type. + * + * The returned #wasm_functype_t is owned by the #wasm_tagtype_t parameter, the + * caller should not deallocate it. + */ + /** * \struct wasm_externtype_t * \brief An opaque object representing the type of a external value. Can be * seen as a superclass of #wasm_functype_t, #wasm_tabletype_t, - * #wasm_globaltype_t, and #wasm_memorytype_t. + * #wasm_globaltype_t, #wasm_memorytype_t, and #wasm_tagtype_t. * * \typedef wasm_externtype_t * \brief Convenience alias for #wasm_externtype_t @@ -675,6 +741,21 @@ * * This is returned from #wasm_extern_kind and #wasm_externtype_kind to * determine what kind of type is wrapped. + * + * \def WASM_EXTERN_FUNC + * \brief Indicator that an extern is a function. + * + * \def WASM_EXTERN_GLOBAL + * \brief Indicator that an extern is a global. + * + * \def WASM_EXTERN_TABLE + * \brief Indicator that an extern is a table. + * + * \def WASM_EXTERN_MEMORY + * \brief Indicator that an extern is a memory. + * + * \def WASM_EXTERN_TAG + * \brief Indicator that an extern is a tag. */ /** @@ -702,6 +783,12 @@ * The returned value is owned by the #wasm_memorytype_t argument and should not * be deleted. * + * \fn wasm_externtype_t* wasm_tagtype_as_externtype(wasm_tagtype_t *) + * \brief Converts a #wasm_tagtype_t to a #wasm_externtype_t + * + * The returned value is owned by the #wasm_tagtype_t argument and should not + * be deleted. + * * \fn const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t *) * \brief Converts a #wasm_functype_t to a #wasm_externtype_t * @@ -726,6 +813,12 @@ * The returned value is owned by the #wasm_memorytype_t argument and should not * be deleted. * + * \fn const wasm_externtype_t* wasm_tagtype_as_externtype_const(const wasm_tagtype_t *) + * \brief Converts a #wasm_tagtype_t to a #wasm_externtype_t + * + * The returned value is owned by the #wasm_tagtype_t argument and should not + * be deleted. + * * \fn wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t *) * \brief Attempts to convert a #wasm_externtype_t to a #wasm_functype_t * @@ -747,6 +840,13 @@ * be deleted. Returns `NULL` if the provided argument is not a * #wasm_memorytype_t. * + * \fn wasm_tagtype_t* wasm_externtype_as_tagtype(wasm_externtype_t *) + * \brief Attempts to convert a #wasm_externtype_t to a #wasm_tagtype_t + * + * The returned value is owned by the #wasm_tagtype_t argument and should not + * be deleted. Returns `NULL` if the provided argument is not a + * #wasm_tagtype_t. + * * \fn wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t *) * \brief Attempts to convert a #wasm_externtype_t to a #wasm_globaltype_t * @@ -775,6 +875,13 @@ * be deleted. Returns `NULL` if the provided argument is not a * #wasm_memorytype_t. * + * \fn const wasm_tagtype_t* wasm_externtype_as_tagtype_const(const wasm_externtype_t *) + * \brief Attempts to convert a #wasm_externtype_t to a #wasm_tagtype_t + * + * The returned value is owned by the #wasm_tagtype_t argument and should not + * be deleted. Returns `NULL` if the provided argument is not a + * #wasm_tagtype_t. + * * \fn const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t *) * \brief Attempts to convert a #wasm_externtype_t to a #wasm_globaltype_t * diff --git a/crates/c-api/include/wasm.h b/crates/c-api/include/wasm.h index c8b3cb7d31f4..dc43a26a4217 100644 --- a/crates/c-api/include/wasm.h +++ b/crates/c-api/include/wasm.h @@ -1,5 +1,5 @@ // Wasmtime-vendored copy of this header file as present in upstream: -// +// // // Wasmtime maintainers can update this vendored copy in-tree with the // ./ci/vendor-c-api-headers.sh shell script. @@ -106,7 +106,7 @@ typedef wasm_byte_vec_t wasm_name_t; #define wasm_name wasm_byte_vec #define wasm_name_new wasm_byte_vec_new #define wasm_name_new_empty wasm_byte_vec_new_empty -#define wasm_name_new_new_uninitialized wasm_byte_vec_new_uninitialized +#define wasm_name_new_uninitialized wasm_byte_vec_new_uninitialized #define wasm_name_copy wasm_byte_vec_copy #define wasm_name_delete wasm_byte_vec_delete @@ -253,6 +253,15 @@ WASM_API_EXTERN own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t*) WASM_API_EXTERN const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t*); +// Tag Types + +WASM_DECLARE_TYPE(tagtype) + +WASM_API_EXTERN own wasm_tagtype_t* wasm_tagtype_new(own wasm_functype_t*); + +WASM_API_EXTERN const wasm_functype_t* wasm_tagtype_functype(const wasm_tagtype_t*); + + // Extern Types WASM_DECLARE_TYPE(externtype) @@ -263,6 +272,7 @@ enum wasm_externkind_enum { WASM_EXTERN_GLOBAL, WASM_EXTERN_TABLE, WASM_EXTERN_MEMORY, + WASM_EXTERN_TAG, }; WASM_API_EXTERN wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*); @@ -271,21 +281,25 @@ WASM_API_EXTERN wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t*) WASM_API_EXTERN wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t*); WASM_API_EXTERN wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t*); WASM_API_EXTERN wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_tagtype_as_externtype(wasm_tagtype_t*); WASM_API_EXTERN wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t*); WASM_API_EXTERN wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t*); WASM_API_EXTERN wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t*); WASM_API_EXTERN wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t*); +WASM_API_EXTERN wasm_tagtype_t* wasm_externtype_as_tagtype(wasm_externtype_t*); WASM_API_EXTERN const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t*); WASM_API_EXTERN const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t*); WASM_API_EXTERN const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t*); WASM_API_EXTERN const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_tagtype_as_externtype_const(const wasm_tagtype_t*); WASM_API_EXTERN const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t*); WASM_API_EXTERN const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t*); WASM_API_EXTERN const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t*); WASM_API_EXTERN const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_tagtype_t* wasm_externtype_as_tagtype_const(const wasm_externtype_t*); // Import Types diff --git a/crates/c-api/include/wasm.hh b/crates/c-api/include/wasm.hh index e03408241b73..7ade36fcc044 100644 --- a/crates/c-api/include/wasm.hh +++ b/crates/c-api/include/wasm.hh @@ -1,5 +1,5 @@ // Wasmtime-vendored copy of this header file as present in upstream: -// +// // // Wasmtime maintainers can update this vendored copy in-tree with the // ./ci/vendor-c-api-headers.sh shell script. @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -299,13 +300,14 @@ public: // External Types enum class ExternKind : uint8_t { - FUNC, GLOBAL, TABLE, MEMORY + FUNC, GLOBAL, TABLE, MEMORY, TAG }; class FuncType; class GlobalType; class TableType; class MemoryType; +class TagType; class WASM_API_EXTERN ExternType { friend class destroyer; @@ -324,11 +326,13 @@ public: auto global() -> GlobalType*; auto table() -> TableType*; auto memory() -> MemoryType*; + auto tag() -> TagType*; auto func() const -> const FuncType*; auto global() const -> const GlobalType*; auto table() const -> const TableType*; auto memory() const -> const MemoryType*; + auto tag() const -> const TagType*; }; @@ -411,6 +415,24 @@ public: }; +// Tag Types + +class WASM_API_EXTERN TagType : public ExternType { + friend class destroyer; + void destroy(); + +protected: + TagType() = default; + ~TagType() = default; + +public: + static auto make(own&&) -> own; + auto copy() const -> own; + + auto functype() const -> const FuncType*; +}; + + // Import Types using Name = vec; diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index da28922d4d0c..3867ed91ed43 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -190,6 +190,7 @@ #include #include #include +#include #include #include #include @@ -201,8 +202,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -212,11 +215,11 @@ /** * \brief Wasmtime version string. */ -#define WASMTIME_VERSION "43.0.0" +#define WASMTIME_VERSION "44.0.2" /** * \brief Wasmtime major version number. */ -#define WASMTIME_VERSION_MAJOR 43 +#define WASMTIME_VERSION_MAJOR 44 /** * \brief Wasmtime minor version number. */ @@ -224,6 +227,6 @@ /** * \brief Wasmtime patch version number. */ -#define WASMTIME_VERSION_PATCH 0 +#define WASMTIME_VERSION_PATCH 2 #endif // WASMTIME_API_H diff --git a/crates/c-api/include/wasmtime.hh b/crates/c-api/include/wasmtime.hh index 9a7c11cb4a85..4a6df6d887ad 100644 --- a/crates/c-api/include/wasmtime.hh +++ b/crates/c-api/include/wasmtime.hh @@ -38,8 +38,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -47,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/crates/c-api/include/wasmtime/component/linker.h b/crates/c-api/include/wasmtime/component/linker.h index 5a125f655ffe..117acdcc2a81 100644 --- a/crates/c-api/include/wasmtime/component/linker.h +++ b/crates/c-api/include/wasmtime/component/linker.h @@ -165,6 +165,19 @@ wasmtime_component_linker_add_wasip2(wasmtime_component_linker_t *linker); #endif // WASMTIME_FEATURE_WASI +#ifdef WASMTIME_FEATURE_WASI_HTTP + +/** + * \brief Add WASI HTTP interfaces into the \p linker provided. + * + * This adds `wasi:http/types` and `wasi:http/outgoing-handler` to the linker. + * Requires WASI to be added first via #wasmtime_component_linker_add_wasip2. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_component_linker_add_wasi_http(wasmtime_component_linker_t *linker); + +#endif // WASMTIME_FEATURE_WASI_HTTP + /// Type of the callback used in /// #wasmtime_component_linker_instance_add_resource typedef wasmtime_error_t *(*wasmtime_component_resource_destructor_t)( @@ -175,7 +188,7 @@ typedef wasmtime_error_t *(*wasmtime_component_resource_destructor_t)( * * This can be used to define a new resource type that the guest will be able * to import. Here the `resource` is a type, often a host-defined type, which - * can be used to distinguish and definie different types of resources. A + * can be used to distinguish and definite different types of resources. A * destruction callback is also specified via `destructor` which has private * data `data` along with an optional `finalizer` for the `data` too. * diff --git a/crates/c-api/include/wasmtime/component/linker.hh b/crates/c-api/include/wasmtime/component/linker.hh index 5606e9cb349c..74d896f65eed 100644 --- a/crates/c-api/include/wasmtime/component/linker.hh +++ b/crates/c-api/include/wasmtime/component/linker.hh @@ -215,6 +215,23 @@ class Linker { return std::monostate(); } #endif // WASMTIME_FEATURE_WASI + +#ifdef WASMTIME_FEATURE_WASI_HTTP + /** + * \brief Adds WASI HTTP definitions to this linker. + * + * This adds `wasi:http/types` and `wasi:http/outgoing-handler` to the linker. + * Requires WASIp2 to be added first via \ref add_wasip2. + */ + Result add_wasi_http() { + wasmtime_error_t *error = + wasmtime_component_linker_add_wasi_http(ptr.get()); + if (error != nullptr) { + return Error(error); + } + return std::monostate(); + } +#endif // WASMTIME_FEATURE_WASI_HTTP }; } // namespace component diff --git a/crates/c-api/include/wasmtime/component/types/val.h b/crates/c-api/include/wasmtime/component/types/val.h index 85301056ad6f..d7d9d009cfc2 100644 --- a/crates/c-api/include/wasmtime/component/types/val.h +++ b/crates/c-api/include/wasmtime/component/types/val.h @@ -335,6 +335,43 @@ WASM_API_EXTERN bool wasmtime_component_stream_type_ty( const wasmtime_component_stream_type_t *ty, struct wasmtime_component_valtype_t *type_ret); +// ----------- maps ------------------------------------------------------------ + +/// \brief Opaque type representing a component map type. +typedef struct wasmtime_component_map_type wasmtime_component_map_type_t; + +/// \brief Clones a component map type. +/// +/// The returned pointer must be deallocated with +/// `wasmtime_component_map_type_delete`. +WASM_API_EXTERN wasmtime_component_map_type_t * +wasmtime_component_map_type_clone(const wasmtime_component_map_type_t *ty); + +/// \brief Compares two component map types for equality. +WASM_API_EXTERN bool +wasmtime_component_map_type_equal(const wasmtime_component_map_type_t *a, + const wasmtime_component_map_type_t *b); + +/// \brief Deallocates a component map type. +WASM_API_EXTERN void +wasmtime_component_map_type_delete(wasmtime_component_map_type_t *ptr); + +/// \brief Returns the key type of a component map type. +/// +/// The returned type must be deallocated with +/// `wasmtime_component_valtype_delete`. +WASM_API_EXTERN void +wasmtime_component_map_type_key(const wasmtime_component_map_type_t *ty, + struct wasmtime_component_valtype_t *type_ret); + +/// \brief Returns the value type of a component map type. +/// +/// The returned type must be deallocated with +/// `wasmtime_component_valtype_delete`. +WASM_API_EXTERN void wasmtime_component_map_type_value( + const wasmtime_component_map_type_t *ty, + struct wasmtime_component_valtype_t *type_ret); + // ----------- valtype --------------------------------------------------------- /// \brief Value of #wasmtime_component_valtype_kind_t meaning that @@ -415,6 +452,9 @@ WASM_API_EXTERN bool wasmtime_component_stream_type_ty( /// \brief Value of #wasmtime_component_valtype_kind_t meaning that /// #wasmtime_component_valtype_t is an `error context` WIT type. #define WASMTIME_COMPONENT_VALTYPE_ERROR_CONTEXT 25 +/// \brief Value of #wasmtime_component_valtype_kind_t meaning that +/// #wasmtime_component_valtype_t is a `map` WIT type. +#define WASMTIME_COMPONENT_VALTYPE_MAP 26 /// \brief Discriminant used in #wasmtime_component_valtype_t::kind typedef uint8_t wasmtime_component_valtype_kind_t; @@ -457,6 +497,9 @@ typedef union wasmtime_component_valtype_union { /// Field used if #wasmtime_component_valtype_t::kind is /// #WASMTIME_COMPONENT_VALTYPE_STREAM wasmtime_component_stream_type_t *stream; + /// Field used if #wasmtime_component_valtype_t::kind is + /// #WASMTIME_COMPONENT_VALTYPE_MAP + wasmtime_component_map_type_t *map; } wasmtime_component_valtype_union_t; /// \brief Represents a single value type in the component model. diff --git a/crates/c-api/include/wasmtime/component/types/val.hh b/crates/c-api/include/wasmtime/component/types/val.hh index 8cf31b62f1c4..6a93b3de5c3a 100644 --- a/crates/c-api/include/wasmtime/component/types/val.hh +++ b/crates/c-api/include/wasmtime/component/types/val.hh @@ -178,6 +178,19 @@ class StreamType { std::optional ty() const; }; +/** + * \brief Represents a component map type. + */ +class MapType { + WASMTIME_CLONE_EQUAL_WRAPPER(MapType, wasmtime_component_map_type); + + /// Returns the key type of this map type. + ValType key() const; + + /// Returns the value type of this map type. + ValType value() const; +}; + /** * \brief Represents a component value type. */ @@ -382,6 +395,12 @@ public: ty.of.stream = stream.capi_release(); } + /// Creates a map value type. + ValType(MapType map) { + ty.kind = WASMTIME_COMPONENT_VALTYPE_MAP; + ty.of.map = map.capi_release(); + } + /// Returns the kind of this value type. wasmtime_component_valtype_kind_t kind() const { return ty.kind; } @@ -481,6 +500,9 @@ public: return ty.kind == WASMTIME_COMPONENT_VALTYPE_ERROR_CONTEXT; } + /// Returns true if this is a map type. + bool is_map() const { return ty.kind == WASMTIME_COMPONENT_VALTYPE_MAP; } + /// Returns the list type, asserting that this is indeed a list. const ListType &list() const { assert(is_list()); @@ -553,6 +575,12 @@ public: return *StreamType::from_capi(&ty.of.stream); } + /// Returns the map type, asserting that this is indeed a map. + const MapType &map() const { + assert(is_map()); + return *MapType::from_capi(&ty.of.map); + } + /// \brief Returns the underlying C API pointer. const wasmtime_component_valtype_t *capi() const { return &ty; } /// \brief Returns the underlying C API pointer. @@ -640,6 +668,18 @@ inline std::optional StreamType::ty() const { return std::nullopt; } +inline ValType MapType::key() const { + wasmtime_component_valtype_t type_ret; + wasmtime_component_map_type_key(ptr.get(), &type_ret); + return ValType(std::move(type_ret)); +} + +inline ValType MapType::value() const { + wasmtime_component_valtype_t type_ret; + wasmtime_component_map_type_value(ptr.get(), &type_ret); + return ValType(std::move(type_ret)); +} + } // namespace component } // namespace wasmtime diff --git a/crates/c-api/include/wasmtime/component/val.h b/crates/c-api/include/wasmtime/component/val.h index 2021651273f9..c6a31469f7b2 100644 --- a/crates/c-api/include/wasmtime/component/val.h +++ b/crates/c-api/include/wasmtime/component/val.h @@ -266,9 +266,13 @@ typedef uint8_t wasmtime_component_valkind_t; /// \brief Value of #wasmtime_component_valkind_t meaning that /// #wasmtime_component_val_t is a resource #define WASMTIME_COMPONENT_RESOURCE 21 +/// \brief Value of #wasmtime_component_valkind_t meaning that +/// #wasmtime_component_val_t is a map +#define WASMTIME_COMPONENT_MAP 22 struct wasmtime_component_val; struct wasmtime_component_valrecord_entry; +struct wasmtime_component_valmap_entry; #define DECLARE_VEC(name, type) \ /** \brief A vec of a type */ \ @@ -296,6 +300,7 @@ DECLARE_VEC(wasmtime_component_valrecord, struct wasmtime_component_valrecord_entry) DECLARE_VEC(wasmtime_component_valtuple, struct wasmtime_component_val) DECLARE_VEC(wasmtime_component_valflags, wasm_name_t) +DECLARE_VEC(wasmtime_component_valmap, struct wasmtime_component_valmap_entry) #undef DECLARE_VEC @@ -366,6 +371,8 @@ typedef union { wasmtime_component_valresult_t result; /// Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_FLAGS wasmtime_component_valflags_t flags; + /// Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_MAP + wasmtime_component_valmap_t map; /// Field used if #wasmtime_component_val_t::kind is /// #WASMTIME_COMPONENT_RESOURCE wasmtime_component_resource_any_t *resource; @@ -389,6 +396,15 @@ typedef struct wasmtime_component_valrecord_entry { wasmtime_component_val_t val; } wasmtime_component_valrecord_entry_t; +/// \brief A pair of a key and a value that represents one entry in a value +/// with kind #WASMTIME_COMPONENT_MAP +typedef struct wasmtime_component_valmap_entry { + /// The key of this entry + wasmtime_component_val_t key; + /// The value of this entry + wasmtime_component_val_t value; +} wasmtime_component_valmap_entry_t; + /// \brief Allocates a new `wasmtime_component_val_t` on the heap, initializing /// it with the contents of `val`. /// diff --git a/crates/c-api/include/wasmtime/component/val.hh b/crates/c-api/include/wasmtime/component/val.hh index ffc377ab2ee1..1ff5c9a5be48 100644 --- a/crates/c-api/include/wasmtime/component/val.hh +++ b/crates/c-api/include/wasmtime/component/val.hh @@ -33,7 +33,7 @@ inline const Val *val_from_capi(const wasmtime_component_val_t *capi) { } // namespace detail -/// Internal helper macro to define ownership-semanitcs for C++ types based on +/// Internal helper macro to define ownership-semantics for C++ types based on /// a C type as a single member where operations are defined in terms of /// `transfer`, `copy`, and `destroy` functions. #define VAL_REPR(name, raw_type) \ @@ -417,6 +417,63 @@ public: } }; +/// \brief Class representing an entry in a map value. +class MapEntry { + friend class Map; + + wasmtime_component_valmap_entry_t entry; + + // This value can't be constructed or destructed, it's only used in iteration + // of `Map`. + MapEntry() = delete; + ~MapEntry() = delete; + + static const MapEntry * + from_capi(const wasmtime_component_valmap_entry_t *capi) { + return reinterpret_cast(capi); + } + +public: + /// \brief Returns the key of this map entry. + const Val &key() const { return *detail::val_from_capi(&entry.key); } + + /// \brief Returns the value of this map entry. + const Val &value() const { return *detail::val_from_capi(&entry.value); } +}; + +/// \brief Class representing a component model map, a collection of key/value +/// pairs. +class Map { + friend class Val; + + VAL_REPR(Map, wasmtime_component_valmap_t); + + static void transfer(Raw &&from, Raw &to) { + to = from; + from.size = 0; + from.data = nullptr; + } + + void copy(const Raw &other) { wasmtime_component_valmap_copy(&raw, &other); } + + void destroy() { wasmtime_component_valmap_delete(&raw); } + +public: + /// Creates a new map from the key/value pairs provided. + Map(std::vector> entries); + + /// \brief Returns the number of entries in the map. + size_t size() const { return raw.size; } + + /// \brief Returns an iterator to the beginning of the map entries. + const MapEntry *begin() const { return MapEntry::from_capi(raw.data); } + + /// \brief Returns an iterator to the end of the map entries. + const MapEntry *end() const { + return MapEntry::from_capi(raw.data + raw.size); + } +}; + class ResourceHost; /// Class representing a component model `resource` value which is either a @@ -644,6 +701,12 @@ public: Flags::transfer(std::move(f.raw), raw.of.flags); } + /// Creates a new map value. + Val(Map m) { + raw.kind = WASMTIME_COMPONENT_MAP; + Map::transfer(std::move(m.raw), raw.of.map); + } + /// Creates a new resource value. Val(ResourceAny r) { raw.kind = WASMTIME_COMPONENT_RESOURCE; @@ -833,11 +896,20 @@ public: /// \brief Returns whether this value is a resource. bool is_resource() const { return raw.kind == WASMTIME_COMPONENT_RESOURCE; } - /// \brief Returns the flags value, only valid if `is_flags()`. + /// \brief Returns the resource value, only valid if `is_resource()`. const ResourceAny &get_resource() const { assert(is_resource()); return *ResourceAny::from_capi(&raw.of.resource); } + + /// \brief Returns whether this value is a map. + bool is_map() const { return raw.kind == WASMTIME_COMPONENT_MAP; } + + /// \brief Returns the map value, only valid if `is_map()`. + const Map &get_map() const { + assert(is_map()); + return *Map::from_capi(&raw.of.map); + } }; #undef VAL_REPR @@ -852,6 +924,16 @@ inline Record::Record(std::vector> entries) { } } +inline Map::Map(std::vector> entries) { + wasmtime_component_valmap_new_uninit(&raw, entries.size()); + auto dst = raw.data; + for (auto &&[key, val] : entries) { + new (&dst->key) Val(std::move(key)); + new (&dst->value) Val(std::move(val)); + dst++; + } +} + inline List::List(std::vector values) { wasmtime_component_vallist_new_uninit(&raw, values.size()); auto dst = raw.data; diff --git a/crates/c-api/include/wasmtime/conf.h.in b/crates/c-api/include/wasmtime/conf.h.in index f308dd3e5c3a..56d6c2514a2a 100644 --- a/crates/c-api/include/wasmtime/conf.h.in +++ b/crates/c-api/include/wasmtime/conf.h.in @@ -13,6 +13,7 @@ #cmakedefine WASMTIME_FEATURE_CACHE #cmakedefine WASMTIME_FEATURE_PARALLEL_COMPILATION #cmakedefine WASMTIME_FEATURE_WASI +#cmakedefine WASMTIME_FEATURE_WASI_HTTP #cmakedefine WASMTIME_FEATURE_LOGGING #cmakedefine WASMTIME_FEATURE_DISABLE_LOGGING #cmakedefine WASMTIME_FEATURE_COREDUMP diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h index 01f8b7a4b7a8..d7788a694b1d 100644 --- a/crates/c-api/include/wasmtime/config.h +++ b/crates/c-api/include/wasmtime/config.h @@ -833,6 +833,15 @@ WASMTIME_CONFIG_PROP(void, wasm_component_model, bool) */ WASMTIME_CONFIG_PROP(void, concurrency_support, bool) +/** + * \brief Configures whether the WebAssembly component-model map type will be + * enabled for compilation. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.wasm_component_model_map. + */ +WASMTIME_CONFIG_PROP(void, wasm_component_model_map, bool) + #endif // WASMTIME_FEATURE_COMPONENT_MODEL #ifdef __cplusplus diff --git a/crates/c-api/include/wasmtime/config.hh b/crates/c-api/include/wasmtime/config.hh index ff418d1fe427..12d57245fb2f 100644 --- a/crates/c-api/include/wasmtime/config.hh +++ b/crates/c-api/include/wasmtime/config.hh @@ -407,6 +407,14 @@ class Config { void wasm_component_model(bool enable) { wasmtime_config_wasm_component_model_set(ptr.get(), enable); } + + /// \brief Configures whether the WebAssembly component model map type will be + /// enabled + /// + /// https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.wasm_component_model_map + void wasm_component_model_map(bool enable) { + wasmtime_config_wasm_component_model_map_set(ptr.get(), enable); + } #endif // WASMTIME_FEATURE_COMPONENT_MODEL #ifdef WASMTIME_FEATURE_PARALLEL_COMPILATION diff --git a/crates/c-api/include/wasmtime/exn.h b/crates/c-api/include/wasmtime/exn.h new file mode 100644 index 000000000000..effc5d383c0d --- /dev/null +++ b/crates/c-api/include/wasmtime/exn.h @@ -0,0 +1,166 @@ +/** + * \file wasmtime/exn.h + * + * \brief Wasmtime APIs for WebAssembly exception objects. + * + * Exception objects carry a tag and a set of field values. They are + * allocated on the GC heap within a store. + * + * ## Throwing from host functions + * + * To throw an exception from a host function implemented via the C API: + * + * 1. Create an exception object with #wasmtime_exn_new. + * 2. Set it as the pending exception with #wasmtime_context_set_exception. + * 3. Return a trap from the host callback (e.g. via `wasmtime_trap_new`). + * + * The runtime will propagate the exception through WebAssembly + * `try_table`/`catch` blocks. + * + * ## Catching exceptions from Wasm + * + * When a call to a WebAssembly function (e.g. via #wasmtime_func_call) + * returns a trap, check for a pending exception: + * + * 1. Call #wasmtime_context_has_exception or #wasmtime_context_take_exception. + * 2. If present, examine the exception's tag and fields. + */ + +#ifndef WASMTIME_EXN_H +#define WASMTIME_EXN_H + +#include + +#ifdef WASMTIME_FEATURE_GC + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_exn_t + * \brief An opaque type representing a WebAssembly exception object. + * + * Exception objects are allocated on the GC heap and referenced through + * this handle. The handle is owned by the caller and must be freed + * with #wasmtime_exn_delete. + */ +typedef struct wasmtime_exn wasmtime_exn_t; + +/** + * \brief Deletes a #wasmtime_exn_t. + */ +WASM_API_EXTERN void wasmtime_exn_delete(wasmtime_exn_t *exn); + +/** + * \brief Creates a new exception object. + * + * \param store the store context + * \param tag the tag to associate with this exception + * \param fields pointer to an array of field values matching the tag's + * payload signature + * \param nfields the number of elements in `fields` + * \param exn_ret on success, set to the newly allocated exception. + * The caller owns the returned pointer and must free it with + * #wasmtime_exn_delete. + * + * \return NULL on success, or an error on failure. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_exn_new(wasmtime_context_t *store, + const wasmtime_tag_t *tag, + const wasmtime_val_t *fields, + size_t nfields, + wasmtime_exn_t **exn_ret); + +/** + * \brief Returns the tag associated with this exception. + * + * \param store the store context + * \param exn the exception to query + * \param tag_ret on success, filled with the exception's tag + * + * \return NULL on success, or an error on failure. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_exn_tag(wasmtime_context_t *store, + const wasmtime_exn_t *exn, + wasmtime_tag_t *tag_ret); + +/** + * \brief Returns the number of fields in this exception. + * + * \param store the store context + * \param exn the exception to query + */ +WASM_API_EXTERN size_t wasmtime_exn_field_count(wasmtime_context_t *store, + const wasmtime_exn_t *exn); + +/** + * \brief Reads a field value from this exception by index. + * + * \param store the store context + * \param exn the exception to query + * \param index the field index (0-based) + * \param val_ret on success, filled with the field value + * (caller-owned on return). + * + * \return NULL on success, or an error if the index is out of bounds. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_exn_field(wasmtime_context_t *store, + const wasmtime_exn_t *exn, + size_t index, + wasmtime_val_t *val_ret); + +/** + * \brief Sets the pending exception on the store and returns a trap. + * + * This transfers ownership of `exn` to the store. After this call, + * the caller must not use or free `exn`. + * + * Returns a `wasm_trap_t` that the host callback MUST return to signal + * to the Wasm runtime that an exception was thrown. The caller owns + * the returned trap. + * + * \param store the store context + * \param exn the exception to throw (ownership transferred) + * \return a trap to return from the host callback (caller-owned) + */ +WASM_API_EXTERN wasm_trap_t * +wasmtime_context_set_exception(wasmtime_context_t *store, wasmtime_exn_t *exn); + +/** + * \brief Takes the pending exception from the store, if any. + * + * If there is a pending exception, removes it from the store and + * returns it. The caller owns the returned pointer and must free it + * with #wasmtime_exn_delete. + * + * \param store the store context + * \param exn_ret on success, set to the exception (caller-owned) + * + * \return true if there was a pending exception, false otherwise. + */ +WASM_API_EXTERN bool wasmtime_context_take_exception(wasmtime_context_t *store, + wasmtime_exn_t **exn_ret); + +/** + * \brief Tests whether there is a pending exception on the store. + * + * \param store the store context + * + * \return true if a pending exception is set, false otherwise. + */ +WASM_API_EXTERN bool wasmtime_context_has_exception(wasmtime_context_t *store); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_FEATURE_GC + +#endif // WASMTIME_EXN_H diff --git a/crates/c-api/include/wasmtime/exn.hh b/crates/c-api/include/wasmtime/exn.hh new file mode 100644 index 000000000000..12ea8f307504 --- /dev/null +++ b/crates/c-api/include/wasmtime/exn.hh @@ -0,0 +1,122 @@ +/** + * \file wasmtime/exn.hh + */ + +#ifndef WASMTIME_EXN_HH +#define WASMTIME_EXN_HH + +#include + +#ifdef WASMTIME_FEATURE_GC + +#include +#include +#include +#include +#include +#include + +namespace wasmtime { + +/** + * \brief A WebAssembly exception object. + * + * Exception objects carry a tag and a set of field values. They are + * allocated on the GC heap within a store. + * + * This type owns its underlying `wasmtime_exn_t` handle. When it goes out + * of scope the handle is freed. + */ +class Exn { + struct deleter { + void operator()(wasmtime_exn_t *p) const { wasmtime_exn_delete(p); } + }; + + std::unique_ptr ptr; + +public: + /// Takes ownership of a raw `wasmtime_exn_t` pointer. + explicit Exn(wasmtime_exn_t *raw) : ptr(raw) {} + + /// Constructs a new exception. + Exn(Exn &&other) = default; + /// Moves an exception. + Exn &operator=(Exn &&other) = default; + Exn(const Exn &) = delete; + Exn &operator=(const Exn &) = delete; + /// Destroys an exception. + ~Exn() = default; + + /** + * \brief Create a new exception object. + * + * \param cx the store in which to allocate the exception + * \param tag the tag to associate with this exception + * \param fields the field values matching the tag's payload signature + */ + static Result create(Store::Context cx, const Tag &tag, + const std::vector &fields) { + wasmtime_exn_t *exn = nullptr; + auto *error = wasmtime_exn_new( + cx.capi(), &tag.capi(), + reinterpret_cast(fields.data()), fields.size(), + &exn); + if (error != nullptr) { + return Error(error); + } + return Exn(exn); + } + + /// Returns the tag associated with this exception. + Result tag(Store::Context cx) const { + wasmtime_tag_t tag; + auto *error = wasmtime_exn_tag(cx.capi(), ptr.get(), &tag); + if (error != nullptr) { + return Error(error); + } + return Tag(tag); + } + + /// Returns the number of fields in this exception. + size_t field_count(Store::Context cx) const { + return wasmtime_exn_field_count(cx.capi(), ptr.get()); + } + + /// Reads a field value by index. + Result field(Store::Context cx, size_t index) const { + wasmtime_val_t val; + auto *error = wasmtime_exn_field(cx.capi(), ptr.get(), index, &val); + if (error != nullptr) { + return Error(error); + } + return Val(val); + } + + /// Returns the raw underlying C API pointer (non-owning). + wasmtime_exn_t *capi() const { return ptr.get(); } + + /// Releases ownership of the underlying C API pointer. + wasmtime_exn_t *release() { return ptr.release(); } +}; + +inline Trap Store::Context::throw_exception(Exn exn) { + return Trap(wasmtime_context_set_exception(capi(), exn.release())); +} + +inline std::optional Store::Context::take_exception() { + wasmtime_exn_t *exn = nullptr; + if (wasmtime_context_take_exception(capi(), &exn)) { + return Exn(exn); + } + return std::nullopt; +} + +inline bool Store::Context::has_exception() { + return wasmtime_context_has_exception(capi()); +} + +} // namespace wasmtime + +#endif // WASMTIME_FEATURE_GC + +#endif // WASMTIME_EXN_HH diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h index 51675b970dbc..113d1e22e9e0 100644 --- a/crates/c-api/include/wasmtime/extern.h +++ b/crates/c-api/include/wasmtime/extern.h @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -105,6 +106,9 @@ typedef uint8_t wasmtime_extern_kind_t; /// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a /// shared memory #define WASMTIME_EXTERN_SHAREDMEMORY 4 +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// tag +#define WASMTIME_EXTERN_TAG 5 /** * \typedef wasmtime_extern_union_t @@ -127,6 +131,8 @@ typedef union wasmtime_extern_union { wasmtime_memory_t memory; /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_SHAREDMEMORY struct wasmtime_sharedmemory *sharedmemory; + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_TAG + wasmtime_tag_t tag; } wasmtime_extern_union_t; /** diff --git a/crates/c-api/include/wasmtime/extern.hh b/crates/c-api/include/wasmtime/extern.hh index 5bab44a3aaa1..05407facddd0 100644 --- a/crates/c-api/include/wasmtime/extern.hh +++ b/crates/c-api/include/wasmtime/extern.hh @@ -11,6 +11,7 @@ #include #include #include +#include namespace wasmtime { @@ -27,6 +28,8 @@ static Extern cvt_extern(wasmtime_extern_t &e) { return Memory(e.of.memory); case WASMTIME_EXTERN_TABLE: return Table(e.of.table); + case WASMTIME_EXTERN_TAG: + return Tag(e.of.tag); } std::abort(); } @@ -44,6 +47,9 @@ static void cvt_extern(const Extern &e, wasmtime_extern_t &raw) { } else if (const auto *memory = std::get_if(&e)) { raw.kind = WASMTIME_EXTERN_MEMORY; raw.of.memory = memory->capi(); + } else if (const auto *tag = std::get_if(&e)) { + raw.kind = WASMTIME_EXTERN_TAG; + raw.of.tag = tag->capi(); } else { std::abort(); } diff --git a/crates/c-api/include/wasmtime/extern_declare.hh b/crates/c-api/include/wasmtime/extern_declare.hh index 3b4257e9ff4d..78cb27d59113 100644 --- a/crates/c-api/include/wasmtime/extern_declare.hh +++ b/crates/c-api/include/wasmtime/extern_declare.hh @@ -12,10 +12,11 @@ class Global; class Func; class Memory; class Table; +class Tag; /// \typedef Extern /// \brief Representation of an external WebAssembly item -typedef std::variant Extern; +typedef std::variant Extern; } // namespace wasmtime diff --git a/crates/c-api/include/wasmtime/gc.h b/crates/c-api/include/wasmtime/gc.h new file mode 100644 index 000000000000..701c296bec25 --- /dev/null +++ b/crates/c-api/include/wasmtime/gc.h @@ -0,0 +1,596 @@ +/** + * \file wasmtime/gc.h + * + * APIs for interacting with WebAssembly GC types in Wasmtime. + * + * This header provides types and functions for GC reference types beyond + * the basic `anyref` and `externref` in val.h: `eqref`, `structref`, + * and `arrayref`. + */ + +#ifndef WASMTIME_GC_H +#define WASMTIME_GC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_eqref_t + * \brief Convenience alias for #wasmtime_eqref + * + * \struct wasmtime_eqref + * \brief A WebAssembly `eqref` value. + * + * This structure represents a reference to a GC object that can be tested for + * equality. The subtypes of `eqref` include `structref`, `arrayref`, and + * `i31ref`. + * + * This type has the same representation and ownership semantics as + * #wasmtime_anyref_t. Values must be explicitly unrooted via + * #wasmtime_eqref_unroot to enable garbage collection. + */ +typedef struct wasmtime_eqref { + /// Internal metadata tracking within the store, embedders should not + /// configure or modify these fields. + uint64_t store_id; + /// Internal to Wasmtime. + uint32_t __private1; + /// Internal to Wasmtime. + uint32_t __private2; + /// Internal to Wasmtime. + void *__private3; +} wasmtime_eqref_t; + +/// \brief Initialize the `ref` to a null `eqref` value. +static inline void wasmtime_eqref_set_null(wasmtime_eqref_t *ref) { + ref->store_id = 0; +} + +/// \brief Returns whether the provided `ref` is a null `eqref` value. +static inline bool wasmtime_eqref_is_null(const wasmtime_eqref_t *ref) { + return ref->store_id == 0; +} + +/** + * \brief Clone an `eqref`, creating a new root. + * + * The cloned reference is stored in `out`. + */ +WASM_API_EXTERN void wasmtime_eqref_clone(const wasmtime_eqref_t *eqref, + wasmtime_eqref_t *out); + +/** + * \brief Unroot an `eqref` to allow garbage collection. + * + * After calling this, `ref` is left in an undefined state and should not be + * used again. + */ +WASM_API_EXTERN void wasmtime_eqref_unroot(wasmtime_eqref_t *ref); + +/** + * \brief Upcast an `eqref` to an `anyref`. + * + * The original `eqref` is not consumed; `out` receives a new cloned root + * pointing to the same GC object as `anyref`. + */ +WASM_API_EXTERN void wasmtime_eqref_to_anyref(const wasmtime_eqref_t *eqref, + wasmtime_anyref_t *out); + +/** + * \brief Create a new `i31ref` value. + * + * Creates a new `i31ref` value (which is a subtype of `eqref`) and returns a + * pointer to it. + * + * If `i31val` does not fit in 31 bits, it is wrapped. + */ +WASM_API_EXTERN void wasmtime_eqref_from_i31(wasmtime_context_t *context, + uint32_t i31val, + wasmtime_eqref_t *out); + +/** + * \brief Test whether this `eqref` is an `i31ref`. + * + * Returns `true` if the given `eqref` is an `i31ref`, `false` otherwise. + * Returns `false` for null references. + */ +WASM_API_EXTERN bool wasmtime_eqref_is_i31(wasmtime_context_t *context, + const wasmtime_eqref_t *eqref); + +/** + * \brief Get the `eqref`'s underlying `i31ref` value, zero extended. + * + * If the given `eqref` is an instance of `i31ref`, then its value is zero + * extended to 32 bits, written to `dst`, and `true` is returned. + * + * If the given `eqref` is not an instance of `i31ref`, then `false` is + * returned and `dst` is left unmodified. + */ +WASM_API_EXTERN bool wasmtime_eqref_i31_get_u(wasmtime_context_t *context, + const wasmtime_eqref_t *eqref, + uint32_t *dst); + +/** + * \brief Get the `eqref`'s underlying `i31ref` value, sign extended. + * + * If the given `eqref` is an instance of `i31ref`, then its value is sign + * extended to 32 bits, written to `dst`, and `true` is returned. + * + * If the given `eqref` is not an instance of `i31ref`, then `false` is + * returned and `dst` is left unmodified. + */ +WASM_API_EXTERN bool wasmtime_eqref_i31_get_s(wasmtime_context_t *context, + const wasmtime_eqref_t *eqref, + int32_t *dst); + +// ============================================================================ +// StructRef +// ============================================================================ + +/** + * \brief Discriminant for storage types in struct/array field types. + * + * Extends #wasmtime_valkind_t with packed storage types + * #WASMTIME_STORAGE_KIND_I8 and #WASMTIME_STORAGE_KIND_I16. + */ +typedef uint8_t wasmtime_storage_kind_t; + +/// \brief An 8-bit packed integer (only valid inside struct/array fields). +#define WASMTIME_STORAGE_KIND_I8 9 +/// \brief A 16-bit packed integer (only valid inside struct/array fields). +#define WASMTIME_STORAGE_KIND_I16 10 + +/** + * \typedef wasmtime_field_type_t + * \brief Convenience alias for #wasmtime_field_type + * + * \struct wasmtime_field_type + * \brief Describes the type and mutability of a struct field or array element. + */ +typedef struct wasmtime_field_type { + /// The storage type of this field. Use #WASMTIME_I32, #WASMTIME_I64, + /// #WASMTIME_F32, etc. for value types, or #WASMTIME_STORAGE_KIND_I8 / + /// #WASMTIME_STORAGE_KIND_I16 for packed types. + wasmtime_storage_kind_t kind; + /// Whether this field is mutable. `true` for mutable, `false` for + /// immutable. + bool mutable_; +} wasmtime_field_type_t; + +/** + * \brief An opaque handle to a WebAssembly struct type definition. + * + * A struct type describes the fields of a struct. It is used to create a + * #wasmtime_struct_ref_pre_t, which can then allocate struct instances. + * + * Owned. Must be deleted with #wasmtime_struct_type_delete. + */ +typedef struct wasmtime_struct_type wasmtime_struct_type_t; + +/** + * \brief Create a new struct type. + * + * \param engine The engine to register the type with. + * \param fields Pointer to an array of field type descriptors. + * \param nfields Number of fields. + * + * \return Returns a new struct type, or NULL on error (e.g. invalid field + * types). + */ +WASM_API_EXTERN wasmtime_struct_type_t * +wasmtime_struct_type_new(const wasm_engine_t *engine, + const wasmtime_field_type_t *fields, size_t nfields); + +/** + * \brief Delete a struct type. + */ +WASM_API_EXTERN void wasmtime_struct_type_delete(wasmtime_struct_type_t *ty); + +/** + * \brief An opaque, pre-allocated, and registered struct layout for faster + * allocation. + * + * Created from a #wasmtime_struct_type_t and a store context. Reusable for + * allocating many struct instances of the same type. + * + * Owned. Must be deleted with #wasmtime_struct_ref_pre_delete. + */ +typedef struct wasmtime_struct_ref_pre wasmtime_struct_ref_pre_t; + +/** + * \brief Create a new struct pre-allocator. + * + * \param context The store context. + * \param ty The struct type. + * + * \return Returns a new struct ref pre-allocator. + */ +WASM_API_EXTERN wasmtime_struct_ref_pre_t * +wasmtime_struct_ref_pre_new(wasmtime_context_t *context, + const wasmtime_struct_type_t *ty); + +/** + * \brief Delete a struct pre-allocator. + */ +WASM_API_EXTERN void +wasmtime_struct_ref_pre_delete(wasmtime_struct_ref_pre_t *pre); + +/** + * \typedef wasmtime_structref_t + * \brief Convenience alias for #wasmtime_structref + * + * \struct wasmtime_structref + * \brief A WebAssembly `structref` value. + * + * This structure represents a reference to a GC struct. It is a subtype of + * `eqref` and `anyref`. + * + * Values must be explicitly unrooted via #wasmtime_structref_unroot. + */ +typedef struct wasmtime_structref { + /// Internal metadata. + uint64_t store_id; + /// Internal to Wasmtime. + uint32_t __private1; + /// Internal to Wasmtime. + uint32_t __private2; + /// Internal to Wasmtime. + void *__private3; +} wasmtime_structref_t; + +/// \brief Initialize the `ref` to a null `structref` value. +static inline void wasmtime_structref_set_null(wasmtime_structref_t *ref) { + ref->store_id = 0; +} + +/// \brief Returns whether the provided `ref` is a null `structref` value. +static inline bool wasmtime_structref_is_null(const wasmtime_structref_t *ref) { + return ref->store_id == 0; +} + +/** + * \brief Allocate a new struct instance. + * + * \param context The store context. + * \param pre The struct pre-allocator. + * \param fields Pointer to array of field values (#wasmtime_val_t). + * \param nfields Number of fields (must match the struct type). + * \param out Receives the new structref on success. + * + * \return NULL on success, or a #wasmtime_error_t on failure. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_structref_new( + wasmtime_context_t *context, const wasmtime_struct_ref_pre_t *pre, + const wasmtime_val_t *fields, size_t nfields, wasmtime_structref_t *out); + +/** + * \brief Clone a `structref`, creating a new root. + */ +WASM_API_EXTERN void +wasmtime_structref_clone(const wasmtime_structref_t *structref, + wasmtime_structref_t *out); + +/** + * \brief Unroot a `structref` to allow garbage collection. + */ +WASM_API_EXTERN void wasmtime_structref_unroot(wasmtime_structref_t *ref); + +/** + * \brief Upcast a `structref` to an `anyref`. + */ +WASM_API_EXTERN void +wasmtime_structref_to_anyref(const wasmtime_structref_t *structref, + wasmtime_anyref_t *out); + +/** + * \brief Upcast a `structref` to an `eqref`. + */ +WASM_API_EXTERN void +wasmtime_structref_to_eqref(const wasmtime_structref_t *structref, + wasmtime_eqref_t *out); + +/** + * \brief Read a field from a struct. + * + * \param context The store context. + * \param structref The struct to read from (not consumed). + * \param index The field index. + * \param out Receives the field value on success. + * + * \return NULL on success, or a #wasmtime_error_t on failure. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_structref_field(wasmtime_context_t *context, + const wasmtime_structref_t *structref, size_t index, + wasmtime_val_t *out); + +/** + * \brief Set a field of a struct. + * + * \param context The store context. + * \param structref The struct to write to (not consumed). + * \param index The field index. + * \param val The value to write (not consumed; caller must still unroot if + * applicable). + * + * \return NULL on success, or a #wasmtime_error_t on failure. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_structref_set_field(wasmtime_context_t *context, + const wasmtime_structref_t *structref, + size_t index, const wasmtime_val_t *val); + +/** + * \brief Test whether an `eqref` is a `structref`. + * + * Returns `false` for null references. + */ +WASM_API_EXTERN bool wasmtime_eqref_is_struct(wasmtime_context_t *context, + const wasmtime_eqref_t *eqref); + +/** + * \brief Downcast an `eqref` to a `structref`. + * + * If the given `eqref` is a `structref`, a new root for it is stored in `out` + * and `true` is returned. Otherwise `false` is returned and `out` is set to + * null. + */ +WASM_API_EXTERN bool wasmtime_eqref_as_struct(wasmtime_context_t *context, + const wasmtime_eqref_t *eqref, + wasmtime_structref_t *out); + +/** + * \brief An opaque handle to a WebAssembly array type definition. + * + * An array type describes the element type of an array. It is used to create a + * #wasmtime_array_ref_pre_t, which can then allocate array instances. + * + * Owned. Must be deleted with #wasmtime_array_type_delete. + */ +typedef struct wasmtime_array_type wasmtime_array_type_t; + +/** + * \brief Create a new array type. + * + * \param engine The engine to register the type with. + * \param field The element type descriptor. + * + * \return Returns a new array type. + */ +WASM_API_EXTERN wasmtime_array_type_t * +wasmtime_array_type_new(const wasm_engine_t *engine, + const wasmtime_field_type_t *field); + +/** + * \brief Delete an array type. + */ +WASM_API_EXTERN void wasmtime_array_type_delete(wasmtime_array_type_t *ty); + +/** + * \brief An opaque pre-allocated array layout for fast allocation. + * + * Created from a #wasmtime_array_type_t and a store context. Reusable for + * allocating many array instances of the same type. + * + * Owned. Must be deleted with #wasmtime_array_ref_pre_delete. + */ +typedef struct wasmtime_array_ref_pre wasmtime_array_ref_pre_t; + +/** + * \brief Create a new array pre-allocator. + * + * \param context The store context. + * \param ty The array type (not consumed; caller retains ownership). + * + * \return Returns a new array ref pre-allocator. + */ +WASM_API_EXTERN wasmtime_array_ref_pre_t * +wasmtime_array_ref_pre_new(wasmtime_context_t *context, + const wasmtime_array_type_t *ty); + +/** + * \brief Delete an array pre-allocator. + */ +WASM_API_EXTERN void +wasmtime_array_ref_pre_delete(wasmtime_array_ref_pre_t *pre); + +/** + * \typedef wasmtime_arrayref_t + * \brief Convenience alias for #wasmtime_arrayref + * + * \struct wasmtime_arrayref + * \brief A WebAssembly `arrayref` value. + * + * This structure represents a reference to a GC array. It is a subtype of + * `eqref` and `anyref`. + * + * Values must be explicitly unrooted via #wasmtime_arrayref_unroot. + */ +typedef struct wasmtime_arrayref { + /// Internal metadata. + uint64_t store_id; + /// Internal to Wasmtime. + uint32_t __private1; + /// Internal to Wasmtime. + uint32_t __private2; + /// Internal to Wasmtime. + void *__private3; +} wasmtime_arrayref_t; + +/// \brief Initialize the `ref` to a null `arrayref` value. +static inline void wasmtime_arrayref_set_null(wasmtime_arrayref_t *ref) { + ref->store_id = 0; +} + +/// \brief Returns whether the provided `ref` is a null `arrayref` value. +static inline bool wasmtime_arrayref_is_null(const wasmtime_arrayref_t *ref) { + return ref->store_id == 0; +} + +/** + * \brief Allocate a new array instance. + * + * All elements are initialized to the same value. + * + * \param context The store context. + * \param pre The array pre-allocator. + * \param elem The initial element value. + * \param len The number of elements. + * \param out Receives the new arrayref on success. + * + * \return NULL on success, or a #wasmtime_error_t on failure. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_arrayref_new( + wasmtime_context_t *context, const wasmtime_array_ref_pre_t *pre, + const wasmtime_val_t *elem, uint32_t len, wasmtime_arrayref_t *out); + +/** + * \brief Clone an `arrayref`, creating a new root. + */ +WASM_API_EXTERN void +wasmtime_arrayref_clone(const wasmtime_arrayref_t *arrayref, + wasmtime_arrayref_t *out); + +/** + * \brief Unroot an `arrayref` to allow garbage collection. + */ +WASM_API_EXTERN void wasmtime_arrayref_unroot(wasmtime_arrayref_t *ref); + +/** + * \brief Upcast an `arrayref` to an `anyref`. + */ +WASM_API_EXTERN void +wasmtime_arrayref_to_anyref(const wasmtime_arrayref_t *arrayref, + wasmtime_anyref_t *out); + +/** + * \brief Upcast an `arrayref` to an `eqref`. + */ +WASM_API_EXTERN void +wasmtime_arrayref_to_eqref(const wasmtime_arrayref_t *arrayref, + wasmtime_eqref_t *out); + +/** + * \brief Get the length of an array. + * + * \param context The store context. + * \param arrayref The array (not consumed). + * \param out Receives the length on success. + * + * \return NULL on success, or a #wasmtime_error_t on failure. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_arrayref_len(wasmtime_context_t *context, + const wasmtime_arrayref_t *arrayref, uint32_t *out); + +/** + * \brief Read an element from an array. + * + * \param context The store context. + * \param arrayref The array (not consumed). + * \param index The element index. + * \param out Receives the element value on success. + * + * \return NULL on success, or a #wasmtime_error_t on failure. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_arrayref_get(wasmtime_context_t *context, + const wasmtime_arrayref_t *arrayref, uint32_t index, + wasmtime_val_t *out); + +/** + * \brief Set an element of an array. + * + * \param context The store context. + * \param arrayref The array (not consumed). + * \param index The element index. + * \param val The value to write. + * + * \return NULL on success, or a #wasmtime_error_t on failure. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_arrayref_set(wasmtime_context_t *context, + const wasmtime_arrayref_t *arrayref, uint32_t index, + const wasmtime_val_t *val); + +/** + * \brief Test whether an `eqref` is an `arrayref`. + * + * Returns `false` for null references. + */ +WASM_API_EXTERN bool wasmtime_eqref_is_array(wasmtime_context_t *context, + const wasmtime_eqref_t *eqref); + +/** + * \brief Downcast an `eqref` to an `arrayref`. + * + * If the given `eqref` is an `arrayref`, a new root for it is stored in `out` + * and `true` is returned. Otherwise `false` is returned and `out` is set to + * null. + */ +WASM_API_EXTERN bool wasmtime_eqref_as_array(wasmtime_context_t *context, + const wasmtime_eqref_t *eqref, + wasmtime_arrayref_t *out); + +/** + * \brief Test whether an `anyref` is an `eqref`. + * + * Returns `false` for null references. + */ +WASM_API_EXTERN bool wasmtime_anyref_is_eqref(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref); + +/** + * \brief Downcast an `anyref` to an `eqref`. + * + * If the given `anyref` is an `eqref`, a new root is stored in `out` and + * `true` is returned. Otherwise `false` is returned and `out` is set to null. + */ +WASM_API_EXTERN bool wasmtime_anyref_as_eqref(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref, + wasmtime_eqref_t *out); + +/** + * \brief Test whether an `anyref` is a `structref`. + * + * Returns `false` for null references. + */ +WASM_API_EXTERN bool wasmtime_anyref_is_struct(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref); + +/** + * \brief Downcast an `anyref` to a `structref`. + * + * If the given `anyref` is a `structref`, a new root is stored in `out` and + * `true` is returned. Otherwise `false` is returned and `out` is set to null. + */ +WASM_API_EXTERN bool wasmtime_anyref_as_struct(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref, + wasmtime_structref_t *out); + +/** + * \brief Test whether an `anyref` is an `arrayref`. + * + * Returns `false` for null references. + */ +WASM_API_EXTERN bool wasmtime_anyref_is_array(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref); + +/** + * \brief Downcast an `anyref` to an `arrayref`. + * + * If the given `anyref` is an `arrayref`, a new root is stored in `out` and + * `true` is returned. Otherwise `false` is returned and `out` is set to null. + */ +WASM_API_EXTERN bool wasmtime_anyref_as_array(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref, + wasmtime_arrayref_t *out); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_GC_H diff --git a/crates/c-api/include/wasmtime/gc.hh b/crates/c-api/include/wasmtime/gc.hh new file mode 100644 index 000000000000..7ce47841f66f --- /dev/null +++ b/crates/c-api/include/wasmtime/gc.hh @@ -0,0 +1,479 @@ +/** + * \file wasmtime/gc.hh + * + * C++ API for WebAssembly GC types: eqref, structref, and arrayref. + */ + +#ifndef WASMTIME_GC_HH +#define WASMTIME_GC_HH + +#include +#include +#include + +namespace wasmtime { + +class StructRef; +class ArrayRef; + +/** + * \brief Representation of a WebAssembly `eqref` value. + * + * An `eqref` is a reference to a GC object that supports equality testing. + * Subtypes include `structref`, `arrayref`, and `i31ref`. + * + * Like all GC references, `EqRef` values are rooted in a `Store` and must be + * unrooted (by destruction or move) to allow garbage collection. + */ +class EqRef { + friend class Val; + friend class AnyRef; + + wasmtime_eqref_t val; + +public: + /// Creates a new `EqRef` from its C-API representation. + explicit EqRef(wasmtime_eqref_t val) : val(val) {} + + /// Copy constructor. + EqRef(const EqRef &other) { wasmtime_eqref_clone(&other.val, &val); } + + /// Copy assignment. + EqRef &operator=(const EqRef &other) { + wasmtime_eqref_unroot(&val); + wasmtime_eqref_clone(&other.val, &val); + return *this; + } + + /// Move constructor. + EqRef(EqRef &&other) { + val = other.val; + wasmtime_eqref_set_null(&other.val); + } + + /// Move assignment. + EqRef &operator=(EqRef &&other) { + wasmtime_eqref_unroot(&val); + val = other.val; + wasmtime_eqref_set_null(&other.val); + return *this; + } + + ~EqRef() { wasmtime_eqref_unroot(&val); } + + /// Create an `eqref` from an i31 value. + static EqRef from_i31(Store::Context cx, uint32_t val) { + wasmtime_eqref_t out; + wasmtime_eqref_from_i31(cx.capi(), val, &out); + return EqRef(out); + } + + /// Returns `true` if this eqref is an i31ref. + bool is_i31(Store::Context cx) const { + return wasmtime_eqref_is_i31(cx.capi(), &val); + } + + /// Get the i31 value as an unsigned 32-bit integer. + /// Returns `std::nullopt` if this eqref is not an i31ref. + std::optional i31_get_u(Store::Context cx) const { + uint32_t dst; + if (wasmtime_eqref_i31_get_u(cx.capi(), &val, &dst)) + return dst; + return std::nullopt; + } + + /// Get the i31 value as a signed 32-bit integer. + /// Returns `std::nullopt` if this eqref is not an i31ref. + std::optional i31_get_s(Store::Context cx) const { + int32_t dst; + if (wasmtime_eqref_i31_get_s(cx.capi(), &val, &dst)) + return dst; + return std::nullopt; + } + + /// Returns `true` if this eqref is a structref. + bool is_struct(Store::Context cx) const { + return wasmtime_eqref_is_struct(cx.capi(), &val); + } + + /// Returns `true` if this eqref is an arrayref. + bool is_array(Store::Context cx) const { + return wasmtime_eqref_is_array(cx.capi(), &val); + } + + /// Upcast this `eqref` to an `anyref`. + AnyRef to_anyref() const { + wasmtime_anyref_t out; + wasmtime_eqref_to_anyref(&val, &out); + return AnyRef(out); + } + + /// Downcast this `eqref` into a `structref`. + // + // as_struct() defined after StructRef below. + inline StructRef as_struct(Store::Context cx) const; + + /// Downcast this `eqref` into an `arrayref`. + // + // as_array() defined after ArrayRef below. + inline ArrayRef as_array(Store::Context cx) const; +}; + +/** + * \brief Describes the storage type and mutability of a struct field or array + * element. + */ +struct FieldType { + /// The field's storage kind. + wasmtime_storage_kind_t kind; + /// Whether the field is mutable or not. + bool mutable_; + + /// Create a mutable field type. + static FieldType mut_(wasmtime_storage_kind_t k) { return {k, true}; } + /// Create an immutable field type. + static FieldType const_(wasmtime_storage_kind_t k) { return {k, false}; } +}; + +/** + * \brief Owned handle to a WebAssembly struct type definition. + * + * Create with StructType::create, then use with StructRefPre to allocate + * instances. + */ +class StructType { + struct Deleter { + void operator()(wasmtime_struct_type_t *p) const { + wasmtime_struct_type_delete(p); + } + }; + std::unique_ptr ptr; + +public: + /// Create a new struct type with the given fields. + static StructType create(const Engine &engine, + const std::vector &fields) { + static_assert(sizeof(FieldType) == sizeof(wasmtime_field_type_t)); + auto *raw = wasmtime_struct_type_new( + engine.capi(), + reinterpret_cast(fields.data()), + fields.size()); + StructType ty; + ty.ptr.reset(raw); + return ty; + } + + /// Get the underlying C pointer (non-owning). + const wasmtime_struct_type_t *capi() const { return ptr.get(); } + +private: + StructType() = default; + friend class StructRefPre; +}; + +/** + * \brief Pre-allocated struct layout for fast allocation of struct instances. + * + * Created from a StructType and a store context. Reusable for allocating + * many struct instances of the same type. + */ +class StructRefPre { + friend class StructRef; + WASMTIME_OWN_WRAPPER(StructRefPre, wasmtime_struct_ref_pre) + +public: + /// Create a new struct pre-allocator. + static StructRefPre create(Store::Context cx, const StructType &ty) { + auto *raw = wasmtime_struct_ref_pre_new(cx.capi(), ty.capi()); + StructRefPre pre(raw); + return pre; + } +}; + +/** + * \brief Representation of a WebAssembly `structref` value. + * + * A `structref` is a reference to a GC struct instance. It is a subtype + * of `eqref` and `anyref`. + */ +class StructRef { + friend class EqRef; + friend class Val; + friend class AnyRef; + + wasmtime_structref_t val; + +public: + /// Create a `StructRef` from its C-API representation. + explicit StructRef(wasmtime_structref_t val) : val(val) {} + + /// Clone a `StructRef`. + StructRef(const StructRef &other) { + wasmtime_structref_clone(&other.val, &val); + } + + /// Clone a `StructRef` into this one. + StructRef &operator=(const StructRef &other) { + wasmtime_structref_unroot(&val); + wasmtime_structref_clone(&other.val, &val); + return *this; + } + + /// Move a `StructRef`. + StructRef(StructRef &&other) { + val = other.val; + wasmtime_structref_set_null(&other.val); + } + + /// Move a `StructRef` into this one. + StructRef &operator=(StructRef &&other) { + wasmtime_structref_unroot(&val); + val = other.val; + wasmtime_structref_set_null(&other.val); + return *this; + } + + /// Unroot this `StructRef`. + ~StructRef() { wasmtime_structref_unroot(&val); } + + /// Allocate a new struct instance. + static Result create(Store::Context cx, const StructRefPre &pre, + const std::vector &fields) { + std::vector c_fields; + c_fields.reserve(fields.size()); + for (auto &f : fields) { + c_fields.push_back(f.val); + } + + wasmtime_structref_t out; + auto *err = wasmtime_structref_new(cx.capi(), pre.capi(), c_fields.data(), + c_fields.size(), &out); + if (err) + return Result(Error(err)); + return Result(StructRef(out)); + } + + /// Read a field from the struct. + Result field(Store::Context cx, size_t index) const { + wasmtime_val_t out; + auto *err = wasmtime_structref_field(cx.capi(), &val, index, &out); + if (err) + return Result(Error(err)); + return Result(Val(out)); + } + + /// Set a field of the struct. + Result set_field(Store::Context cx, size_t index, + const Val &value) const { + auto *err = + wasmtime_structref_set_field(cx.capi(), &val, index, &value.val); + if (err) + return Result(Error(err)); + return Result(std::monostate{}); + } + + /// Upcast to anyref. + AnyRef to_anyref() const { + wasmtime_anyref_t out; + wasmtime_structref_to_anyref(&val, &out); + return AnyRef(out); + } + + /// Upcast to eqref. + EqRef to_eqref() const { + wasmtime_eqref_t out; + wasmtime_structref_to_eqref(&val, &out); + return EqRef(out); + } +}; + +inline StructRef EqRef::as_struct(Store::Context cx) const { + wasmtime_structref_t out; + wasmtime_eqref_as_struct(cx.capi(), &val, &out); + return StructRef(out); +} + +/** + * \brief Owned handle to a WebAssembly array type definition. + */ +class ArrayType { + struct Deleter { + void operator()(wasmtime_array_type_t *p) const { + wasmtime_array_type_delete(p); + } + }; + std::unique_ptr ptr; + +public: + /// Create a new array type with the given element type. + static ArrayType create(const Engine &engine, const FieldType &field) { + static_assert(sizeof(FieldType) == sizeof(wasmtime_field_type_t)); + auto *raw = wasmtime_array_type_new( + engine.capi(), reinterpret_cast(&field)); + ArrayType ty; + ty.ptr.reset(raw); + return ty; + } + + /// Get the underlying C pointer (non-owning). + const wasmtime_array_type_t *capi() const { return ptr.get(); } + +private: + ArrayType() = default; + friend class ArrayRefPre; +}; + +/** + * \brief Pre-allocated array layout for fast allocation of array instances. + * + * Created from a ArrayType and a store context. Reusable for allocating + * many array instances of the same type. + */ +class ArrayRefPre { + friend class ArrayRef; + WASMTIME_OWN_WRAPPER(ArrayRefPre, wasmtime_array_ref_pre) + +public: + /// Create a new array pre-allocator. + static ArrayRefPre create(Store::Context cx, const ArrayType &ty) { + auto *raw = wasmtime_array_ref_pre_new(cx.capi(), ty.capi()); + ArrayRefPre pre(raw); + return pre; + } +}; + +/** + * \brief Representation of a WebAssembly `arrayref` value. + * + * An `arrayref` is a reference to a GC array instance. It is a subtype + * of `eqref` and `anyref`. + */ +class ArrayRef { + friend class EqRef; + friend class Val; + friend class AnyRef; + + wasmtime_arrayref_t val; + +public: + /// Create a `ArrayRef` from its C-API representation. + explicit ArrayRef(wasmtime_arrayref_t val) : val(val) {} + + /// Clone a `ArrayRef`. + ArrayRef(const ArrayRef &other) { wasmtime_arrayref_clone(&other.val, &val); } + + /// Clone a `ArrayRef` into this one. + ArrayRef &operator=(const ArrayRef &other) { + wasmtime_arrayref_unroot(&val); + wasmtime_arrayref_clone(&other.val, &val); + return *this; + } + + /// Move a `ArrayRef`. + ArrayRef(ArrayRef &&other) { + val = other.val; + wasmtime_arrayref_set_null(&other.val); + } + + /// Move a `ArrayRef` into this one. + ArrayRef &operator=(ArrayRef &&other) { + wasmtime_arrayref_unroot(&val); + val = other.val; + wasmtime_arrayref_set_null(&other.val); + return *this; + } + + /// Unroot this `ArrayRef`. + ~ArrayRef() { wasmtime_arrayref_unroot(&val); } + + /// Allocate a new array with all elements set to the same value. + static Result create(Store::Context cx, const ArrayRefPre &pre, + const Val &elem, uint32_t len) { + wasmtime_arrayref_t out; + auto *err = + wasmtime_arrayref_new(cx.capi(), pre.capi(), &elem.val, len, &out); + if (err) + return Result(Error(err)); + return Result(ArrayRef(out)); + } + + /// Get the length of the array. + Result len(Store::Context cx) const { + uint32_t out; + auto *err = wasmtime_arrayref_len(cx.capi(), &val, &out); + if (err) + return Result(Error(err)); + return Result(out); + } + + /// Read an element from the array. + Result get(Store::Context cx, uint32_t index) const { + wasmtime_val_t out; + auto *err = wasmtime_arrayref_get(cx.capi(), &val, index, &out); + if (err) + return Result(Error(err)); + return Result(Val(out)); + } + + /// Set an element of the array. + Result set(Store::Context cx, uint32_t index, + const Val &value) const { + auto *err = wasmtime_arrayref_set(cx.capi(), &val, index, &value.val); + if (err) + return Result(Error(err)); + return Result(std::monostate{}); + } + + /// Upcast to anyref. + AnyRef to_anyref() const { + wasmtime_anyref_t out; + wasmtime_arrayref_to_anyref(&val, &out); + return AnyRef(out); + } + + /// Upcast to eqref. + EqRef to_eqref() const { + wasmtime_eqref_t out; + wasmtime_arrayref_to_eqref(&val, &out); + return EqRef(out); + } +}; + +inline ArrayRef EqRef::as_array(Store::Context cx) const { + wasmtime_arrayref_t out; + wasmtime_eqref_as_array(cx.capi(), &val, &out); + return ArrayRef(out); +} + +// AnyRef downcast method definitions (declared in val.hh) +inline bool AnyRef::is_eqref(Store::Context cx) const { + return wasmtime_anyref_is_eqref(cx.capi(), &val); +} +inline bool AnyRef::is_struct(Store::Context cx) const { + return wasmtime_anyref_is_struct(cx.capi(), &val); +} +inline bool AnyRef::is_array(Store::Context cx) const { + return wasmtime_anyref_is_array(cx.capi(), &val); +} +inline std::optional AnyRef::as_eqref(Store::Context cx) const { + wasmtime_eqref_t out; + if (wasmtime_anyref_as_eqref(cx.capi(), &val, &out)) + return EqRef(out); + return std::nullopt; +} +inline std::optional AnyRef::as_struct(Store::Context cx) const { + wasmtime_structref_t out; + if (wasmtime_anyref_as_struct(cx.capi(), &val, &out)) + return StructRef(out); + return std::nullopt; +} +inline std::optional AnyRef::as_array(Store::Context cx) const { + wasmtime_arrayref_t out; + if (wasmtime_anyref_as_array(cx.capi(), &val, &out)) + return ArrayRef(out); + return std::nullopt; +} + +} // namespace wasmtime + +#endif // WASMTIME_GC_HH diff --git a/crates/c-api/include/wasmtime/instance.hh b/crates/c-api/include/wasmtime/instance.hh index 1aa75a060f92..636c058b677c 100644 --- a/crates/c-api/include/wasmtime/instance.hh +++ b/crates/c-api/include/wasmtime/instance.hh @@ -13,6 +13,7 @@ #include #include #include +#include namespace wasmtime { diff --git a/crates/c-api/include/wasmtime/store.h b/crates/c-api/include/wasmtime/store.h index aefe31c959a4..f440db1a3f2e 100644 --- a/crates/c-api/include/wasmtime/store.h +++ b/crates/c-api/include/wasmtime/store.h @@ -195,6 +195,19 @@ wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi); #endif // WASMTIME_FEATURE_WASI +#ifdef WASMTIME_FEATURE_WASI_HTTP + +/** + * \brief Initializes the WASI HTTP context for this store. + * + * Must be called before instantiating a component that uses `wasi:http`. + * Requires WASI to be configured first via #wasmtime_context_set_wasi. + */ +WASM_API_EXTERN void +wasmtime_context_set_wasi_http(wasmtime_context_t *context); + +#endif // WASMTIME_FEATURE_WASI_HTTP + /** * \brief Configures the relative deadline at which point WebAssembly code will * trap or invoke the callback function. diff --git a/crates/c-api/include/wasmtime/store.hh b/crates/c-api/include/wasmtime/store.hh index 1f386751b7c7..4cad54b39e8d 100644 --- a/crates/c-api/include/wasmtime/store.hh +++ b/crates/c-api/include/wasmtime/store.hh @@ -18,6 +18,9 @@ namespace wasmtime { class Caller; +class Tag; +class Exn; +class Trap; /// \brief An enum for the behavior before extending the epoch deadline. enum class DeadlineKind { @@ -72,8 +75,10 @@ public: friend class Linker; friend class ExternRef; friend class AnyRef; + friend class EqRef; friend class Val; friend class Store; + friend class Tag; wasmtime_context_t *ptr; public: @@ -162,6 +167,23 @@ public: wasmtime_context_set_epoch_deadline(ptr, ticks_beyond_current); } +#ifdef WASMTIME_FEATURE_GC + /// \brief Sets the pending exception on the store and returns a Trap. + /// + /// This transfers ownership of `exn`. After this call, `exn` is consumed. + /// Returns a Trap that the host callback MUST return to propagate the + /// exception through Wasm catch blocks. + inline Trap throw_exception(Exn exn); + + /// \brief Takes the pending exception from the store, if any. + /// + /// Returns the exception if one was pending, or std::nullopt. + inline std::optional take_exception(); + + /// \brief Tests whether there is a pending exception on the store. + inline bool has_exception(); +#endif // WASMTIME_FEATURE_GC + /// \brief Returns the underlying C API pointer. const wasmtime_context_t *capi() const { return ptr; } diff --git a/crates/c-api/include/wasmtime/tag.h b/crates/c-api/include/wasmtime/tag.h new file mode 100644 index 000000000000..9335a6a05a02 --- /dev/null +++ b/crates/c-api/include/wasmtime/tag.h @@ -0,0 +1,75 @@ +/** + * \file wasmtime/tag.h + * + * \brief Wasmtime APIs for interacting with WebAssembly tags. + * + * Tags are used to create and identify exception objects. A tag describes + * the signature (payload types) of exceptions created with it. + */ + +#ifndef WASMTIME_TAG_H +#define WASMTIME_TAG_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// \brief Representation of a tag in Wasmtime. +/// +/// Tags in Wasmtime are represented as an index into a store and don't +/// have any data or destructor associated with the #wasmtime_tag_t value. +/// Tags cannot interoperate between #wasmtime_store_t instances and if the +/// wrong tag is passed to the wrong store then it may trigger an assertion +/// to abort the process. +typedef struct wasmtime_tag { + struct { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Private field for Wasmtime. + uint32_t __private1; + }; + /// Private field for Wasmtime. + uint32_t __private2; +} wasmtime_tag_t; + +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// tag +#define WASMTIME_EXTERN_TAG 5 + +/** + * \brief Creates a new host-defined tag. + * + * \param store the store in which to create the tag + * \param tt the tag type that describes the tag's exception payload + * \param ret on success, filled with the new tag + * + * \return NULL on success, otherwise an error describing the failure. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_tag_new(wasmtime_context_t *store, + const wasm_tagtype_t *tt, + wasmtime_tag_t *ret); + +/** + * \brief Returns the type of the given tag. + * + * The returned #wasm_tagtype_t is owned by the caller. + */ +WASM_API_EXTERN wasm_tagtype_t * +wasmtime_tag_type(const wasmtime_context_t *store, const wasmtime_tag_t *tag); + +/** + * \brief Tests whether two tags are identical (same definition). + */ +WASM_API_EXTERN bool wasmtime_tag_eq(const wasmtime_context_t *store, + const wasmtime_tag_t *a, + const wasmtime_tag_t *b); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_TAG_H diff --git a/crates/c-api/include/wasmtime/tag.hh b/crates/c-api/include/wasmtime/tag.hh new file mode 100644 index 000000000000..fac4ae4e898d --- /dev/null +++ b/crates/c-api/include/wasmtime/tag.hh @@ -0,0 +1,65 @@ +/** + * \file wasmtime/tag.hh + */ + +#ifndef WASMTIME_TAG_HH +#define WASMTIME_TAG_HH + +#include +#include +#include +#include + +namespace wasmtime { + +/** + * \brief A WebAssembly tag. + * + * Tags are used to identify exception types. A tag describes the payload + * signature of exceptions created with it. + * + * Note that this type does not itself own any resources. It points to resources + * owned within a `Store` and the `Store` must be passed in as the first + * argument to the functions defined on `Tag`. Note that if the wrong `Store` + * is passed in then the process will be aborted. + */ +class Tag { + friend class Instance; + wasmtime_tag_t tag; + +public: + /// Creates a tag from the raw underlying C API representation. + Tag(wasmtime_tag_t tag) : tag(tag) {} + + /** + * \brief Create a new host-defined tag. + * + * \param cx the store in which to create the tag + * \param ty the tag type describing the exception payload + */ + static Result create(Store::Context cx, const TagType &ty) { + wasmtime_tag_t tag; + auto *error = wasmtime_tag_new(cx.ptr, ty.ptr.get(), &tag); + if (error != nullptr) { + return Error(error); + } + return Tag(tag); + } + + /// Returns the type of this tag. + TagType type(Store::Context cx) const { + return TagType(wasmtime_tag_type(cx.ptr, &tag)); + } + + /// Tests whether two tags are identical. + bool eq(Store::Context cx, const Tag &other) const { + return wasmtime_tag_eq(cx.ptr, &tag, &other.tag); + } + + /// Returns the raw underlying C API tag this is using. + const wasmtime_tag_t &capi() const { return tag; } +}; + +} // namespace wasmtime + +#endif // WASMTIME_TAG_HH diff --git a/crates/c-api/include/wasmtime/trap.h b/crates/c-api/include/wasmtime/trap.h index f7aae70d7d6c..6c29b991a5ae 100644 --- a/crates/c-api/include/wasmtime/trap.h +++ b/crates/c-api/include/wasmtime/trap.h @@ -131,6 +131,9 @@ enum wasmtime_trap_code_enum { WASMTIME_TRAP_CODE_UNSUPPORTED_CALLBACK_CODE = 43, /// Cannot resume a thread which is not suspended. WASMTIME_TRAP_CODE_CANNOT_RESUME_THREAD = 44, + /// Cannot issue a read/write on a future/stream while there is a + /// pending operation already. + WASMTIME_TRAP_CODE_CONCURRENT_FUTURE_STREAM_OP = 45, }; /** diff --git a/crates/c-api/include/wasmtime/types.hh b/crates/c-api/include/wasmtime/types.hh index 3694740cce8b..d44633f72c8d 100644 --- a/crates/c-api/include/wasmtime/types.hh +++ b/crates/c-api/include/wasmtime/types.hh @@ -8,6 +8,7 @@ #include #include #include +#include #include #endif // WASMTIME_TYPES_HH diff --git a/crates/c-api/include/wasmtime/types/extern.hh b/crates/c-api/include/wasmtime/types/extern.hh index 88412a0e423f..755c3f826534 100644 --- a/crates/c-api/include/wasmtime/types/extern.hh +++ b/crates/c-api/include/wasmtime/types/extern.hh @@ -13,6 +13,7 @@ #include #include #include +#include namespace wasmtime { @@ -31,7 +32,7 @@ public: /// otherwise this is used to determine what the actual type of the outer item /// is. typedef std::variant + MemoryType::Ref, TagType::Ref> Ref; /// Extract the type of the item imported by the provided type. @@ -59,6 +60,8 @@ private: return wasm_externtype_as_tabletype_const(ptr); case WASM_EXTERN_MEMORY: return wasm_externtype_as_memorytype_const(ptr); + case WASM_EXTERN_TAG: + return wasm_externtype_as_tagtype_const(ptr); } std::abort(); } diff --git a/crates/c-api/include/wasmtime/types/func.hh b/crates/c-api/include/wasmtime/types/func.hh index a3bc2a686831..0971fe957963 100644 --- a/crates/c-api/include/wasmtime/types/func.hh +++ b/crates/c-api/include/wasmtime/types/func.hh @@ -15,6 +15,7 @@ namespace wasmtime { class FuncType { friend class Func; friend class Linker; + friend class TagType; struct deleter { void operator()(wasm_functype_t *p) const { wasm_functype_delete(p); } diff --git a/crates/c-api/include/wasmtime/types/tag.hh b/crates/c-api/include/wasmtime/types/tag.hh new file mode 100644 index 000000000000..28ce8a8df4d2 --- /dev/null +++ b/crates/c-api/include/wasmtime/types/tag.hh @@ -0,0 +1,87 @@ +/** + * \file wasmtime/types/tag.hh + */ + +#ifndef WASMTIME_TYPES_TAG_HH +#define WASMTIME_TYPES_TAG_HH + +#include +#include +#include + +namespace wasmtime { + +/** + * \brief Type information for a WebAssembly exception tag. + * + * A tag type is described by a function type whose parameters are the exception + * payload types (and whose results are empty under the current proposal). + */ +class TagType { + friend class Tag; + friend class Exn; + + struct deleter { + void operator()(wasm_tagtype_t *p) const { wasm_tagtype_delete(p); } + }; + + std::unique_ptr ptr; + +public: + /// Non-owning reference to a `TagType`, must not be used after the original + /// owner is deleted. + class Ref { + friend class TagType; + const wasm_tagtype_t *ptr; + + public: + /// Creates a reference from the raw underlying C API representation. + Ref(const wasm_tagtype_t *ptr) : ptr(ptr) {} + /// Creates a reference to the provided `TagType`. + Ref(const TagType &ty) : Ref(ty.ptr.get()) {} + + /// Returns the function type describing the exception payload of this tag. + /// + /// The caller owns the returned `FuncType`. + FuncType functype() const { + return FuncType(wasm_functype_copy(wasm_tagtype_functype(ptr))); + } + }; + +private: + Ref ref; + TagType(wasm_tagtype_t *ptr) : ptr(ptr), ref(ptr) {} + +public: + /// Creates a new tag type from the given function type. + /// Copies `functype` so the original is not consumed. + explicit TagType(const FuncType &functype) + : TagType(wasm_tagtype_new(wasm_functype_copy(functype.ptr.get()))) {} + + /// Copies a reference into a uniquely owned tag type. + TagType(Ref other) : TagType(wasm_tagtype_copy(other.ptr)) {} + /// Copies another tag type into this one. + TagType(const TagType &other) : TagType(wasm_tagtype_copy(other.ptr.get())) {} + /// Copies another tag type into this one. + TagType &operator=(const TagType &other) { + ptr.reset(wasm_tagtype_copy(other.ptr.get())); + ref = ptr.get(); + return *this; + } + ~TagType() = default; + /// Moves the tag type resources from another type to this one. + TagType(TagType &&other) = default; + /// Moves the tag type resources from another type to this one. + TagType &operator=(TagType &&other) = default; + + /// \brief Returns the underlying `Ref`, a non-owning reference pointing to + /// this instance. + Ref *operator->() { return &ref; } + /// \brief Returns the underlying `Ref`, a non-owning reference pointing to + /// this instance. + Ref *operator*() { return &ref; } +}; + +}; // namespace wasmtime + +#endif // WASMTIME_TYPES_TAG_HH diff --git a/crates/c-api/include/wasmtime/types/val.hh b/crates/c-api/include/wasmtime/types/val.hh index 2b063e176b62..a42d445b6d98 100644 --- a/crates/c-api/include/wasmtime/types/val.hh +++ b/crates/c-api/include/wasmtime/types/val.hh @@ -30,6 +30,8 @@ enum class ValKind { FuncRef, /// WebAssembly's `anyref` type AnyRef, + /// WebAssembly's `exnref` type + ExnRef, }; /// Helper X macro to construct statement for each enumerator in `ValKind`. @@ -42,6 +44,7 @@ enum class ValKind { X(ExternRef, "externref", WASM_EXTERNREF) \ X(FuncRef, "funcref", WASM_FUNCREF) \ X(AnyRef, "anyref", WASMTIME_ANYREF) \ + X(ExnRef, "exnref", WASMTIME_EXNREF) \ X(V128, "v128", WASMTIME_V128) /// \brief Used to print a ValKind. @@ -68,7 +71,6 @@ class ValType { friend class TableType; friend class GlobalType; friend class FuncType; - struct deleter { void operator()(wasm_valtype_t *p) const { wasm_valtype_delete(p); } }; diff --git a/crates/c-api/include/wasmtime/val.h b/crates/c-api/include/wasmtime/val.h index 9d7e2aea94c0..bb4cfa993cf0 100644 --- a/crates/c-api/include/wasmtime/val.h +++ b/crates/c-api/include/wasmtime/val.h @@ -15,6 +15,10 @@ extern "C" { #endif +struct wasmtime_eqref; +/// Convenience alias for #wasmtime_eqref +typedef struct wasmtime_eqref wasmtime_eqref_t; + /** * \typedef wasmtime_anyref_t * \brief Convenience alias for #wasmtime_anyref @@ -77,6 +81,19 @@ static inline bool wasmtime_anyref_is_null(const wasmtime_anyref_t *ref) { WASM_API_EXTERN void wasmtime_anyref_clone(const wasmtime_anyref_t *anyref, wasmtime_anyref_t *out); +/** + * \brief Downcast an `anyref` to an `eqyref`. + * + * Returns `true` if the downcast succeeded, and `out` is initialized. Returns + * `false` if the downcast failed, and `out` is left uninitialized. + * + * The original `anyref` is not consumed; `out` receives a new cloned root + * pointing to the same GC object as `anyref`. + */ +WASM_API_EXTERN bool wasmtime_anyref_to_eqref(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref, + wasmtime_eqref_t *out); + /** * \brief Unroots the `ref` provided within the `context`. * @@ -128,6 +145,15 @@ WASM_API_EXTERN void wasmtime_anyref_from_i31(wasmtime_context_t *context, uint32_t i31val, wasmtime_anyref_t *out); +/** + * \brief Test whether an `anyref` is an `i31ref`. + * + * Returns `true` if the given `anyref` is an `i31ref`, `false` otherwise. + * Returns `false` for null references. + */ +WASM_API_EXTERN bool wasmtime_anyref_is_i31(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref); + /** * \brief Get the `anyref`'s underlying `i31ref` value, zero extended, if any. * @@ -288,6 +314,60 @@ WASM_API_EXTERN void wasmtime_externref_from_raw(wasmtime_context_t *context, WASM_API_EXTERN uint32_t wasmtime_externref_to_raw( wasmtime_context_t *context, const wasmtime_externref_t *ref); +/** + * \typedef wasmtime_exnref_t + * \brief Convenience alias for #wasmtime_exnref + * + * \struct wasmtime_exnref + * \brief A WebAssembly exception reference value. + * + * This structure represents an `exnref` value, which is a reference to a + * WebAssembly exception object. Like other GC reference types, it must be + * explicitly unrooted via #wasmtime_exnref_unroot to enable garbage + * collection. + * + * Null exnref values are represented with `store_id == 0`. + */ +typedef struct wasmtime_exnref { + /// Internal metadata tracking within the store, embedders should not + /// configure or modify these fields. + uint64_t store_id; + /// Internal to Wasmtime. + uint32_t __private1; + /// Internal to Wasmtime. + uint32_t __private2; + /// Internal to Wasmtime. + void *__private3; +} wasmtime_exnref_t; + +/// \brief Helper function to initialize the `ref` provided to a null exnref +/// value. +static inline void wasmtime_exnref_set_null(wasmtime_exnref_t *ref) { + ref->store_id = 0; +} + +/// \brief Helper function to return whether the provided `ref` points to a null +/// `exnref` value. +static inline bool wasmtime_exnref_is_null(const wasmtime_exnref_t *ref) { + return ref->store_id == 0; +} + +/** + * \brief Creates a new reference pointing to the same exception that `ref` + * points to. + * + * The returned reference is stored in `out`. + */ +WASM_API_EXTERN void wasmtime_exnref_clone(const wasmtime_exnref_t *ref, + wasmtime_exnref_t *out); + +/** + * \brief Unroots the `ref` provided, enabling future garbage collection. + * + * After this call, `ref` is left in an undefined state and should not be used. + */ +WASM_API_EXTERN void wasmtime_exnref_unroot(wasmtime_exnref_t *ref); + /// \brief Discriminant stored in #wasmtime_val::kind typedef uint8_t wasmtime_valkind_t; /// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i32 @@ -309,6 +389,9 @@ typedef uint8_t wasmtime_valkind_t; /// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an /// anyref #define WASMTIME_ANYREF 7 +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an +/// exnref +#define WASMTIME_EXNREF 8 /// \brief A 128-bit value representing the WebAssembly `v128` type. Bytes are /// stored in little-endian order. @@ -337,6 +420,8 @@ typedef union wasmtime_valunion { wasmtime_anyref_t anyref; /// Field used if #wasmtime_val_t::kind is #WASMTIME_EXTERNREF wasmtime_externref_t externref; + /// Field used if #wasmtime_val_t::kind is #WASMTIME_EXNREF + wasmtime_exnref_t exnref; /// Field used if #wasmtime_val_t::kind is #WASMTIME_FUNCREF /// /// Use `wasmtime_funcref_is_null` to test whether this is a null function @@ -464,7 +549,8 @@ typedef struct wasmtime_val { * \brief Unroot the value contained by `val`. * * This function will unroot any GC references that `val` points to, for - * example if it has the `WASMTIME_EXTERNREF` or `WASMTIME_ANYREF` kinds. This + * example if it has the `WASMTIME_EXTERNREF`, `WASMTIME_ANYREF`, or + * `WASMTIME_EXNREF` kinds. This * function leaves `val` in an undefined state and it should not be used again * without re-initializing. * diff --git a/crates/c-api/include/wasmtime/val.hh b/crates/c-api/include/wasmtime/val.hh index 5902042dff4b..7f27aad84fbf 100644 --- a/crates/c-api/include/wasmtime/val.hh +++ b/crates/c-api/include/wasmtime/val.hh @@ -6,12 +6,17 @@ #define WASMTIME_VAL_HH #include +#include #include #include #include namespace wasmtime { +class EqRef; +class StructRef; +class ArrayRef; + /** * \brief Representation of a WebAssembly `externref` value. * @@ -99,6 +104,8 @@ public: } }; +class EqRef; + /** * \brief Representation of a WebAssembly `anyref` value. */ @@ -173,6 +180,29 @@ public: return ret; return std::nullopt; } + + /// \brief Returns `true` if this anyref is an i31ref. + bool is_i31(Store::Context cx) const { + return wasmtime_anyref_is_i31(cx.ptr, &val); + } + + /// \brief Returns `true` if this anyref is an eqref. + inline bool is_eqref(Store::Context cx) const; + + /// \brief Returns `true` if this anyref is a structref. + inline bool is_struct(Store::Context cx) const; + + /// \brief Returns `true` if this anyref is an arrayref. + inline bool is_array(Store::Context cx) const; + + /// \brief Downcast to eqref. Returns null eqref if not an eqref. + inline std::optional as_eqref(Store::Context cx) const; + + /// \brief Downcast to structref. Returns null structref if not a structref. + inline std::optional as_struct(Store::Context cx) const; + + /// \brief Downcast to arrayref. Returns null arrayref if not an arrayref. + inline std::optional as_array(Store::Context cx) const; }; /// \brief Container for the `v128` WebAssembly type. @@ -205,6 +235,9 @@ class Val { friend class Global; friend class Table; friend class Func; + friend class Exn; + friend class StructRef; + friend class ArrayRef; wasmtime_val_t val; @@ -317,6 +350,8 @@ public: return ValKind::ExternRef; case WASMTIME_ANYREF: return ValKind::AnyRef; + case WASMTIME_EXNREF: + return ValKind::ExnRef; case WASMTIME_V128: return ValKind::V128; } diff --git a/crates/c-api/src/component/component.rs b/crates/c-api/src/component/component.rs index cfededb3dc0b..1db3dbaa55d5 100644 --- a/crates/c-api/src/component/component.rs +++ b/crates/c-api/src/component/component.rs @@ -10,6 +10,14 @@ pub extern "C" fn wasmtime_config_wasm_component_model_set(c: &mut wasm_config_t c.config.wasm_component_model(enable); } +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_config_wasm_component_model_map_set( + c: &mut wasm_config_t, + enable: bool, +) { + c.config.wasm_component_model_map(enable); +} + #[derive(Clone)] #[repr(transparent)] pub struct wasmtime_component_t { diff --git a/crates/c-api/src/component/linker.rs b/crates/c-api/src/component/linker.rs index b17045a08101..9af8c82c9ce7 100644 --- a/crates/c-api/src/component/linker.rs +++ b/crates/c-api/src/component/linker.rs @@ -167,6 +167,15 @@ pub unsafe extern "C" fn wasmtime_component_linker_add_wasip2( crate::handle_result(result, |_| ()) } +#[unsafe(no_mangle)] +#[cfg(feature = "wasi-http")] +pub unsafe extern "C" fn wasmtime_component_linker_add_wasi_http( + linker: &mut wasmtime_component_linker_t, +) -> Option> { + let result = wasmtime_wasi_http::p2::add_only_http_to_linker_sync(&mut linker.linker); + crate::handle_result(result, |_| ()) +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn wasmtime_component_linker_define_unknown_imports_as_traps( linker: &mut wasmtime_component_linker_t, diff --git a/crates/c-api/src/component/types/val.rs b/crates/c-api/src/component/types/val.rs index b705ada33031..c3e4f1630bb0 100644 --- a/crates/c-api/src/component/types/val.rs +++ b/crates/c-api/src/component/types/val.rs @@ -31,6 +31,7 @@ pub enum wasmtime_component_valtype_t { Future(Box), Stream(Box), ErrorContext, + Map(Box), } impl From for wasmtime_component_valtype_t { @@ -61,6 +62,7 @@ impl From for wasmtime_component_valtype_t { Type::Borrow(ty) => Self::Borrow(Box::new(ty.into())), Type::Future(ty) => Self::Future(Box::new(ty.into())), Type::Stream(ty) => Self::Stream(Box::new(ty.into())), + Type::Map(ty) => Self::Map(Box::new(ty.into())), Type::ErrorContext => Self::ErrorContext, } } @@ -108,6 +110,33 @@ pub extern "C" fn wasmtime_component_list_type_element( type_ret.write(ty.ty.ty().into()); } +type_wrapper! { + #[derive(PartialEq)] + pub struct wasmtime_component_map_type_t { + pub(crate) ty: Map, + } + + clone: wasmtime_component_map_type_clone, + delete: wasmtime_component_map_type_delete, + equal: wasmtime_component_map_type_equal, +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_component_map_type_key( + ty: &wasmtime_component_map_type_t, + type_ret: &mut MaybeUninit, +) { + type_ret.write(ty.ty.key().into()); +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_component_map_type_value( + ty: &wasmtime_component_map_type_t, + type_ret: &mut MaybeUninit, +) { + type_ret.write(ty.ty.value().into()); +} + type_wrapper! { #[derive(PartialEq)] pub struct wasmtime_component_record_type_t { diff --git a/crates/c-api/src/component/val.rs b/crates/c-api/src/component/val.rs index 00c2c6a63963..60177f2d2dc6 100644 --- a/crates/c-api/src/component/val.rs +++ b/crates/c-api/src/component/val.rs @@ -45,6 +45,15 @@ crate::declare_vecs! { copy: wasmtime_component_valflags_copy, delete: wasmtime_component_valflags_delete, ) + ( + name: wasmtime_component_valmap_t, + ty: wasmtime_component_valmap_entry_t, + new: wasmtime_component_valmap_new, + empty: wasmtime_component_valmap_new_empty, + uninit: wasmtime_component_valmap_new_uninit, + copy: wasmtime_component_valmap_copy, + delete: wasmtime_component_valmap_delete, + ) } impl From<&wasmtime_component_vallist_t> for Vec { @@ -63,6 +72,29 @@ impl From<&[Val]> for wasmtime_component_vallist_t { } } +impl From<&wasmtime_component_valmap_t> for Vec<(Val, Val)> { + fn from(value: &wasmtime_component_valmap_t) -> Self { + value + .as_slice() + .iter() + .map(|entry| (Val::from(&entry.key), Val::from(&entry.value))) + .collect() + } +} + +impl From<&[(Val, Val)]> for wasmtime_component_valmap_t { + fn from(value: &[(Val, Val)]) -> Self { + value + .iter() + .map(|(k, v)| wasmtime_component_valmap_entry_t { + key: wasmtime_component_val_t::from(k), + value: wasmtime_component_val_t::from(v), + }) + .collect::>() + .into() + } +} + #[derive(Clone)] #[repr(C)] pub struct wasmtime_component_valrecord_entry_t { @@ -70,6 +102,13 @@ pub struct wasmtime_component_valrecord_entry_t { val: wasmtime_component_val_t, } +#[derive(Clone, Default)] +#[repr(C)] +pub struct wasmtime_component_valmap_entry_t { + key: wasmtime_component_val_t, + value: wasmtime_component_val_t, +} + impl Default for wasmtime_component_valrecord_entry_t { fn default() -> Self { Self { @@ -234,6 +273,7 @@ pub enum wasmtime_component_val_t { Result(wasmtime_component_valresult_t), Flags(wasmtime_component_valflags_t), Resource(Box), + Map(wasmtime_component_valmap_t), } impl Default for wasmtime_component_val_t { @@ -275,6 +315,7 @@ impl From<&wasmtime_component_val_t> for Val { } wasmtime_component_val_t::Result(x) => Val::Result(x.into()), wasmtime_component_val_t::Flags(x) => Val::Flags(x.into()), + wasmtime_component_val_t::Map(x) => Val::Map(x.into()), wasmtime_component_val_t::Resource(x) => Val::Resource(x.resource), } } @@ -309,6 +350,7 @@ impl From<&Val> for wasmtime_component_val_t { ), Val::Result(x) => wasmtime_component_val_t::Result(x.into()), Val::Flags(x) => wasmtime_component_val_t::Flags(x.as_slice().into()), + Val::Map(x) => wasmtime_component_val_t::Map(x.as_slice().into()), Val::Resource(resource_any) => { wasmtime_component_val_t::Resource(Box::new(wasmtime_component_resource_any_t { resource: *resource_any, diff --git a/crates/c-api/src/exn.rs b/crates/c-api/src/exn.rs new file mode 100644 index 000000000000..618644170fbb --- /dev/null +++ b/crates/c-api/src/exn.rs @@ -0,0 +1,107 @@ +use crate::{ + WasmtimeStoreContextMut, handle_result, wasm_trap_t, wasmtime_error_t, wasmtime_val_t, +}; +use std::mem::MaybeUninit; +use wasmtime::{AsContextMut, ExnRef, ExnRefPre, ExnType, RootScope, Tag}; + +/// An opaque type representing a WebAssembly exception object. +pub struct wasmtime_exn_t { + exn: wasmtime::OwnedRooted, +} + +wasmtime_c_api_macros::declare_own!(wasmtime_exn_t); + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_exn_new( + mut store: WasmtimeStoreContextMut<'_>, + tag: &Tag, + fields: *const wasmtime_val_t, + nfields: usize, + exn_ret: &mut *mut wasmtime_exn_t, +) -> Option> { + let mut scope = RootScope::new(&mut store); + + let result = (|| { + let tag_ty = tag.ty(&scope); + let exn_type = ExnType::from_tag_type(&tag_ty)?; + let allocator = ExnRefPre::new(&mut scope, exn_type); + let raw_fields = crate::slice_from_raw_parts(fields, nfields); + let field_vals: Vec = + raw_fields.iter().map(|f| f.to_val(&mut scope)).collect(); + ExnRef::new(&mut scope, &allocator, tag, &field_vals) + })(); + + handle_result(result, |rooted| { + let owned = rooted.to_owned_rooted(&mut scope).unwrap(); + *exn_ret = Box::into_raw(Box::new(wasmtime_exn_t { exn: owned })); + }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_exn_tag( + mut store: WasmtimeStoreContextMut<'_>, + exn: &wasmtime_exn_t, + tag_ret: &mut Tag, +) -> Option> { + let mut scope = RootScope::new(&mut store); + let rooted = exn.exn.to_rooted(&mut scope); + handle_result(rooted.tag(&mut scope), |tag| { + *tag_ret = tag; + }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_exn_field_count( + mut store: WasmtimeStoreContextMut<'_>, + exn: &wasmtime_exn_t, +) -> usize { + let mut scope = RootScope::new(&mut store); + let rooted = exn.exn.to_rooted(&mut scope); + let ty = rooted.ty(&scope).unwrap(); + ty.fields().len() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_exn_field( + mut store: WasmtimeStoreContextMut<'_>, + exn: &wasmtime_exn_t, + index: usize, + val_ret: &mut MaybeUninit, +) -> Option> { + let mut scope = RootScope::new(&mut store); + let rooted = exn.exn.to_rooted(&mut scope); + handle_result(rooted.field(&mut scope, index), |val| { + crate::initialize(val_ret, wasmtime_val_t::from_val(&mut scope, val)); + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_context_set_exception( + mut store: WasmtimeStoreContextMut<'_>, + exn: Box, +) -> Box { + let mut scope = RootScope::new(&mut store); + let rooted = exn.exn.to_rooted(&mut scope); + let thrown = scope.as_context_mut().throw::<()>(rooted).unwrap_err(); + Box::new(wasm_trap_t::new(wasmtime::Error::new(thrown))) +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_context_take_exception( + mut store: WasmtimeStoreContextMut<'_>, + exn_ret: &mut *mut wasmtime_exn_t, +) -> bool { + match store.take_pending_exception() { + Some(rooted) => { + let owned = rooted.to_owned_rooted(&mut store).unwrap(); + *exn_ret = Box::into_raw(Box::new(wasmtime_exn_t { exn: owned })); + true + } + None => false, + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_context_has_exception(store: WasmtimeStoreContextMut<'_>) -> bool { + store.has_pending_exception() +} diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs index b32eb801f1dd..ae54173d8fcd 100644 --- a/crates/c-api/src/extern.rs +++ b/crates/c-api/src/extern.rs @@ -3,7 +3,7 @@ use crate::{ wasm_global_t, wasm_memory_t, wasm_table_t, }; use std::mem::ManuallyDrop; -use wasmtime::{Extern, Func, Global, Memory, SharedMemory, Table}; +use wasmtime::{Extern, Func, Global, Memory, SharedMemory, Table, Tag}; #[derive(Clone)] pub struct wasm_extern_t { @@ -23,7 +23,7 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t { Extern::SharedMemory(_) => panic!( "Shared Memory no implemented for wasm_* types. Please use wasmtime_* types instead" ), - Extern::Tag(_) => todo!(), // FIXME: #10252 C embedder API for exceptions and control tags. + Extern::Tag(_) => crate::types::WASMTIME_EXTERNTYPE_TAG, } } @@ -86,6 +86,7 @@ pub const WASMTIME_EXTERN_GLOBAL: wasmtime_extern_kind_t = 1; pub const WASMTIME_EXTERN_TABLE: wasmtime_extern_kind_t = 2; pub const WASMTIME_EXTERN_MEMORY: wasmtime_extern_kind_t = 3; pub const WASMTIME_EXTERN_SHAREDMEMORY: wasmtime_extern_kind_t = 4; +pub const WASMTIME_EXTERN_TAG: wasmtime_extern_kind_t = 5; #[repr(C)] pub union wasmtime_extern_union { @@ -94,6 +95,7 @@ pub union wasmtime_extern_union { pub global: Global, pub memory: Memory, pub sharedmemory: ManuallyDrop>, + pub tag: Tag, } impl Drop for wasmtime_extern_t { @@ -114,6 +116,7 @@ impl wasmtime_extern_t { WASMTIME_EXTERN_TABLE => Extern::Table(self.of.table), WASMTIME_EXTERN_MEMORY => Extern::Memory(self.of.memory), WASMTIME_EXTERN_SHAREDMEMORY => Extern::SharedMemory((**self.of.sharedmemory).clone()), + WASMTIME_EXTERN_TAG => Extern::Tag(self.of.tag), other => panic!("unknown wasmtime_extern_kind_t: {other}"), } } @@ -144,7 +147,10 @@ impl From for wasmtime_extern_t { sharedmemory: ManuallyDrop::new(Box::new(sharedmemory)), }, }, - Extern::Tag(_) => todo!(), // FIXME: #10252 C embedder API for exceptions and control tags. + Extern::Tag(tag) => wasmtime_extern_t { + kind: WASMTIME_EXTERN_TAG, + of: wasmtime_extern_union { tag }, + }, } } } diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 4ff396e14613..6200f12146df 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -403,7 +403,7 @@ pub unsafe extern "C" fn wasmtime_func_call_unchecked( } fn store_err(err: Error, trap_ret: &mut *mut wasm_trap_t) -> Option> { - if err.is::() { + if err.is::() || err.is::() { *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err))); None } else { diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 5d5abad2a29d..4d9fdce1742d 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -20,6 +20,7 @@ pub use wasmtime; mod config; mod engine; mod error; +mod exn; mod r#extern; mod func; mod global; @@ -33,6 +34,7 @@ mod r#ref; mod sharedmemory; mod store; mod table; +mod tag; mod trap; mod types; mod val; @@ -41,6 +43,7 @@ mod vec; pub use crate::config::*; pub use crate::engine::*; pub use crate::error::*; +pub use crate::exn::*; pub use crate::r#extern::*; pub use crate::func::*; pub use crate::global::*; @@ -51,6 +54,7 @@ pub use crate::module::*; pub use crate::r#ref::*; pub use crate::store::*; pub use crate::table::*; +pub use crate::tag::*; pub use crate::trap::*; pub use crate::types::*; pub use crate::val::*; diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 93a0d38631d1..00d04cb6190f 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -204,11 +204,11 @@ pub unsafe extern "C" fn wasmtime_linker_get( Err(_) => return false, }; match linker.get(store, module, name) { - Some(which) => { + Ok(which) => { crate::initialize(item_ptr, which.into()); true } - None => false, + Err(_) => false, } } diff --git a/crates/c-api/src/ref.rs b/crates/c-api/src/ref.rs index b890f03c7463..9774a713c11c 100644 --- a/crates/c-api/src/ref.rs +++ b/crates/c-api/src/ref.rs @@ -1,7 +1,10 @@ use crate::{WasmtimeStoreContextMut, abort}; use std::mem::{ManuallyDrop, MaybeUninit}; use std::{num::NonZeroU64, os::raw::c_void, ptr}; -use wasmtime::{AnyRef, ExternRef, I31, OwnedRooted, Ref, RootScope, Val}; +use wasmtime::{ + AnyRef, ArrayRef, ArrayRefPre, ArrayType, EqRef, ExnRef, ExternRef, FieldType, I31, Mutability, + OwnedRooted, Ref, RootScope, StorageType, StructRef, StructRefPre, StructType, Val, ValType, +}; /// `*mut wasm_ref_t` is a reference type (`externref` or `funcref`), as seen by /// the C API. Because we do not have a uniform representation for `funcref`s @@ -247,6 +250,12 @@ macro_rules! ref_wrapper { } } + impl From> for $c { + fn from(rooted: OwnedRooted<$wasmtime>) -> $c { + Self::from(Some(rooted)) + } + } + // SAFETY: The `*const ()` comes from (and is converted back // into) an `Arc<()>`, and is only accessed as such, so this // type is both Send and Sync. These constraints are necessary @@ -258,6 +267,32 @@ macro_rules! ref_wrapper { ref_wrapper!(AnyRef => wasmtime_anyref_t); ref_wrapper!(ExternRef => wasmtime_externref_t); +ref_wrapper!(EqRef => wasmtime_eqref_t); +ref_wrapper!(StructRef => wasmtime_structref_t); +ref_wrapper!(ExnRef => wasmtime_exnref_t); + +// Opaque types for struct type and struct ref pre-allocator +pub struct wasmtime_struct_type_t { + ty: StructType, +} +wasmtime_c_api_macros::declare_own!(wasmtime_struct_type_t); + +pub struct wasmtime_struct_ref_pre_t { + pre: StructRefPre, +} +wasmtime_c_api_macros::declare_own!(wasmtime_struct_ref_pre_t); + +ref_wrapper!(ArrayRef => wasmtime_arrayref_t); + +pub struct wasmtime_array_type_t { + ty: ArrayType, +} +wasmtime_c_api_macros::declare_own!(wasmtime_array_type_t); + +pub struct wasmtime_array_ref_pre_t { + pre: ArrayRefPre, +} +wasmtime_c_api_macros::declare_own!(wasmtime_array_ref_pre_t); #[unsafe(no_mangle)] pub unsafe extern "C" fn wasmtime_anyref_clone( @@ -311,6 +346,17 @@ pub extern "C" fn wasmtime_anyref_from_i31( crate::initialize(out, Some(anyref).into()) } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_anyref_is_i31( + cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, +) -> bool { + match anyref.and_then(|a| a.as_wasmtime()) { + Some(anyref) => anyref.is_i31(&cx).expect("OwnedRooted always in scope"), + None => false, + } +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn wasmtime_anyref_i31_get_u( cx: WasmtimeStoreContextMut<'_>, @@ -421,3 +467,563 @@ pub unsafe extern "C" fn wasmtime_externref_from_raw( .map(|e| e.to_owned_rooted(&mut scope).expect("in scope")); crate::initialize(val, rooted.into()); } + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_exnref_clone( + exnref: Option<&wasmtime_exnref_t>, + out: &mut MaybeUninit, +) { + let exnref = exnref.and_then(|e| e.as_wasmtime()); + crate::initialize(out, exnref.into()); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_exnref_unroot(val: Option<&mut ManuallyDrop>) { + if let Some(val) = val { + unsafe { + ManuallyDrop::drop(val); + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_clone( + eqref: Option<&wasmtime_eqref_t>, + out: &mut MaybeUninit, +) { + let eqref = eqref.and_then(|e| e.as_wasmtime()); + crate::initialize(out, eqref.into()); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_unroot(val: Option<&mut ManuallyDrop>) { + if let Some(val) = val { + unsafe { + ManuallyDrop::drop(val); + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_to_anyref( + eqref: Option<&wasmtime_eqref_t>, + out: &mut MaybeUninit, +) { + let anyref = eqref.and_then(|e| e.as_wasmtime()).map(|e| e.to_anyref()); + crate::initialize(out, anyref.into()); +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_eqref_from_i31( + cx: WasmtimeStoreContextMut<'_>, + val: u32, + out: &mut MaybeUninit, +) { + let mut scope = RootScope::new(cx); + let eqref = EqRef::from_i31(&mut scope, I31::wrapping_u32(val)); + let eqref = eqref.to_owned_rooted(&mut scope).expect("in scope"); + crate::initialize(out, Some(eqref).into()) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_is_i31( + cx: WasmtimeStoreContextMut<'_>, + eqref: Option<&wasmtime_eqref_t>, +) -> bool { + match eqref.and_then(|e| e.as_wasmtime()) { + Some(eqref) => eqref.is_i31(&cx).expect("OwnedRooted always in scope"), + None => false, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_i31_get_u( + cx: WasmtimeStoreContextMut<'_>, + eqref: Option<&wasmtime_eqref_t>, + dst: &mut MaybeUninit, +) -> bool { + let mut scope = RootScope::new(cx); + if let Some(eqref) = eqref.and_then(|e| e.as_wasmtime()) { + if let Some(val) = eqref.as_i31(&mut scope).expect("in scope") { + crate::initialize(dst, val.get_u32()); + return true; + } + } + false +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_i31_get_s( + cx: WasmtimeStoreContextMut<'_>, + eqref: Option<&wasmtime_eqref_t>, + dst: &mut MaybeUninit, +) -> bool { + let mut scope = RootScope::new(cx); + if let Some(eqref) = eqref.and_then(|e| e.as_wasmtime()) { + if let Some(val) = eqref.as_i31(&mut scope).expect("in scope") { + crate::initialize(dst, val.get_i32()); + return true; + } + } + false +} + +pub type wasmtime_storage_kind_t = u8; +pub const WASMTIME_STORAGE_KIND_I8: wasmtime_storage_kind_t = 9; +pub const WASMTIME_STORAGE_KIND_I16: wasmtime_storage_kind_t = 10; + +#[repr(C)] +pub struct wasmtime_field_type_t { + pub kind: wasmtime_storage_kind_t, + pub mutable_: bool, +} + +fn field_type_from_c(ft: &wasmtime_field_type_t) -> FieldType { + let mutability = if ft.mutable_ { + Mutability::Var + } else { + Mutability::Const + }; + let storage = match ft.kind { + WASMTIME_STORAGE_KIND_I8 => StorageType::I8, + WASMTIME_STORAGE_KIND_I16 => StorageType::I16, + crate::WASMTIME_I32 => StorageType::ValType(ValType::I32), + crate::WASMTIME_I64 => StorageType::ValType(ValType::I64), + crate::WASMTIME_F32 => StorageType::ValType(ValType::F32), + crate::WASMTIME_F64 => StorageType::ValType(ValType::F64), + crate::WASMTIME_V128 => StorageType::ValType(ValType::V128), + crate::WASMTIME_FUNCREF => StorageType::ValType(ValType::FUNCREF), + crate::WASMTIME_EXTERNREF => StorageType::ValType(ValType::EXTERNREF), + crate::WASMTIME_ANYREF => StorageType::ValType(ValType::ANYREF), + crate::WASMTIME_EXNREF => StorageType::ValType(ValType::EXNREF), + other => panic!("unknown wasmtime_storage_kind_t: {other}"), + }; + FieldType::new(mutability, storage) +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_struct_type_new( + engine: &crate::wasm_engine_t, + fields: *const wasmtime_field_type_t, + nfields: usize, +) -> Box { + let fields = if nfields == 0 { + &[] + } else { + unsafe { std::slice::from_raw_parts(fields, nfields) } + }; + let field_types: Vec = fields.iter().map(field_type_from_c).collect(); + let ty = StructType::new(&engine.engine, field_types).expect("failed to create struct type"); + Box::new(wasmtime_struct_type_t { ty }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_struct_ref_pre_new( + cx: WasmtimeStoreContextMut<'_>, + ty: &wasmtime_struct_type_t, +) -> Box { + let pre = StructRefPre::new(cx, ty.ty.clone()); + Box::new(wasmtime_struct_ref_pre_t { pre }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_structref_new( + mut cx: WasmtimeStoreContextMut<'_>, + pre: &wasmtime_struct_ref_pre_t, + fields: *const crate::wasmtime_val_t, + nfields: usize, + out: &mut MaybeUninit, +) -> Option> { + let c_fields = if nfields == 0 { + &[] + } else { + std::slice::from_raw_parts(fields, nfields) + }; + let mut scope = RootScope::new(&mut cx); + let vals: Vec = c_fields.iter().map(|v| v.to_val(&mut scope)).collect(); + match StructRef::new(&mut scope, &pre.pre, &vals) { + Ok(structref) => { + let owned = structref + .to_owned_rooted(&mut scope) + .expect("just allocated"); + crate::initialize(out, Some(owned).into()); + None + } + Err(e) => { + crate::initialize(out, None::>.into()); + Some(Box::new(e.into())) + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_structref_clone( + structref: Option<&wasmtime_structref_t>, + out: &mut MaybeUninit, +) { + let structref = structref.and_then(|s| s.as_wasmtime()); + crate::initialize(out, structref.into()); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_structref_unroot( + structref: Option<&mut ManuallyDrop>, +) { + if let Some(structref) = structref { + ManuallyDrop::drop(structref); + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_structref_to_anyref( + structref: Option<&wasmtime_structref_t>, + out: &mut MaybeUninit, +) { + let anyref = structref + .and_then(|s| s.as_wasmtime()) + .map(|s| s.to_anyref()); + crate::initialize(out, anyref.into()); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_structref_to_eqref( + structref: Option<&wasmtime_structref_t>, + out: &mut MaybeUninit, +) { + let eqref = structref + .and_then(|s| s.as_wasmtime()) + .map(|s| s.to_eqref()); + crate::initialize(out, eqref.into()); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_structref_field( + mut cx: WasmtimeStoreContextMut<'_>, + structref: Option<&wasmtime_structref_t>, + index: usize, + out: &mut MaybeUninit, +) -> Option> { + let structref = structref + .and_then(|s| s.as_wasmtime()) + .expect("non-null structref required"); + let mut scope = RootScope::new(&mut cx); + let rooted = structref.to_rooted(&mut scope); + match rooted.field(&mut scope, index) { + Ok(val) => { + let c_val = crate::wasmtime_val_t::from_val(&mut scope, val); + crate::initialize(out, c_val); + None + } + Err(e) => Some(Box::new(e.into())), + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_structref_set_field( + mut cx: WasmtimeStoreContextMut<'_>, + structref: Option<&wasmtime_structref_t>, + index: usize, + val: &crate::wasmtime_val_t, +) -> Option> { + let structref = structref + .and_then(|s| s.as_wasmtime()) + .expect("non-null structref required"); + let mut scope = RootScope::new(&mut cx); + let rooted = structref.to_rooted(&mut scope); + let rust_val = val.to_val(&mut scope); + match rooted.set_field(&mut scope, index, rust_val) { + Ok(()) => None, + Err(e) => Some(Box::new(e.into())), + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_is_struct( + cx: WasmtimeStoreContextMut<'_>, + eqref: Option<&wasmtime_eqref_t>, +) -> bool { + match eqref.and_then(|e| e.as_wasmtime()) { + Some(eqref) => eqref.is_struct(&cx).expect("OwnedRooted always in scope"), + None => false, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_as_struct( + mut cx: WasmtimeStoreContextMut<'_>, + eqref: Option<&wasmtime_eqref_t>, + out: &mut MaybeUninit, +) -> bool { + if let Some(eqref) = eqref.and_then(|e| e.as_wasmtime()) { + let mut scope = RootScope::new(&mut cx); + let rooted = eqref.to_rooted(&mut scope); + if let Ok(Some(structref)) = rooted.as_struct(&scope) { + let owned = structref.to_owned_rooted(&mut scope).expect("in scope"); + crate::initialize(out, Some(owned).into()); + return true; + } + } + crate::initialize(out, None::>.into()); + false +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_array_type_new( + engine: &crate::wasm_engine_t, + field: &wasmtime_field_type_t, +) -> Box { + let ft = field_type_from_c(field); + let ty = ArrayType::new(&engine.engine, ft); + Box::new(wasmtime_array_type_t { ty }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_array_ref_pre_new( + cx: WasmtimeStoreContextMut<'_>, + ty: &wasmtime_array_type_t, +) -> Box { + let pre = ArrayRefPre::new(cx, ty.ty.clone()); + Box::new(wasmtime_array_ref_pre_t { pre }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_arrayref_new( + mut cx: WasmtimeStoreContextMut<'_>, + pre: &wasmtime_array_ref_pre_t, + elem: &crate::wasmtime_val_t, + len: u32, + out: &mut MaybeUninit, +) -> Option> { + let mut scope = RootScope::new(&mut cx); + let val = elem.to_val(&mut scope); + match ArrayRef::new(&mut scope, &pre.pre, &val, len) { + Ok(arrayref) => { + let owned = arrayref + .to_owned_rooted(&mut scope) + .expect("just allocated"); + crate::initialize(out, Some(owned).into()); + None + } + Err(e) => { + crate::initialize(out, None::>.into()); + Some(Box::new(e.into())) + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_arrayref_clone( + arrayref: Option<&wasmtime_arrayref_t>, + out: &mut MaybeUninit, +) { + let arrayref = arrayref.and_then(|a| a.as_wasmtime()); + crate::initialize(out, arrayref.into()); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_arrayref_unroot( + arrayref: Option<&mut ManuallyDrop>, +) { + if let Some(arrayref) = arrayref { + ManuallyDrop::drop(arrayref); + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_arrayref_to_anyref( + arrayref: Option<&wasmtime_arrayref_t>, + out: &mut MaybeUninit, +) { + let anyref = arrayref + .and_then(|a| a.as_wasmtime()) + .map(|a| a.to_anyref()); + crate::initialize(out, anyref.into()); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_arrayref_to_eqref( + arrayref: Option<&wasmtime_arrayref_t>, + out: &mut MaybeUninit, +) { + let eqref = arrayref.and_then(|a| a.as_wasmtime()).map(|a| a.to_eqref()); + crate::initialize(out, eqref.into()); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_arrayref_len( + cx: WasmtimeStoreContextMut<'_>, + arrayref: Option<&wasmtime_arrayref_t>, + out: &mut MaybeUninit, +) -> Option> { + let arrayref = arrayref + .and_then(|a| a.as_wasmtime()) + .expect("non-null arrayref required"); + match arrayref.len(&cx) { + Ok(len) => { + crate::initialize(out, len); + None + } + Err(e) => Some(Box::new(e.into())), + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_arrayref_get( + mut cx: WasmtimeStoreContextMut<'_>, + arrayref: Option<&wasmtime_arrayref_t>, + index: u32, + out: &mut MaybeUninit, +) -> Option> { + let arrayref = arrayref + .and_then(|a| a.as_wasmtime()) + .expect("non-null arrayref required"); + let mut scope = RootScope::new(&mut cx); + let rooted = arrayref.to_rooted(&mut scope); + match rooted.get(&mut scope, index) { + Ok(val) => { + let c_val = crate::wasmtime_val_t::from_val(&mut scope, val); + crate::initialize(out, c_val); + None + } + Err(e) => Some(Box::new(e.into())), + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_arrayref_set( + mut cx: WasmtimeStoreContextMut<'_>, + arrayref: Option<&wasmtime_arrayref_t>, + index: u32, + val: &crate::wasmtime_val_t, +) -> Option> { + let arrayref = arrayref + .and_then(|a| a.as_wasmtime()) + .expect("non-null arrayref required"); + let mut scope = RootScope::new(&mut cx); + let rooted = arrayref.to_rooted(&mut scope); + let rust_val = val.to_val(&mut scope); + match rooted.set(&mut scope, index, rust_val) { + Ok(()) => None, + Err(e) => Some(Box::new(e.into())), + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_is_array( + cx: WasmtimeStoreContextMut<'_>, + eqref: Option<&wasmtime_eqref_t>, +) -> bool { + match eqref.and_then(|e| e.as_wasmtime()) { + Some(eqref) => eqref.is_array(&cx).expect("OwnedRooted always in scope"), + None => false, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_eqref_as_array( + mut cx: WasmtimeStoreContextMut<'_>, + eqref: Option<&wasmtime_eqref_t>, + out: &mut MaybeUninit, +) -> bool { + if let Some(eqref) = eqref.and_then(|e| e.as_wasmtime()) { + let mut scope = RootScope::new(&mut cx); + let rooted = eqref.to_rooted(&mut scope); + if let Ok(Some(arrayref)) = rooted.as_array(&scope) { + let owned = arrayref.to_owned_rooted(&mut scope).expect("just created"); + crate::initialize(out, Some(owned).into()); + return true; + } + } + crate::initialize(out, None::>.into()); + false +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_anyref_is_eqref( + cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, +) -> bool { + match anyref.and_then(|a| a.as_wasmtime()) { + Some(anyref) => anyref.is_eqref(&cx).expect("OwnedRooted always in scope"), + None => false, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_anyref_as_eqref( + mut cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, + out: &mut MaybeUninit, +) -> bool { + if let Some(anyref) = anyref.and_then(|a| a.as_wasmtime()) { + let mut scope = RootScope::new(&mut cx); + let rooted = anyref.to_rooted(&mut scope); + if let Ok(Some(eqref)) = rooted.as_eqref(&mut scope) { + let owned = eqref.to_owned_rooted(&mut scope).expect("in scope"); + crate::initialize(out, Some(owned).into()); + return true; + } + } + crate::initialize(out, None::>.into()); + false +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_anyref_is_struct( + cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, +) -> bool { + match anyref.and_then(|a| a.as_wasmtime()) { + Some(anyref) => anyref.is_struct(&cx).expect("OwnedRooted always in scope"), + None => false, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_anyref_as_struct( + mut cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, + out: &mut MaybeUninit, +) -> bool { + if let Some(anyref) = anyref.and_then(|a| a.as_wasmtime()) { + let mut scope = RootScope::new(&mut cx); + let rooted = anyref.to_rooted(&mut scope); + if let Ok(Some(structref)) = rooted.as_struct(&scope) { + let owned = structref.to_owned_rooted(&mut scope).expect("in scope"); + crate::initialize(out, Some(owned).into()); + return true; + } + } + crate::initialize(out, None::>.into()); + false +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_anyref_is_array( + cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, +) -> bool { + match anyref.and_then(|a| a.as_wasmtime()) { + Some(anyref) => anyref.is_array(&cx).expect("OwnedRooted always in scope"), + None => false, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_anyref_as_array( + mut cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, + out: &mut MaybeUninit, +) -> bool { + if let Some(anyref) = anyref.and_then(|a| a.as_wasmtime()) { + let mut scope = RootScope::new(&mut cx); + let rooted = anyref.to_rooted(&mut scope); + if let Ok(Some(arrayref)) = rooted.as_array(&scope) { + let owned = arrayref.to_owned_rooted(&mut scope).expect("in scope"); + crate::initialize(out, Some(owned).into()); + return true; + } + } + crate::initialize(out, None::>.into()); + false +} diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs index 6e5d2d8e74ef..6ea7fdce7204 100644 --- a/crates/c-api/src/store.rs +++ b/crates/c-api/src/store.rs @@ -84,6 +84,8 @@ pub struct WasmtimeStoreData { foreign: crate::ForeignData, #[cfg(feature = "wasi")] pub(crate) wasi: Option, + #[cfg(feature = "wasi-http")] + pub(crate) wasi_http: Option, /// Temporary storage for usage during a wasm->host call to store values /// in a slice we pass to the C API. @@ -104,6 +106,20 @@ impl wasmtime_wasi::WasiView for WasmtimeStoreData { } } +#[cfg(all(feature = "component-model", feature = "wasi-http"))] +impl wasmtime_wasi_http::p2::WasiHttpView for WasmtimeStoreData { + fn http(&mut self) -> wasmtime_wasi_http::p2::WasiHttpCtxView<'_> { + use wasmtime_wasi::WasiView; + let ctx = self.wasi_http.as_mut().unwrap(); + let table = self.wasi.as_mut().unwrap().ctx().table; + wasmtime_wasi_http::p2::WasiHttpCtxView { + ctx, + table, + hooks: wasmtime_wasi_http::p2::default_hooks(), + } + } +} + #[unsafe(no_mangle)] pub extern "C" fn wasmtime_store_new( engine: &wasm_engine_t, @@ -117,6 +133,8 @@ pub extern "C" fn wasmtime_store_new( foreign: ForeignData { data, finalizer }, #[cfg(feature = "wasi")] wasi: None, + #[cfg(feature = "wasi-http")] + wasi_http: None, hostcall_val_storage: Vec::new(), wasm_val_storage: Vec::new(), store_limits: StoreLimits::default(), @@ -224,6 +242,12 @@ pub extern "C" fn wasmtime_context_set_wasi( }) } +#[cfg(feature = "wasi-http")] +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_context_set_wasi_http(mut context: WasmtimeStoreContextMut<'_>) { + context.data_mut().wasi_http = Some(wasmtime_wasi_http::WasiHttpCtx::new()); +} + #[unsafe(no_mangle)] pub extern "C" fn wasmtime_context_gc( mut context: WasmtimeStoreContextMut<'_>, diff --git a/crates/c-api/src/tag.rs b/crates/c-api/src/tag.rs new file mode 100644 index 000000000000..bf23b8a648a7 --- /dev/null +++ b/crates/c-api/src/tag.rs @@ -0,0 +1,34 @@ +use crate::{ + WasmtimeStoreContext, WasmtimeStoreContextMut, handle_result, wasm_tagtype_t, wasmtime_error_t, +}; +use std::mem::MaybeUninit; +use wasmtime::Tag; + +/// Creates a new host-defined tag with the given type. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasmtime_tag_new( + mut store: WasmtimeStoreContextMut<'_>, + tt: &wasm_tagtype_t, + ret: &mut MaybeUninit, +) -> Option> { + let tag_type = tt.to_tag_type(store.engine()); + handle_result(Tag::new(&mut store, &tag_type), |tag| { + ret.write(tag); + }) +} + +/// Returns the type of this tag. +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_tag_type( + store: WasmtimeStoreContext<'_>, + tag: &Tag, +) -> Box { + let ty = tag.ty(store); + Box::new(wasm_tagtype_t::from_tag_type(ty)) +} + +/// Tests whether two tags are the same (identity equality). +#[unsafe(no_mangle)] +pub extern "C" fn wasmtime_tag_eq(store: WasmtimeStoreContext<'_>, a: &Tag, b: &Tag) -> bool { + Tag::eq(a, b, store) +} diff --git a/crates/c-api/src/trap.rs b/crates/c-api/src/trap.rs index 7f22b4f86b9c..9af88d36713d 100644 --- a/crates/c-api/src/trap.rs +++ b/crates/c-api/src/trap.rs @@ -51,6 +51,7 @@ const _: () = { assert!(Trap::BackpressureOverflow as u8 == 42); assert!(Trap::UnsupportedCallbackCode as u8 == 43); assert!(Trap::CannotResumeThread as u8 == 44); + assert!(Trap::ConcurrentFutureStreamOp as u8 == 45); }; #[repr(C)] diff --git a/crates/c-api/src/types.rs b/crates/c-api/src/types.rs index e1aa3a704a34..99e08400fe4c 100644 --- a/crates/c-api/src/types.rs +++ b/crates/c-api/src/types.rs @@ -22,6 +22,7 @@ mod global; mod import; mod memory; mod table; +mod tag; mod val; pub use self::export::*; pub use self::r#extern::*; @@ -30,4 +31,5 @@ pub use self::global::*; pub use self::import::*; pub use self::memory::*; pub use self::table::*; +pub use self::tag::*; pub use self::val::*; diff --git a/crates/c-api/src/types/extern.rs b/crates/c-api/src/types/extern.rs index 075b1b71051f..ea755bd835cd 100644 --- a/crates/c-api/src/types/extern.rs +++ b/crates/c-api/src/types/extern.rs @@ -1,4 +1,4 @@ -use crate::{CFuncType, CGlobalType, CMemoryType, CTableType}; +use crate::{CFuncType, CGlobalType, CMemoryType, CTableType, CTagType}; use crate::{wasm_functype_t, wasm_globaltype_t, wasm_memorytype_t, wasm_tabletype_t}; use wasmtime::ExternType; @@ -16,6 +16,7 @@ pub(crate) enum CExternType { Global(CGlobalType), Memory(CMemoryType), Table(CTableType), + Tag(CTagType), } impl CExternType { @@ -25,7 +26,7 @@ impl CExternType { ExternType::Global(f) => CExternType::Global(CGlobalType::new(f)), ExternType::Memory(f) => CExternType::Memory(CMemoryType::new(f)), ExternType::Table(f) => CExternType::Table(CTableType::new(f)), - ExternType::Tag(_) => todo!(), // FIXME: #10252 C embedder API for exceptions and control tags. + ExternType::Tag(t) => CExternType::Tag(CTagType::new(t)), } } } @@ -36,6 +37,9 @@ pub const WASM_EXTERN_FUNC: wasm_externkind_t = 0; pub const WASM_EXTERN_GLOBAL: wasm_externkind_t = 1; pub const WASM_EXTERN_TABLE: wasm_externkind_t = 2; pub const WASM_EXTERN_MEMORY: wasm_externkind_t = 3; +/// Value returned by `wasm_externtype_kind` for exception tags. +/// This extends the `wasm_externkind_t` range (0-3 in wasm.h) with tag support. +pub const WASMTIME_EXTERNTYPE_TAG: wasm_externkind_t = 4; impl wasm_externtype_t { pub(crate) fn from_extern_type(ty: ExternType) -> wasm_externtype_t { @@ -56,6 +60,7 @@ pub extern "C" fn wasm_externtype_kind(et: &wasm_externtype_t) -> wasm_externkin CExternType::Table(_) => WASM_EXTERN_TABLE, CExternType::Global(_) => WASM_EXTERN_GLOBAL, CExternType::Memory(_) => WASM_EXTERN_MEMORY, + CExternType::Tag(_) => WASMTIME_EXTERNTYPE_TAG, } } diff --git a/crates/c-api/src/types/func.rs b/crates/c-api/src/types/func.rs index 220801a3f1c1..af506ee1fb40 100644 --- a/crates/c-api/src/types/func.rs +++ b/crates/c-api/src/types/func.rs @@ -102,6 +102,12 @@ impl wasm_functype_t { } } + pub(crate) fn from_cfunc(ty: CFuncType) -> wasm_functype_t { + wasm_functype_t { + ext: wasm_externtype_t::from_cextern_type(CExternType::Func(ty)), + } + } + pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasm_functype_t> { match &e.which { CExternType::Func(_) => Some(unsafe { &*(e as *const _ as *const _) }), diff --git a/crates/c-api/src/types/tag.rs b/crates/c-api/src/types/tag.rs new file mode 100644 index 000000000000..8f22f4bf03d3 --- /dev/null +++ b/crates/c-api/src/types/tag.rs @@ -0,0 +1,106 @@ +use crate::{CExternType, CFuncType, wasm_externtype_t, wasm_functype_t}; +use std::cell::OnceCell; +use wasmtime::{Engine, TagType}; + +#[repr(transparent)] +#[derive(Clone)] +pub struct wasm_tagtype_t { + ext: wasm_externtype_t, +} + +wasmtime_c_api_macros::declare_ty!(wasm_tagtype_t); + +#[derive(Clone)] +pub(crate) struct CTagType { + ty: CFuncType, + functype_cache: OnceCell>, +} + +impl CTagType { + pub(crate) fn new(ty: TagType) -> CTagType { + CTagType { + ty: CFuncType::new(ty.ty().clone()), + functype_cache: OnceCell::new(), + } + } + + fn from_cfunc(ty: CFuncType) -> CTagType { + CTagType { + ty, + functype_cache: OnceCell::new(), + } + } +} + +impl wasm_tagtype_t { + pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasm_tagtype_t> { + match &e.which { + CExternType::Tag(_) => Some(unsafe { &*(e as *const _ as *const _) }), + _ => None, + } + } + + fn cty(&self) -> &CTagType { + match &self.ext.which { + CExternType::Tag(t) => t, + _ => unsafe { std::hint::unreachable_unchecked() }, + } + } + + /// Converts this C tag type into a Wasmtime `TagType`. + pub(crate) fn to_tag_type(&self, engine: &Engine) -> TagType { + let func_type = self.cty().ty.ty(engine); + TagType::new(func_type) + } + + /// Creates a `wasm_tagtype_t` from a Wasmtime `TagType`. + pub(crate) fn from_tag_type(ty: TagType) -> wasm_tagtype_t { + wasm_tagtype_t { + ext: wasm_externtype_t::from_cextern_type(CExternType::Tag(CTagType::new(ty))), + } + } +} + +/// Creates a new tag type from a function type describing the exception payload. +/// Takes ownership of `functype`. +#[unsafe(no_mangle)] +pub extern "C" fn wasm_tagtype_new(functype: Box) -> Box { + let cfunc = functype.ty().clone(); + Box::new(wasm_tagtype_t { + ext: wasm_externtype_t::from_cextern_type(CExternType::Tag(CTagType::from_cfunc(cfunc))), + }) +} + +/// Returns a borrowed reference to the function type describing this tag's exception payload. +#[unsafe(no_mangle)] +pub extern "C" fn wasm_tagtype_functype(tt: &wasm_tagtype_t) -> &wasm_functype_t { + let cty = tt.cty(); + cty.functype_cache + .get_or_init(|| Box::new(wasm_functype_t::from_cfunc(cty.ty.clone()))) +} + +/// Converts a `wasm_tagtype_t` to a `wasm_externtype_t` (borrowed). +#[unsafe(no_mangle)] +pub extern "C" fn wasm_tagtype_as_externtype(ty: &wasm_tagtype_t) -> &wasm_externtype_t { + &ty.ext +} + +/// Converts a const `wasm_tagtype_t` to a const `wasm_externtype_t` (borrowed). +#[unsafe(no_mangle)] +pub extern "C" fn wasm_tagtype_as_externtype_const(ty: &wasm_tagtype_t) -> &wasm_externtype_t { + &ty.ext +} + +/// Converts a `wasm_externtype_t` to a `wasm_tagtype_t`, or NULL if not a tag. +#[unsafe(no_mangle)] +pub extern "C" fn wasm_externtype_as_tagtype(et: &wasm_externtype_t) -> Option<&wasm_tagtype_t> { + wasm_externtype_as_tagtype_const(et) +} + +/// Converts a const `wasm_externtype_t` to a const `wasm_tagtype_t`, or NULL if not a tag. +#[unsafe(no_mangle)] +pub extern "C" fn wasm_externtype_as_tagtype_const( + et: &wasm_externtype_t, +) -> Option<&wasm_tagtype_t> { + wasm_tagtype_t::try_from(et) +} diff --git a/crates/c-api/src/types/val.rs b/crates/c-api/src/types/val.rs index 57f10d35b0ee..baf134e6b4d4 100644 --- a/crates/c-api/src/types/val.rs +++ b/crates/c-api/src/types/val.rs @@ -65,3 +65,4 @@ pub const WASMTIME_V128: wasmtime_valkind_t = 4; pub const WASMTIME_FUNCREF: wasmtime_valkind_t = 5; pub const WASMTIME_EXTERNREF: wasmtime_valkind_t = 6; pub const WASMTIME_ANYREF: wasmtime_valkind_t = 7; +pub const WASMTIME_EXNREF: wasmtime_valkind_t = 8; diff --git a/crates/c-api/src/val.rs b/crates/c-api/src/val.rs index 8da29cc8d7df..8d8ff86dfcb9 100644 --- a/crates/c-api/src/val.rs +++ b/crates/c-api/src/val.rs @@ -1,7 +1,7 @@ use crate::r#ref::ref_to_val; use crate::{ WASM_I32, from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, wasmtime_anyref_t, - wasmtime_externref_t, wasmtime_valkind_t, + wasmtime_exnref_t, wasmtime_externref_t, wasmtime_valkind_t, }; use std::mem::{ManuallyDrop, MaybeUninit}; use std::ptr; @@ -148,6 +148,7 @@ pub union wasmtime_val_union { pub f64: u64, pub anyref: ManuallyDrop, pub externref: ManuallyDrop, + pub exnref: ManuallyDrop, pub funcref: wasmtime_func_t, pub v128: [u8; 16], } @@ -168,6 +169,9 @@ impl Drop for wasmtime_val_t { crate::WASMTIME_EXTERNREF => { let _ = ManuallyDrop::take(&mut self.of.externref); } + crate::WASMTIME_EXNREF => { + let _ = ManuallyDrop::take(&mut self.of.exnref); + } _ => {} } } @@ -179,12 +183,14 @@ unsafe impl Send for wasmtime_val_union where Option>: Send, Option>: Send, + Option>: Send, { } unsafe impl Sync for wasmtime_val_union where Option>: Sync, Option>: Sync, + Option>: Sync, { } @@ -268,7 +274,12 @@ impl wasmtime_val_t { funcref: func.into(), }, }, - Val::ExnRef(_) => crate::abort("exnrefs not yet supported in C API"), + Val::ExnRef(e) => wasmtime_val_t { + kind: crate::WASMTIME_EXNREF, + of: wasmtime_val_union { + exnref: ManuallyDrop::new(e.and_then(|e| e.to_owned_rooted(cx).ok()).into()), + }, + }, Val::V128(val) => wasmtime_val_t { kind: crate::WASMTIME_V128, of: wasmtime_val_union { @@ -308,6 +319,9 @@ impl wasmtime_val_t { Val::ExternRef(self.of.externref.as_wasmtime().map(|e| e.to_rooted(cx))) } crate::WASMTIME_FUNCREF => Val::FuncRef(self.of.funcref.as_wasmtime()), + crate::WASMTIME_EXNREF => { + Val::ExnRef(self.of.exnref.as_wasmtime().map(|e| e.to_rooted(cx))) + } other => panic!("unknown wasmtime_valkind_t: {other}"), } } @@ -330,6 +344,9 @@ pub unsafe extern "C" fn wasmtime_val_clone( crate::WASMTIME_EXTERNREF => wasmtime_val_union { externref: ManuallyDrop::new(src.of.externref.as_wasmtime().into()), }, + crate::WASMTIME_EXNREF => wasmtime_val_union { + exnref: ManuallyDrop::new(src.of.exnref.as_wasmtime().into()), + }, crate::WASMTIME_I32 => wasmtime_val_union { i32: src.of.i32 }, crate::WASMTIME_I64 => wasmtime_val_union { i64: src.of.i64 }, crate::WASMTIME_F32 => wasmtime_val_union { f32: src.of.f32 }, diff --git a/crates/c-api/tests/CMakeLists.txt b/crates/c-api/tests/CMakeLists.txt index b77c1e68bfcc..6e8a6b0b70e7 100644 --- a/crates/c-api/tests/CMakeLists.txt +++ b/crates/c-api/tests/CMakeLists.txt @@ -30,6 +30,8 @@ add_capi_test(tests FILES import_type.cc export_type.cc extern_type.cc + tag_type.cc + exception.cc func.cc component/instantiate.cc component/define_module.cc @@ -47,6 +49,7 @@ add_capi_test(tests FILES wasi.cc store.cc val.cc + gc.cc table.cc global.cc memory.cc diff --git a/crates/c-api/tests/component/types.cc b/crates/c-api/tests/component/types.cc index dcef68dfd8b0..5953366f48e0 100644 --- a/crates/c-api/tests/component/types.cc +++ b/crates/c-api/tests/component/types.cc @@ -2,6 +2,7 @@ #include using namespace wasmtime::component; +using wasmtime::Config; using wasmtime::Engine; using wasmtime::ExternType; using wasmtime::Result; @@ -187,6 +188,22 @@ TEST(types, valtype_list) { EXPECT_TRUE(elem.is_u8()); } +TEST(types, valtype_map) { + Config config; + config.wasm_component_model_map(true); + Engine engine(std::move(config)); + auto component = + Component::compile( + engine, "(component (import \"f\" (func (result (map u32 string)))))") + .unwrap(); + auto ty = + *component.type().import_get(engine, "f")->component_func().result(); + EXPECT_TRUE(ty.is_map()); + auto map_ty = ty.map(); + EXPECT_TRUE(map_ty.key().is_u32()); + EXPECT_TRUE(map_ty.value().is_string()); +} + TEST(types, valtype_record) { auto ty = result(R"( (component diff --git a/crates/c-api/tests/exception.cc b/crates/c-api/tests/exception.cc new file mode 100644 index 000000000000..63962b7231af --- /dev/null +++ b/crates/c-api/tests/exception.cc @@ -0,0 +1,217 @@ +#include +#include + +using namespace wasmtime; + +static Engine make_engine() { + Config config; + config.wasm_exceptions(true); + return Engine(std::move(config)); +} + +TEST(Exception, ConstructAndExamine) { + auto engine = make_engine(); + Store store(engine); + auto cx = store.context(); + + // Create a tag type with (i32, i64) payload. + FuncType ft({ValKind::I32, ValKind::I64}, {}); + TagType tt(ft); + + // Create a tag instance. + auto tag = Tag::create(cx, tt).unwrap(); + + // Create an exception with payload (42, 100). + std::vector fields = {Val(int32_t(42)), Val(int64_t(100))}; + auto exn = Exn::create(cx, tag, fields).unwrap(); + + // Read back the tag and verify identity. + auto exn_tag = exn.tag(cx).unwrap(); + EXPECT_TRUE(tag.eq(cx, exn_tag)); + + // Read field count. + EXPECT_EQ(exn.field_count(cx), 2u); + + // Read fields. + auto f0 = exn.field(cx, 0).unwrap(); + EXPECT_EQ(f0.kind(), ValKind::I32); + EXPECT_EQ(f0.i32(), 42); + + auto f1 = exn.field(cx, 1).unwrap(); + EXPECT_EQ(f1.kind(), ValKind::I64); + EXPECT_EQ(f1.i64(), 100); +} + +TEST(Exception, TagFromModule) { + auto engine = make_engine(); + Store store(engine); + auto cx = store.context(); + + Module module = Module::compile(engine, "(module" + " (tag $t (param i32))" + " (export \"t\" (tag $t))" + ")") + .unwrap(); + + auto instance = Instance::create(cx, module, {}).unwrap(); + auto ext = instance.get(cx, "t"); + ASSERT_TRUE(ext.has_value()); + auto tag = std::get(*ext); + + auto tt = tag.type(cx); + auto func = tt->functype(); + EXPECT_EQ(func->params().size(), 1u); + EXPECT_EQ(func->params().begin()->kind(), ValKind::I32); +} + +TEST(Exception, HostThrowWasmCatch) { + auto engine = make_engine(); + Store store(engine); + auto cx = store.context(); + + FuncType tag_ft({ValKind::I32}, {}); + TagType tt(tag_ft); + auto tag = Tag::create(cx, tt).unwrap(); + + Module module = + Module::compile(engine, "(module" + " (import \"host\" \"throw\" (func $throw))" + " (import \"host\" \"tag\" (tag $t (param i32)))" + " (func (export \"run\") (result i32)" + " (block $done (result i32)" + " (try_table (catch 0 $done)" + " (call $throw)" + " )" + " (unreachable)" + " )" + " )" + ")") + .unwrap(); + + FuncType throw_ft({}, {}); + Func throw_fn(cx, throw_ft, + [&](Caller caller, Span args, + Span results) -> Result { + (void)args; + (void)results; + auto cx2 = caller.context(); + + std::vector fields = {Val(int32_t(99))}; + auto exn = Exn::create(cx2, tag, fields).unwrap(); + return cx2.throw_exception(std::move(exn)); + }); + + std::vector imports = {throw_fn, tag}; + auto instance = Instance::create(cx, module, imports).unwrap(); + + auto run_ext = instance.get(cx, "run"); + ASSERT_TRUE(run_ext.has_value()); + auto run_fn = std::get(*run_ext); + auto result = run_fn.call(cx, {}); + ASSERT_TRUE(result); + ASSERT_EQ(result.ok().size(), 1u); + EXPECT_EQ(result.ok()[0].i32(), 99); +} + +TEST(Exception, ExnRefRoundTripThroughVal) { + auto engine = make_engine(); + Store store(engine); + auto cx = store.context(); + + // A module that throws an exception and catches it as an exnref. + Module module = + Module::compile(engine, "(module" + " (tag $t (param i32))" + " (export \"tag\" (tag $t))" + " (func (export \"make_exnref\") (result exnref)" + " (block $done (result exnref)" + " (try_table (catch_all_ref $done)" + " (throw $t (i32.const 55))" + " )" + " (unreachable)" + " )" + " )" + ")") + .unwrap(); + + auto instance = Instance::create(cx, module, {}).unwrap(); + + auto tag_ext = instance.get(cx, "tag"); + ASSERT_TRUE(tag_ext.has_value()); + auto tag = std::get(*tag_ext); + + auto make_fn = std::get(*instance.get(cx, "make_exnref")); + auto result = make_fn.call(cx, {}); + ASSERT_TRUE(result); + ASSERT_EQ(result.ok().size(), 1u); + + // The returned value should be an exnref. + auto &exnref_val = result.ok()[0]; + EXPECT_EQ(exnref_val.kind(), ValKind::ExnRef); + + // Pass the exnref back into a wasm function that extracts the i32 payload. + Module module2 = + Module::compile(engine, + "(module" + " (import \"host\" \"tag\" (tag $t (param i32)))" + " (func (export \"read\") (param exnref) (result i32)" + " (block $done (result i32)" + " (try_table (catch 0 $done)" + " (throw_ref (local.get 0))" + " )" + " (unreachable)" + " )" + " )" + ")") + .unwrap(); + + std::vector imports2 = {tag}; + auto instance2 = Instance::create(cx, module2, imports2).unwrap(); + auto read_fn = std::get(*instance2.get(cx, "read")); + + // Call with the exnref we got back from the first module. + std::vector args = {std::move(exnref_val)}; + auto result2 = read_fn.call(cx, args); + ASSERT_TRUE(result2); + ASSERT_EQ(result2.ok().size(), 1u); + EXPECT_EQ(result2.ok()[0].i32(), 55); +} + +TEST(Exception, WasmThrowHostCatch) { + auto engine = make_engine(); + Store store(engine); + auto cx = store.context(); + + Module module = Module::compile(engine, "(module" + " (tag $t (param i32))" + " (export \"tag\" (tag $t))" + " (func (export \"throw\")" + " (throw $t (i32.const 77))" + " )" + ")") + .unwrap(); + + auto instance = Instance::create(cx, module, {}).unwrap(); + + auto tag_ext = instance.get(cx, "tag"); + ASSERT_TRUE(tag_ext.has_value()); + auto tag = std::get(*tag_ext); + + auto throw_ext = instance.get(cx, "throw"); + ASSERT_TRUE(throw_ext.has_value()); + auto throw_fn = std::get(*throw_ext); + + auto result = throw_fn.call(cx, {}); + ASSERT_FALSE(result); + + ASSERT_TRUE(cx.has_exception()); + auto exn = cx.take_exception(); + ASSERT_TRUE(exn.has_value()); + + auto exn_tag = exn->tag(cx).unwrap(); + EXPECT_TRUE(tag.eq(cx, exn_tag)); + + EXPECT_EQ(exn->field_count(cx), 1u); + auto f0 = exn->field(cx, 0).unwrap(); + EXPECT_EQ(f0.i32(), 77); +} diff --git a/crates/c-api/tests/gc.cc b/crates/c-api/tests/gc.cc new file mode 100644 index 000000000000..b7fbc4450159 --- /dev/null +++ b/crates/c-api/tests/gc.cc @@ -0,0 +1,313 @@ +#include + +#include +#include + +using namespace wasmtime; + +TEST(EqRef, CopyAndMove) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + + auto cx = store.context(); + + // Create an i31 eqref. + EqRef eq = EqRef::from_i31(cx, 42); + EXPECT_TRUE(eq.is_i31(cx)); + + // Copy constructor + EqRef eq2(eq); + + // Move constructor + EqRef eq3(std::move(eq2)); + + // Copy assignment + EqRef eq4 = eq3; + + // Move assignment + EqRef eq5 = std::move(eq4); + + // Upcast to anyref and check value + AnyRef upcast = eq.to_anyref(); + auto val = upcast.u31(cx); + ASSERT_TRUE(val.has_value()); + EXPECT_EQ(*val, 42u); +} + +TEST(EqRef, NullEqRef) { + wasmtime_eqref_t raw; + wasmtime_eqref_set_null(&raw); + EXPECT_TRUE(wasmtime_eqref_is_null(&raw)); + + // Upcast null eqref to anyref via C API + wasmtime_anyref_t out; + wasmtime_eqref_to_anyref(&raw, &out); + EXPECT_TRUE(wasmtime_anyref_is_null(&out)); +} + +TEST(I31Ref, CreateAndRead) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + auto cx = store.context(); + + // Create an i31 eqref + EqRef eq = EqRef::from_i31(cx, 42); + EXPECT_TRUE(eq.is_i31(cx)); + + // Read unsigned + auto u = eq.i31_get_u(cx); + ASSERT_TRUE(u.has_value()); + EXPECT_EQ(*u, 42u); + + // Read signed (positive value) + auto s = eq.i31_get_s(cx); + ASSERT_TRUE(s.has_value()); + EXPECT_EQ(*s, 42); + + // Upcast to anyref and check is_i31 + AnyRef any = eq.to_anyref(); + EXPECT_TRUE(any.is_i31(cx)); +} + +TEST(I31Ref, SignedValues) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + auto cx = store.context(); + + // i31 wraps to 31 bits. 0x7FFFFFFF (all 31 bits set) should give -1 signed. + EqRef eq = EqRef::from_i31(cx, 0x7FFFFFFF); + auto u = eq.i31_get_u(cx); + ASSERT_TRUE(u.has_value()); + EXPECT_EQ(*u, 0x7FFFFFFFu); + + auto s = eq.i31_get_s(cx); + ASSERT_TRUE(s.has_value()); + EXPECT_EQ(*s, -1); +} + +TEST(StructRef, CreateAndReadFields) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + auto cx = store.context(); + + // Create a struct type with two mutable i32 fields. + auto ty = StructType::create(engine, { + FieldType::mut_(WASMTIME_I32), + FieldType::mut_(WASMTIME_I32), + }); + auto pre = StructRefPre::create(cx, ty); + + // Allocate a struct with field values 10 and 20. + auto result = + StructRef::create(cx, pre, {Val(int32_t(10)), Val(int32_t(20))}); + ASSERT_TRUE(result); + StructRef s = result.ok(); + + // Read fields back. + auto v0 = s.field(cx, 0); + ASSERT_TRUE(v0); + EXPECT_EQ(v0.ok().i32(), 10); + + auto v1 = s.field(cx, 1); + ASSERT_TRUE(v1); + EXPECT_EQ(v1.ok().i32(), 20); + + // Write field 0 to 42 and read back. + auto set_result = s.set_field(cx, 0, Val(int32_t(42))); + ASSERT_TRUE(set_result); + + auto v0b = s.field(cx, 0); + ASSERT_TRUE(v0b); + EXPECT_EQ(v0b.ok().i32(), 42); +} + +TEST(StructRef, UpcastAndDowncast) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + auto cx = store.context(); + + auto ty = StructType::create(engine, { + FieldType::const_(WASMTIME_I32), + }); + auto pre = StructRefPre::create(cx, ty); + + auto result = StructRef::create(cx, pre, {Val(int32_t(99))}); + ASSERT_TRUE(result); + StructRef s = result.ok(); + + // Upcast to eqref. + EqRef eq = s.to_eqref(); + EXPECT_TRUE(eq.is_struct(cx)); + EXPECT_FALSE(eq.is_i31(cx)); + + // Downcast back to structref. + StructRef s2 = eq.as_struct(cx); + auto v = s2.field(cx, 0); + ASSERT_TRUE(v); + EXPECT_EQ(v.ok().i32(), 99); + + // Upcast to anyref. + AnyRef any = s.to_anyref(); + EXPECT_FALSE(any.is_i31(cx)); +} + +TEST(ArrayRef, CreateAndReadElements) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + auto cx = store.context(); + + // Create an array type with mutable i32 elements. + auto ty = ArrayType::create(engine, FieldType::mut_(WASMTIME_I32)); + auto pre = ArrayRefPre::create(cx, ty); + + // Allocate an array of 5 elements, all initialized to 7. + auto result = ArrayRef::create(cx, pre, Val(int32_t(7)), 5); + ASSERT_TRUE(result); + ArrayRef arr = result.ok(); + + // Check length. + auto len_result = arr.len(cx); + ASSERT_TRUE(len_result); + EXPECT_EQ(len_result.ok(), 5u); + + // Read elements. + for (uint32_t i = 0; i < 5; i++) { + auto v = arr.get(cx, i); + ASSERT_TRUE(v); + EXPECT_EQ(v.ok().i32(), 7); + } + + // Write element 2 to 42, read back. + auto set_result = arr.set(cx, 2, Val(int32_t(42))); + ASSERT_TRUE(set_result); + + auto v2 = arr.get(cx, 2); + ASSERT_TRUE(v2); + EXPECT_EQ(v2.ok().i32(), 42); +} + +TEST(ArrayRef, UpcastAndDowncast) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + auto cx = store.context(); + + auto ty = ArrayType::create(engine, FieldType::const_(WASMTIME_I32)); + auto pre = ArrayRefPre::create(cx, ty); + + auto result = ArrayRef::create(cx, pre, Val(int32_t(99)), 3); + ASSERT_TRUE(result); + ArrayRef arr = result.ok(); + + // Upcast to eqref. + EqRef eq = arr.to_eqref(); + EXPECT_TRUE(eq.is_array(cx)); + EXPECT_FALSE(eq.is_struct(cx)); + EXPECT_FALSE(eq.is_i31(cx)); + + // Downcast back to arrayref. + ArrayRef arr2 = eq.as_array(cx); + auto len = arr2.len(cx); + ASSERT_TRUE(len); + EXPECT_EQ(len.ok(), 3u); + + auto v = arr2.get(cx, 0); + ASSERT_TRUE(v); + EXPECT_EQ(v.ok().i32(), 99); + + // Upcast to anyref. + AnyRef any = arr.to_anyref(); + EXPECT_FALSE(any.is_i31(cx)); +} + +TEST(AnyRef, DowncastI31) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + auto cx = store.context(); + + AnyRef any = AnyRef::i31(cx, 42); + EXPECT_TRUE(any.is_i31(cx)); + EXPECT_TRUE(any.is_eqref(cx)); + EXPECT_FALSE(any.is_struct(cx)); + EXPECT_FALSE(any.is_array(cx)); + + // Downcast to eqref. + auto opt_eq = any.as_eqref(cx); + EXPECT_TRUE(opt_eq); + EqRef eq = *opt_eq; + EXPECT_TRUE(eq.is_i31(cx)); + auto val = eq.i31_get_u(cx); + ASSERT_TRUE(val.has_value()); + EXPECT_EQ(*val, 42u); +} + +TEST(AnyRef, DowncastStruct) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + auto cx = store.context(); + + auto ty = StructType::create(engine, {FieldType::const_(WASMTIME_I32)}); + auto pre = StructRefPre::create(cx, ty); + auto result = StructRef::create(cx, pre, {Val(int32_t(77))}); + ASSERT_TRUE(result); + StructRef s = result.ok(); + + AnyRef any = s.to_anyref(); + EXPECT_TRUE(any.is_eqref(cx)); + EXPECT_TRUE(any.is_struct(cx)); + EXPECT_FALSE(any.is_array(cx)); + EXPECT_FALSE(any.is_i31(cx)); + + // Downcast back to struct. + auto opt_s2 = any.as_struct(cx); + EXPECT_TRUE(opt_s2); + StructRef s2 = *opt_s2; + auto v = s2.field(cx, 0); + ASSERT_TRUE(v); + EXPECT_EQ(v.ok().i32(), 77); +} + +TEST(AnyRef, DowncastArray) { + Config config; + config.wasm_gc(true); + Engine engine(std::move(config)); + Store store(engine); + auto cx = store.context(); + + auto ty = ArrayType::create(engine, FieldType::const_(WASMTIME_I32)); + auto pre = ArrayRefPre::create(cx, ty); + auto result = ArrayRef::create(cx, pre, Val(int32_t(55)), 2); + ASSERT_TRUE(result); + ArrayRef arr = result.ok(); + + AnyRef any = arr.to_anyref(); + EXPECT_TRUE(any.is_eqref(cx)); + EXPECT_FALSE(any.is_struct(cx)); + EXPECT_TRUE(any.is_array(cx)); + EXPECT_FALSE(any.is_i31(cx)); + + // Downcast back to array. + auto opt_arr2 = any.as_array(cx); + EXPECT_TRUE(opt_arr2); + ArrayRef arr2 = *opt_arr2; + auto len = arr2.len(cx); + ASSERT_TRUE(len); + EXPECT_EQ(len.ok(), 2u); +} diff --git a/crates/c-api/tests/tag_type.cc b/crates/c-api/tests/tag_type.cc new file mode 100644 index 000000000000..a2f7db1ead7b --- /dev/null +++ b/crates/c-api/tests/tag_type.cc @@ -0,0 +1,96 @@ +#include + +#include +#include + +using namespace wasmtime; + +// Basic TagType construction and param inspection via functype. +TEST(TagType, Simple) { + Config config; + config.wasm_exceptions(true); + Engine engine(std::move(config)); + + // Tag with no payload: empty functype. + FuncType empty_ft({}, {}); + TagType empty(empty_ft); + auto empty_func = empty->functype(); + EXPECT_EQ(empty_func->params().size(), 0u); + + // Tag with i32 and i64 payload types. + FuncType ft({ValKind::I32, ValKind::I64}, {}); + TagType t(ft); + auto func = t->functype(); + auto params = func->params(); + EXPECT_EQ(params.size(), 2u); + auto it = params.begin(); + EXPECT_EQ(it->kind(), ValKind::I32); + ++it; + EXPECT_EQ(it->kind(), ValKind::I64); + + // Copy. + auto t2 = t; + EXPECT_EQ(t2->functype()->params().size(), 2u); +} + +// Verify that a module exporting an exception tag can have its exports +// enumerated without panicking (regression test for wasmtime issue #10252). +// Previously, CExternType::new() hit todo!() for ExternType::Tag. +TEST(TagType, ModuleExportEnumeration) { + Config config; + config.wasm_exceptions(true); + Engine engine(std::move(config)); + + // Compile a module that exports a tag. The tag has an i32 payload. + Module module = Module::compile(engine, "(module" + " (tag $t (param i32))" + " (export \"t\" (tag $t))" + ")") + .unwrap(); + + auto exports = module.exports(); + ASSERT_EQ(exports.size(), 1); + + auto e = *exports.begin(); + EXPECT_EQ(e.name(), "t"); + + auto extern_ty = ExternType::from_export(e); + ASSERT_TRUE(std::holds_alternative(extern_ty)); + + auto tag_ref = std::get(extern_ty); + auto func = tag_ref.functype(); + auto params = func->params(); + ASSERT_EQ(params.size(), 1u); + EXPECT_EQ(params.begin()->kind(), ValKind::I32); +} + +// Verify that wasm_externtype_kind returns WASM_EXTERN_TAG for tag exports +// and that the C-level cast functions work correctly. +TEST(TagType, ExternTypeKindAndCast) { + Config config; + config.wasm_exceptions(true); + Engine engine(std::move(config)); + + Module module = Module::compile(engine, "(module" + " (tag $t)" + " (export \"t\" (tag $t))" + ")") + .unwrap(); + + auto exports = module.exports(); + ASSERT_EQ(exports.size(), 1); + + auto e = *exports.begin(); + + // Access the raw C export type to verify kind and cast functions. + const wasm_exporttype_t *raw_et = + *reinterpret_cast(&e); + const wasm_externtype_t *ext = wasm_exporttype_type(raw_et); + + EXPECT_EQ(wasm_externtype_kind(ext), WASM_EXTERN_TAG); + EXPECT_NE(wasm_externtype_as_tagtype_const(ext), nullptr); + EXPECT_EQ(wasm_externtype_as_functype_const(ext), nullptr); + EXPECT_EQ(wasm_externtype_as_globaltype_const(ext), nullptr); + EXPECT_EQ(wasm_externtype_as_memorytype_const(ext), nullptr); + EXPECT_EQ(wasm_externtype_as_tabletype_const(ext), nullptr); +} diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index 148676975c70..35da8409b0c0 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -236,8 +236,6 @@ wasmtime_option_group! { pub cache_config: Option, /// Whether or not to enable parallel compilation of modules. pub parallel_compilation: Option, - /// Whether to enable proof-carrying code (PCC)-based validation. - pub pcc: Option, /// Controls whether native unwind information is present in compiled /// object files. pub native_unwind_info: Option, @@ -273,6 +271,22 @@ wasmtime_option_group! { pub log_to_files: Option, /// Enable coredump generation to this file after a WebAssembly trap. pub coredump: Option, + /// Load the given debugger component and attach it to the + /// main module or component. + pub debugger: Option, + /// Pass the given command-line arguments to the debugger + /// component. May be specified multiple times. + #[serde(default)] + pub arg: Vec, + /// Allow the debugger component to inherit stdin.Off by + /// default. + pub inherit_stdin: Option, + /// Allow the debugger component to inherit stdout. Off by + /// default. + pub inherit_stdout: Option, + /// Allow the debugger component to inherit stderr. Off by + /// default. + pub inherit_stderr: Option, } enum Debug { @@ -393,6 +407,8 @@ wasmtime_option_group! { /// GC support in the component model: this corresponds to the 🛸 emoji /// in the component model specification. pub component_model_gc: Option, + /// Map support in the component model. + pub component_model_map: Option, /// Configure support for the function-references proposal. pub function_references: Option, /// Configure support for the stack-switching proposal. @@ -489,6 +505,12 @@ wasmtime_option_group! { /// /// This option can be further overwritten with `--env` flags. pub inherit_env: Option, + /// Inherit stdin from the parent process. On by default. + pub inherit_stdin: Option, + /// Inherit stdout from the parent process. On by default. + pub inherit_stdout: Option, + /// Inherit stderr from the parent process. On by default. + pub inherit_stderr: Option, /// Pass a wasi config variable to the program. #[serde(skip)] pub config_var: Vec, @@ -589,7 +611,7 @@ pub struct CommonOptions { /// /// Generates a serialized trace of the Wasm module execution that captures all /// non-determinism observable by the module. This trace can subsequently be - /// re-executed in a determinstic, embedding-agnostic manner (see the `wasmtime replay` command). + /// re-executed in a deterministic, embedding-agnostic manner (see the `wasmtime replay` command). /// /// Note: Minimal configuration options for deterministic Wasm semantics will be /// enforced during recording by default (NaN canonicalization, deterministic relaxed SIMD). @@ -634,7 +656,7 @@ pub struct CommonOptions { /// Use the specified TOML configuration file. /// This TOML configuration file can provide same configuration options as the - /// `--optimize`, `--codgen`, `--debug`, `--wasm`, `--wasi` CLI options, with a couple exceptions. + /// `--optimize`, `--codegen`, `--debug`, `--wasm`, `--wasi` CLI options, with a couple exceptions. /// /// Additional options specified on the command line will take precedent over options loaded from /// this TOML file. @@ -793,11 +815,6 @@ impl CommonOptions { enable => config.cranelift_nan_canonicalization(enable), true => err, } - match_feature! { - ["cranelift" : self.codegen.pcc] - enable => config.cranelift_pcc(enable), - true => err, - } self.enable_wasm_features(&mut config)?; @@ -1137,6 +1154,7 @@ impl CommonOptions { ("component-model-async", component_model_async_stackful, wasm_component_model_async_stackful) ("component-model-async", component_model_threading, wasm_component_model_threading) ("component-model", component_model_error_context, wasm_component_model_error_context) + ("component-model", component_model_map, wasm_component_model_map) ("component-model", component_model_fixed_length_lists, wasm_component_model_fixed_length_lists) ("threads", threads, wasm_threads) ("gc", gc, wasm_gc) diff --git a/crates/cli-flags/src/opt.rs b/crates/cli-flags/src/opt.rs index c8c8ef4e2098..1427bb93c1c3 100644 --- a/crates/cli-flags/src/opt.rs +++ b/crates/cli-flags/src/opt.rs @@ -9,6 +9,8 @@ use crate::{KeyValuePair, WasiNnGraph}; use clap::builder::{StringValueParser, TypedValueParser, ValueParserFactory}; use clap::error::{Error, ErrorKind}; use serde::de::{self, Visitor}; +use std::path::PathBuf; +use std::str::FromStr; use std::time::Duration; use std::{fmt, marker}; use wasmtime::{Result, bail}; @@ -344,6 +346,20 @@ impl WasmtimeOptionValue for String { } } +impl WasmtimeOptionValue for PathBuf { + const VAL_HELP: &'static str = "=path"; + fn parse(val: Option<&str>) -> Result { + match val { + Some(val) => Ok(PathBuf::from_str(val)?), + None => bail!("value must be specified with key=val syntax"), + } + } + + fn display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:?}") + } +} + impl WasmtimeOptionValue for u32 { const VAL_HELP: &'static str = "=N"; fn parse(val: Option<&str>) -> Result { diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs index 9636c7f342b8..8d3d49f2db02 100644 --- a/crates/component-macro/src/bindgen.rs +++ b/crates/component-macro/src/bindgen.rs @@ -58,13 +58,16 @@ pub fn expand(input: &Config) -> Result { } let mut contents = src.parse::().unwrap(); - // Include a dummy `include_str!` for any files we read so rustc knows that + // Include a dummy `include_bytes!` for any files we read so rustc knows that // we depend on the contents of those files. for file in input.files.iter() { contents.extend( - format!("const _: &str = include_str!(r#\"{}\"#);\n", file.display()) - .parse::() - .unwrap(), + format!( + "const _: &[u8] = include_bytes!(r#\"{}\"#);\n", + file.display() + ) + .parse::() + .unwrap(), ); } diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index a8c0defef677..b8573e2b3a1d 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -13,7 +13,7 @@ version.workspace = true anyhow = { workspace = true, optional = true } hashbrown = { workspace = true, features = ["default-hasher"] } libm = { workspace = true } -serde = { workspace = true } +serde = { workspace = true, optional = true } [lints] workspace = true @@ -26,3 +26,4 @@ backtrace = ["std"] # Enable the `From for anyhow::Error` implementation and # `Error::from_anyhow` constructor. anyhow = ["dep:anyhow"] +serde = ["dep:serde"] diff --git a/crates/core/src/alloc.rs b/crates/core/src/alloc.rs index 63b70164a6f0..0fc2a8c59c8c 100644 --- a/crates/core/src/alloc.rs +++ b/crates/core/src/alloc.rs @@ -19,7 +19,7 @@ pub use try_clone::TryClone; pub use try_collect::{TryCollect, TryExtend, TryFromIterator}; pub use try_cow::{TryCow, TryToOwned}; pub use try_new::{TryNew, try_new}; -pub use vec::Vec; +pub use vec::TryVec; use crate::error::OutOfMemory; use core::{alloc::Layout, ptr::NonNull}; diff --git a/crates/core/src/alloc/boxed.rs b/crates/core/src/alloc/boxed.rs index 478b1d97cf17..a95a52fe6b08 100644 --- a/crates/core/src/alloc/boxed.rs +++ b/crates/core/src/alloc/boxed.rs @@ -1,4 +1,4 @@ -use super::{TryClone, TryNew, Vec, try_alloc}; +use super::{TryClone, TryNew, TryVec, try_alloc}; use crate::{alloc::str_ptr_from_slice_ptr, error::OutOfMemory}; use core::{ alloc::Layout, @@ -114,18 +114,18 @@ use boxed_slice_builder::BoxedSliceBuilder; mod boxed_slice_builder { use super::*; - /// Builder for constructing and initalizing a boxed slice. + /// Builder for constructing and initializing a boxed slice. /// /// Also acts as an RAII guard to handle dropping the already-initialized /// elements when we get too few items or an iterator panics during /// construction. pub struct BoxedSliceBuilder { - vec: Vec, + vec: TryVec, } impl BoxedSliceBuilder { pub fn new(len: usize) -> Result { - let mut vec = Vec::new(); + let mut vec = TryVec::new(); vec.reserve_exact(len)?; Ok(Self { vec }) } @@ -136,7 +136,7 @@ mod boxed_slice_builder { let ptr = ptr.cast::(); // Safety: the pointer was allocated by the global allocator and is // valid for `[T; len]` since it was a boxed slice. - let vec = unsafe { Vec::from_raw_parts(ptr, 0, len) }; + let vec = unsafe { TryVec::from_raw_parts(ptr, 0, len) }; Self { vec } } @@ -197,7 +197,7 @@ mod boxed_slice_builder { debug_assert!(!ptr.is_null()); debug_assert!(ptr.is_aligned()); // Safety: T's dangling pointer is always non-null and aligned. - self.vec = unsafe { Vec::from_raw_parts(ptr, len, len) }; + self.vec = unsafe { TryVec::from_raw_parts(ptr, len, len) }; } debug_assert_eq!(self.capacity(), self.init_len()); return Ok(()); @@ -221,14 +221,14 @@ mod boxed_slice_builder { if new_ptr.is_null() { // Safety: The allocation failed so we retain ownership of `ptr`, // which was a valid vec and we can safely make it a vec again. - self.vec = unsafe { Vec::from_raw_parts(ptr, len, cap) }; + self.vec = unsafe { TryVec::from_raw_parts(ptr, len, cap) }; Err(OutOfMemory::new(new_layout.size())) } else { let new_ptr = new_ptr.cast::(); // Safety: The allocation succeeded, `new_ptr` was reallocated by // the global allocator and points to a valid boxed slice of length // `len`. - self.vec = unsafe { Vec::from_raw_parts(new_ptr, len, len) }; + self.vec = unsafe { TryVec::from_raw_parts(new_ptr, len, len) }; debug_assert_eq!(self.capacity(), self.init_len()); Ok(()) } diff --git a/crates/core/src/alloc/string.rs b/crates/core/src/alloc/string.rs index 018063f2393e..097f1d5ca139 100644 --- a/crates/core/src/alloc/string.rs +++ b/crates/core/src/alloc/string.rs @@ -67,6 +67,7 @@ impl From for TryString { } } +#[cfg(feature = "serde")] impl serde::ser::Serialize for TryString { fn serialize(&self, serializer: S) -> Result where @@ -76,6 +77,7 @@ impl serde::ser::Serialize for TryString { } } +#[cfg(feature = "serde")] impl<'de> serde::de::Deserialize<'de> for TryString { fn deserialize(deserializer: D) -> Result where diff --git a/crates/core/src/alloc/try_collect.rs b/crates/core/src/alloc/try_collect.rs index 766668d6bd40..1b6e8c2d0c24 100644 --- a/crates/core/src/alloc/try_collect.rs +++ b/crates/core/src/alloc/try_collect.rs @@ -1,4 +1,4 @@ -use crate::alloc::Vec; +use crate::alloc::TryVec; use crate::error::OutOfMemory; use std_alloc::boxed::Box; @@ -21,20 +21,20 @@ impl TryCollect for I {} /// Analogue of [`FromIterator`] in the standard library, but used with /// [`TryCollect::try_collect`] instead. pub trait TryFromIterator: Sized { - /// Creates an intance of this collection from the `iter` provided. + /// Creates an instance of this collection from the `iter` provided. /// - /// Does not abort on OOM but insteads return an error. + /// Does not abort on OOM but instead returns an error. fn try_from_iter(iter: I) -> Result where I: Iterator; } -impl TryFromIterator for Vec { +impl TryFromIterator for TryVec { fn try_from_iter(iter: I) -> Result where I: Iterator, { - let mut result = Vec::with_capacity(iter.size_hint().0)?; + let mut result = TryVec::with_capacity(iter.size_hint().0)?; for item in iter { result.push(item)?; } @@ -47,12 +47,12 @@ impl TryFromIterator for Box<[T]> { where I: Iterator, { - let vec = Vec::try_from_iter(iter)?; + let vec = TryVec::try_from_iter(iter)?; vec.into_boxed_slice() } } -impl TryFromIterator, E> for Vec +impl TryFromIterator, E> for TryVec where E: From, { @@ -60,7 +60,7 @@ where where I: Iterator>, { - let mut result = Vec::with_capacity(iter.size_hint().0)?; + let mut result = TryVec::with_capacity(iter.size_hint().0)?; for item in iter { result.push(item?)?; } @@ -76,7 +76,7 @@ where where I: Iterator>, { - let vec = iter.try_collect::, E>()?; + let vec = iter.try_collect::, E>()?; Ok(vec.into_boxed_slice()?) } } @@ -93,7 +93,7 @@ pub trait TryExtend { I: IntoIterator; } -impl TryExtend for Vec { +impl TryExtend for TryVec { fn try_extend(&mut self, iter: I) -> Result<(), OutOfMemory> where I: IntoIterator, @@ -109,12 +109,12 @@ impl TryExtend for Vec { #[cfg(test)] mod tests { - use super::{Box, TryCollect, TryExtend, Vec}; + use super::{Box, TryCollect, TryExtend, TryVec}; use crate::error::{OutOfMemory, Result}; #[test] fn test_vec_collect() -> Result<(), OutOfMemory> { - let v: Vec = (0..10).try_collect()?; + let v: TryVec = (0..10).try_collect()?; assert_eq!(&*v, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); Ok(()) } @@ -128,18 +128,18 @@ mod tests { #[test] fn test_vec_result_collect() -> Result<()> { - let v: Result> = [].into_iter().try_collect(); + let v: Result> = [].into_iter().try_collect(); assert!(v?.is_empty()); - let v: Result> = [Ok(1), Ok(2)].into_iter().try_collect(); + let v: Result> = [Ok(1), Ok(2)].into_iter().try_collect(); assert_eq!(&*v?, &[1, 2]); - let v: Result> = [Ok(1), Err(crate::format_err!("hi"))] + let v: Result> = [Ok(1), Err(crate::format_err!("hi"))] .into_iter() .try_collect(); assert!(v.is_err()); - let v: Result> = [Err(crate::format_err!("hi")), Ok(1)] + let v: Result> = [Err(crate::format_err!("hi")), Ok(1)] .into_iter() .try_collect(); assert!(v.is_err()); @@ -168,7 +168,7 @@ mod tests { #[test] fn test_try_extend() -> Result<(), OutOfMemory> { - let mut vec = Vec::new(); + let mut vec = TryVec::new(); vec.try_extend([1, 2, 3].iter().cloned())?; assert_eq!(&*vec, &[1, 2, 3]); diff --git a/crates/core/src/alloc/try_cow.rs b/crates/core/src/alloc/try_cow.rs index 640120349392..ec4fd75026cd 100644 --- a/crates/core/src/alloc/try_cow.rs +++ b/crates/core/src/alloc/try_cow.rs @@ -26,10 +26,10 @@ impl TryToOwned for [T] where T: TryClone, { - type Owned = Vec; + type Owned = TryVec; fn try_to_owned(&self) -> Result { - let mut v = Vec::with_capacity(self.len())?; + let mut v = TryVec::with_capacity(self.len())?; for x in self { v.push(x.try_clone()?)?; } @@ -222,7 +222,7 @@ mod tests { #[test] fn into_owned() -> Result<()> { let v = TryCow::Borrowed(&[42u8, 36][..]); - let v: Vec = v.into_owned()?; + let v: TryVec = v.into_owned()?; assert_eq!(&*v, &[42, 36]); Ok(()) } diff --git a/crates/core/src/alloc/vec.rs b/crates/core/src/alloc/vec.rs index 0d19a6690f65..4e9a1614a268 100644 --- a/crates/core/src/alloc/vec.rs +++ b/crates/core/src/alloc/vec.rs @@ -3,13 +3,12 @@ use crate::error::OutOfMemory; use core::borrow::Borrow; use core::{ cmp::Ordering, - fmt, - marker::PhantomData, - mem, + fmt, mem, num::NonZeroUsize, ops::{Deref, DerefMut, Index, IndexMut}, slice::SliceIndex, }; +#[cfg(feature = "serde")] use serde::ser::SerializeSeq; use std_alloc::alloc::Layout; use std_alloc::boxed::Box; @@ -17,10 +16,10 @@ use std_alloc::vec::Vec as StdVec; /// Same as the [`std::vec!`] macro but returns an error on allocation failure. #[macro_export] -macro_rules! vec { +macro_rules! try_vec { ( $( $elem:expr ),* ) => {{ let len = $crate::private_len!( $( $elem ),* ); - $crate::alloc::Vec::with_capacity(len).and_then(|mut v| { + $crate::alloc::TryVec::with_capacity(len).and_then(|mut v| { $( v.push($elem)?; )* let _ = &mut v; Ok(v) @@ -31,9 +30,9 @@ macro_rules! vec { let len: usize = $len; if let Some(len) = ::core::num::NonZeroUsize::new(len) { let elem = $elem; - $crate::alloc::Vec::from_elem(elem, len) + $crate::alloc::TryVec::from_elem(elem, len) } else { - Ok($crate::alloc::Vec::new()) + Ok($crate::alloc::TryVec::new()) } }}; @@ -50,11 +49,11 @@ macro_rules! private_len { /// Like `std::vec::Vec` but all methods that allocate force handling allocation /// failure. #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Vec { +pub struct TryVec { inner: StdVec, } -impl Default for Vec { +impl Default for TryVec { fn default() -> Self { Self { inner: Default::default(), @@ -62,18 +61,18 @@ impl Default for Vec { } } -impl fmt::Debug for Vec { +impl fmt::Debug for TryVec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.inner, f) } } -impl TryClone for Vec +impl TryClone for TryVec where T: TryClone, { fn try_clone(&self) -> Result { - let mut v = Vec::with_capacity(self.len())?; + let mut v = TryVec::with_capacity(self.len())?; for x in self { v.push(x.try_clone()?).expect("reserved capacity"); } @@ -81,7 +80,7 @@ where } } -impl Vec { +impl TryVec { /// Same as [`std::vec::Vec::new`]. pub const fn new() -> Self { Self { @@ -192,6 +191,36 @@ impl Vec { Ok(()) } + /// Same as [`std::vec::Vec::resize_with`] but returns an error on + /// allocation failure. + pub fn resize_with(&mut self, new_len: usize, f: F) -> Result<(), OutOfMemory> + where + F: FnMut() -> T, + { + let len = self.len(); + if new_len > len { + self.reserve(new_len - len)?; + } + self.inner.resize_with(new_len, f); + Ok(()) + } + + /// Same as [`std::vec::Vec::retain`]. + pub fn retain(&mut self, f: F) + where + F: FnMut(&T) -> bool, + { + self.inner.retain(f); + } + + /// Same as [`std::vec::Vec::retain_mut`]. + pub fn retain_mut(&mut self, f: F) + where + F: FnMut(&mut T) -> bool, + { + self.inner.retain_mut(f); + } + /// Same as [`std::vec::Vec::into_raw_parts`]. pub fn into_raw_parts(mut self) -> (*mut T, usize, usize) { // NB: Can't use `Vec::into_raw_parts` until our MSRV is >= 1.93. @@ -214,7 +243,7 @@ impl Vec { /// Same as [`std::vec::Vec::from_raw_parts`]. pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Self { - Vec { + TryVec { // Safety: Same as our unsafe contract. inner: unsafe { StdVec::from_raw_parts(ptr, length, capacity) }, } @@ -274,7 +303,7 @@ impl Vec { // SAFETY: If reallocation fails then it's guaranteed that the // original allocation is not tampered with, so it's safe to // reassemble the original vector. - *self = unsafe { Vec::from_raw_parts(ptr, len, cap) }; + *self = unsafe { TryVec::from_raw_parts(ptr, len, cap) }; Err(oom) } } @@ -294,9 +323,20 @@ impl Vec { pub fn clear(&mut self) { self.inner.clear(); } + + /// Same as [`std::vec::Vec::as_mut_ptr`]. + // + // Note that this is technically inherited through the `DerefMut` impl but + // that converts `&mut Self` to `&mut [T]` which invalidates all previously + // derived pointers. This causes problems in Miri so by having an inherent + // method here it means that the borrow scope matches what we want with + // Miri. + pub fn as_mut_ptr(&mut self) -> *mut T { + self.inner.as_mut_ptr() + } } -impl Deref for Vec { +impl Deref for TryVec { type Target = [T]; fn deref(&self) -> &Self::Target { @@ -304,25 +344,25 @@ impl Deref for Vec { } } -impl DerefMut for Vec { +impl DerefMut for TryVec { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } -impl AsRef<[T]> for Vec { +impl AsRef<[T]> for TryVec { fn as_ref(&self) -> &[T] { self } } -impl Borrow<[T]> for Vec { +impl Borrow<[T]> for TryVec { fn borrow(&self) -> &[T] { self } } -impl Index for Vec +impl Index for TryVec where I: SliceIndex<[T]>, { @@ -333,7 +373,7 @@ where } } -impl IndexMut for Vec +impl IndexMut for TryVec where I: SliceIndex<[T]>, { @@ -342,7 +382,7 @@ where } } -impl IntoIterator for Vec { +impl IntoIterator for TryVec { type Item = T; type IntoIter = std_alloc::vec::IntoIter; @@ -351,7 +391,7 @@ impl IntoIterator for Vec { } } -impl<'a, T> IntoIterator for &'a Vec { +impl<'a, T> IntoIterator for &'a TryVec { type Item = &'a T; type IntoIter = core::slice::Iter<'a, T>; @@ -361,7 +401,7 @@ impl<'a, T> IntoIterator for &'a Vec { } } -impl<'a, T> IntoIterator for &'a mut Vec { +impl<'a, T> IntoIterator for &'a mut TryVec { type Item = &'a mut T; type IntoIter = core::slice::IterMut<'a, T>; @@ -371,25 +411,26 @@ impl<'a, T> IntoIterator for &'a mut Vec { } } -impl From> for StdVec { - fn from(v: Vec) -> Self { +impl From> for StdVec { + fn from(v: TryVec) -> Self { v.inner } } -impl From> for Vec { +impl From> for TryVec { fn from(inner: StdVec) -> Self { Self { inner } } } -impl From> for Vec { +impl From> for TryVec { fn from(boxed_slice: Box<[T]>) -> Self { Self::from(StdVec::from(boxed_slice)) } } -impl serde::ser::Serialize for Vec +#[cfg(feature = "serde")] +impl serde::ser::Serialize for TryVec where T: serde::ser::Serialize, { @@ -405,7 +446,8 @@ where } } -impl<'de, T> serde::de::Deserialize<'de> for Vec +#[cfg(feature = "serde")] +impl<'de, T> serde::de::Deserialize<'de> for TryVec where T: serde::de::Deserialize<'de>, { @@ -413,13 +455,15 @@ where where D: serde::Deserializer<'de>, { - struct Visitor(PhantomData Vec>); + use core::marker::PhantomData; + + struct Visitor(PhantomData TryVec>); impl<'de, T> serde::de::Visitor<'de> for Visitor where T: serde::de::Deserialize<'de>, { - type Value = Vec; + type Value = TryVec; fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("a `wasmtime_core::alloc::Vec` sequence") @@ -431,7 +475,7 @@ where { use serde::de::Error as _; - let mut v = Vec::new(); + let mut v = TryVec::new(); if let Some(len) = seq.size_hint() { v.reserve_exact(len).map_err(|oom| A::Error::custom(oom))?; @@ -451,32 +495,32 @@ where #[cfg(test)] mod tests { - use super::Vec; + use super::TryVec; use crate::error::OutOfMemory; #[test] fn test_into_boxed_slice() -> Result<(), OutOfMemory> { - assert_eq!(*Vec::::new().into_boxed_slice()?, []); + assert_eq!(*TryVec::::new().into_boxed_slice()?, []); - let mut vec = Vec::new(); + let mut vec = TryVec::new(); vec.push(1)?; assert_eq!(*vec.into_boxed_slice()?, [1]); - let mut vec = Vec::with_capacity(2)?; + let mut vec = TryVec::with_capacity(2)?; vec.push(1)?; assert_eq!(*vec.into_boxed_slice()?, [1]); - let mut vec = Vec::with_capacity(2)?; + let mut vec = TryVec::with_capacity(2)?; vec.push(1_u128)?; assert_eq!(*vec.into_boxed_slice()?, [1]); - assert_eq!(*Vec::<()>::new().into_boxed_slice()?, []); + assert_eq!(*TryVec::<()>::new().into_boxed_slice()?, []); - let mut vec = Vec::new(); + let mut vec = TryVec::new(); vec.push(())?; assert_eq!(*vec.into_boxed_slice()?, [()]); - let vec = Vec::::with_capacity(2)?; + let vec = TryVec::::with_capacity(2)?; assert_eq!(*vec.into_boxed_slice()?, []); Ok(()) } diff --git a/crates/core/src/error/error.rs b/crates/core/src/error/error.rs index bf46b05b9074..0a9ece9c5a4e 100644 --- a/crates/core/src/error/error.rs +++ b/crates/core/src/error/error.rs @@ -591,7 +591,7 @@ impl Error { { if TypeId::of::() == TypeId::of::() { // Although we know that `E == OutOfMemory` in this block, the - // compiler doesn't understand that, and we have to do this litle + // compiler doesn't understand that, and we have to do this little // dance. union ToOom { oom: OutOfMemory, diff --git a/crates/core/src/slab.rs b/crates/core/src/slab.rs index f7b7fd50fcb7..38a483a839f9 100644 --- a/crates/core/src/slab.rs +++ b/crates/core/src/slab.rs @@ -133,7 +133,7 @@ //! } //! ``` -use crate::alloc::Vec; +use crate::alloc::TryVec; use crate::error::OutOfMemory; use core::fmt; use core::num::NonZeroU32; @@ -171,7 +171,7 @@ impl Id { pub struct Slab { /// The slab's entries, each is either occupied and holding a `T` or vacant /// and is a link the free list. - entries: Vec>, + entries: TryVec>, /// The index of the first free entry in the free list. free: Option, @@ -224,7 +224,7 @@ impl Default for Slab { #[inline] fn default() -> Self { Self { - entries: Vec::default(), + entries: TryVec::default(), free: None, len: 0, } diff --git a/crates/cranelift/src/bounds_checks.rs b/crates/cranelift/src/bounds_checks.rs index 4935a82f9ce7..2743dd861dbc 100644 --- a/crates/cranelift/src/bounds_checks.rs +++ b/crates/cranelift/src/bounds_checks.rs @@ -29,7 +29,6 @@ use Reachability::*; use cranelift_codegen::{ cursor::{Cursor, FuncCursor}, ir::{self, InstBuilder, RelSourceLoc, condcodes::IntCC}, - ir::{Expr, Fact}, }; use cranelift_frontend::FunctionBuilder; @@ -159,7 +158,6 @@ pub fn bounds_check_and_compute_addr( backwards_offset, ir::types::I32, env.pointer_type(), - false, &mut builder.cursor(), trap, ); @@ -180,12 +178,10 @@ fn bounds_check_field_access( trap: ir::TrapCode, ) -> Reachability { let pointer_bit_width = u16::try_from(env.pointer_type().bits()).unwrap(); - let bound_gv = heap.bound; - let orig_index = index; + let clif_memory_traps_enabled = env.clif_memory_traps_enabled(); let spectre_mitigations_enabled = env.heap_access_spectre_mitigation() && clif_memory_traps_enabled; - let pcc = env.proof_carrying_code(); let host_page_size_log2 = env.target_config().page_size_align_log2; let can_use_virtual_memory = heap @@ -206,7 +202,6 @@ fn bounds_check_field_access( index, heap.index_type(), env.pointer_type(), - heap.pcc_memory_type.is_some(), &mut builder.cursor(), trap, ); @@ -223,57 +218,10 @@ fn bounds_check_field_access( OobBehavior::ExplicitTrap }; - let make_compare = |builder: &mut FunctionBuilder, - compare_kind: IntCC, - lhs: ir::Value, - lhs_off: Option, - rhs: ir::Value, - rhs_off: Option| { - let result = builder.ins().icmp(compare_kind, lhs, rhs); - if pcc { - // Name the original value as a def of the SSA value; - // if the value was extended, name that as well with a - // dynamic range, overwriting the basic full-range - // fact that we previously put on the uextend. - builder.func.dfg.facts[orig_index] = Some(Fact::Def { value: orig_index }); - if index != orig_index { - builder.func.dfg.facts[index] = Some(Fact::value(pointer_bit_width, orig_index)); - } - - // Create a fact on the LHS that is a "trivial symbolic - // fact": v1 has range v1+LHS_off..=v1+LHS_off - builder.func.dfg.facts[lhs] = Some(Fact::value_offset( - pointer_bit_width, - orig_index, - lhs_off.unwrap(), - )); - // If the RHS is a symbolic value (v1 or gv1), we can - // emit a Compare fact. - if let Some(rhs) = builder.func.dfg.facts[rhs] - .as_ref() - .and_then(|f| f.as_symbol()) - { - builder.func.dfg.facts[result] = Some(Fact::Compare { - kind: compare_kind, - lhs: Expr::offset(&Expr::value(orig_index), lhs_off.unwrap()).unwrap(), - rhs: Expr::offset(rhs, rhs_off.unwrap()).unwrap(), - }); - } - // Likewise, if the RHS is a constant, we can emit a - // Compare fact. - if let Some(k) = builder.func.dfg.facts[rhs] - .as_ref() - .and_then(|f| f.as_const(pointer_bit_width)) - { - builder.func.dfg.facts[result] = Some(Fact::Compare { - kind: compare_kind, - lhs: Expr::offset(&Expr::value(orig_index), lhs_off.unwrap()).unwrap(), - rhs: Expr::constant((k as i64).checked_add(rhs_off.unwrap()).unwrap()), - }); - } - } - result - }; + let make_compare = + |builder: &mut FunctionBuilder, compare_kind: IntCC, lhs: ir::Value, rhs: ir::Value| { + builder.ins().icmp(compare_kind, lhs, rhs) + }; // We need to emit code that will trap (or compute an address that will trap // when accessed) if @@ -365,7 +313,6 @@ fn bounds_check_field_access( env.pointer_type(), index, offset, - AddrPcc::static32(heap.pcc_memory_type, memory_reservation + memory_guard_size), )); } @@ -378,7 +325,6 @@ fn bounds_check_field_access( env.pointer_type(), index, offset, - AddrPcc::static32(heap.pcc_memory_type, memory_reservation + memory_guard_size), )); } @@ -409,17 +355,11 @@ fn bounds_check_field_access( let adjusted_bound_value = builder .ins() .iconst(env.pointer_type(), adjusted_bound as i64); - if pcc { - builder.func.dfg.facts[adjusted_bound_value] = - Some(Fact::constant(pointer_bit_width, adjusted_bound)); - } let oob = make_compare( builder, IntCC::UnsignedGreaterThan, index, - Some(0), adjusted_bound_value, - Some(0), ); return Reachable(explicit_check_oob_condition_and_compute_addr( env, @@ -427,9 +367,7 @@ fn bounds_check_field_access( heap, index, offset, - access_size, oob_behavior, - AddrPcc::static32(heap.pcc_memory_type, memory_reservation), oob, trap, )); @@ -447,23 +385,14 @@ fn bounds_check_field_access( // largely only applicable for native platforms. if offset_and_size == 1 && !env.is_pulley() { let bound = get_dynamic_heap_bound(builder, env, heap); - let oob = make_compare( - builder, - IntCC::UnsignedGreaterThanOrEqual, - index, - Some(0), - bound, - Some(0), - ); + let oob = make_compare(builder, IntCC::UnsignedGreaterThanOrEqual, index, bound); return Reachable(explicit_check_oob_condition_and_compute_addr( env, builder, heap, index, offset, - access_size, oob_behavior, - AddrPcc::dynamic(heap.pcc_memory_type, bound_gv), oob, trap, )); @@ -496,23 +425,14 @@ fn bounds_check_field_access( // will all emit the same `index > bound` check, which we can GVN. if can_use_virtual_memory && offset_and_size <= memory_guard_size { let bound = get_dynamic_heap_bound(builder, env, heap); - let oob = make_compare( - builder, - IntCC::UnsignedGreaterThan, - index, - Some(0), - bound, - Some(0), - ); + let oob = make_compare(builder, IntCC::UnsignedGreaterThan, index, bound); return Reachable(explicit_check_oob_condition_and_compute_addr( env, builder, heap, index, offset, - access_size, oob_behavior, - AddrPcc::dynamic(heap.pcc_memory_type, bound_gv), oob, trap, )); @@ -529,35 +449,15 @@ fn bounds_check_field_access( let bound = get_dynamic_heap_bound(builder, env, heap); let adjustment = offset_and_size as i64; let adjustment_value = builder.ins().iconst(env.pointer_type(), adjustment); - if pcc { - builder.func.dfg.facts[adjustment_value] = - Some(Fact::constant(pointer_bit_width, offset_and_size)); - } let adjusted_bound = builder.ins().isub(bound, adjustment_value); - if pcc { - builder.func.dfg.facts[adjusted_bound] = Some(Fact::global_value_offset( - pointer_bit_width, - bound_gv, - -adjustment, - )); - } - let oob = make_compare( - builder, - IntCC::UnsignedGreaterThan, - index, - Some(0), - adjusted_bound, - Some(adjustment), - ); + let oob = make_compare(builder, IntCC::UnsignedGreaterThan, index, adjusted_bound); return Reachable(explicit_check_oob_condition_and_compute_addr( env, builder, heap, index, offset, - access_size, oob_behavior, - AddrPcc::dynamic(heap.pcc_memory_type, bound_gv), oob, trap, )); @@ -573,36 +473,16 @@ fn bounds_check_field_access( // Explicit cast from u64 to i64: we just want the raw // bits, and iconst takes an `Imm64`. .iconst(env.pointer_type(), offset_and_size as i64); - if pcc { - builder.func.dfg.facts[access_size_val] = - Some(Fact::constant(pointer_bit_width, offset_and_size)); - } let adjusted_index = env.uadd_overflow_trap(builder, index, access_size_val, trap); - if pcc { - builder.func.dfg.facts[adjusted_index] = Some(Fact::value_offset( - pointer_bit_width, - index, - i64::try_from(offset_and_size).unwrap(), - )); - } let bound = get_dynamic_heap_bound(builder, env, heap); - let oob = make_compare( - builder, - IntCC::UnsignedGreaterThan, - adjusted_index, - i64::try_from(offset_and_size).ok(), - bound, - Some(0), - ); + let oob = make_compare(builder, IntCC::UnsignedGreaterThan, adjusted_index, bound); Reachable(explicit_check_oob_condition_and_compute_addr( env, builder, heap, index, offset, - access_size, oob_behavior, - AddrPcc::dynamic(heap.pcc_memory_type, bound_gv), oob, trap, )) @@ -614,46 +494,20 @@ fn get_dynamic_heap_bound( env: &mut FuncEnvironment<'_>, heap: &HeapData, ) -> ir::Value { - let enable_pcc = heap.pcc_memory_type.is_some(); - - let (value, gv) = match heap.memory.static_heap_size() { + match heap.memory.static_heap_size() { // The heap has a constant size, no need to actually load the - // bound. TODO: this is currently disabled for PCC because we - // can't easily prove that the GV load indeed results in a - // constant (that information is lost in the CLIF). We'll want - // to create an `iconst` GV expression kind to reify this fact - // in the GV, then re-enable this opt. (Or, alternately, - // compile such memories with a static-bound memtype and - // facts.) - Some(max_size) if !enable_pcc => ( - builder.ins().iconst(env.pointer_type(), max_size as i64), - heap.bound, - ), + // bound. + Some(max_size) => builder.ins().iconst(env.pointer_type(), max_size as i64), // Load the heap bound from its global variable. - _ => ( - builder.ins().global_value(env.pointer_type(), heap.bound), - heap.bound, - ), - }; - - // If proof-carrying code is enabled, apply a fact to the range to - // tie it to the GV. - if enable_pcc { - builder.func.dfg.facts[value] = Some(Fact::global_value( - u16::try_from(env.pointer_type().bits()).unwrap(), - gv, - )); + _ => builder.ins().global_value(env.pointer_type(), heap.bound), } - - value } fn cast_index_to_pointer_ty( index: ir::Value, index_ty: ir::Type, pointer_ty: ir::Type, - pcc: bool, pos: &mut FuncCursor, trap: ir::TrapCode, ) -> ir::Value { @@ -666,7 +520,7 @@ fn cast_index_to_pointer_ty( // larger than 2**32 then that's guaranteed to be out-of-bounds, otherwise we // `ireduce` the index. // - // Also note that at this time this branch doesn't support pcc nor the + // Also note that at this time this branch doesn't support the // value-label-ranges of the below path. // // Finally, note that the returned `low_bits` here are still subject to an @@ -686,14 +540,6 @@ fn cast_index_to_pointer_ty( // Convert `index` to `addr_ty`. let extended_index = pos.ins().uextend(pointer_ty, index); - // Add a range fact on the extended value. - if pcc { - pos.func.dfg.facts[extended_index] = Some(Fact::max_range_for_width_extended( - u16::try_from(index_ty.bits()).unwrap(), - u16::try_from(pointer_ty.bits()).unwrap(), - )); - } - // Add debug value-label alias so that debuginfo can name the extended // value as the address let loc = pos.srcloc(); @@ -706,25 +552,6 @@ fn cast_index_to_pointer_ty( extended_index } -/// Which facts do we want to emit for proof-carrying code, if any, on -/// address computations? -#[derive(Clone, Copy, Debug)] -enum AddrPcc { - /// A 32-bit static memory with the given size. - Static32(ir::MemoryType, u64), - /// Dynamic bounds-check, with actual memory size (the `GlobalValue`) - /// expressed symbolically. - Dynamic(ir::MemoryType, ir::GlobalValue), -} -impl AddrPcc { - fn static32(memory_type: Option, size: u64) -> Option { - memory_type.map(|ty| AddrPcc::Static32(ty, size)) - } - fn dynamic(memory_type: Option, bound: ir::GlobalValue) -> Option { - memory_type.map(|ty| AddrPcc::Dynamic(ty, bound)) - } -} - /// What to do on out-of-bounds for the /// `explicit_check_oob_condition_and_compute_addr` function below. enum OobBehavior { @@ -749,10 +576,7 @@ fn explicit_check_oob_condition_and_compute_addr( heap: &HeapData, index: ir::Value, offset: u32, - access_size: u8, oob_behavior: OobBehavior, - // Whether we're emitting PCC facts. - pcc: Option, // The `i8` boolean value that is non-zero when the heap access is out of // bounds (and therefore we should trap) and is zero when the heap access is // in bounds (and therefore we can proceed). @@ -764,7 +588,7 @@ fn explicit_check_oob_condition_and_compute_addr( } let addr_ty = env.pointer_type(); - let mut addr = compute_addr(&mut builder.cursor(), heap, addr_ty, index, offset, pcc); + let mut addr = compute_addr(&mut builder.cursor(), heap, addr_ty, index, offset); if let OobBehavior::ConditionallyLoadFromZero { select_spectre_guard, @@ -782,37 +606,6 @@ fn explicit_check_oob_condition_and_compute_addr( } else { builder.ins().select(oob_condition, null, addr) }; - - match pcc { - None => {} - Some(AddrPcc::Static32(ty, size)) => { - builder.func.dfg.facts[null] = - Some(Fact::constant(u16::try_from(addr_ty.bits()).unwrap(), 0)); - builder.func.dfg.facts[addr] = Some(Fact::Mem { - ty, - min_offset: 0, - max_offset: size.checked_sub(u64::from(access_size)).unwrap(), - nullable: true, - }); - } - Some(AddrPcc::Dynamic(ty, gv)) => { - builder.func.dfg.facts[null] = - Some(Fact::constant(u16::try_from(addr_ty.bits()).unwrap(), 0)); - builder.func.dfg.facts[addr] = Some(Fact::DynamicMem { - ty, - min: Expr::constant(0), - max: Expr::offset( - &Expr::global_value(gv), - i64::try_from(env.tunables().memory_guard_size) - .unwrap() - .checked_sub(i64::from(access_size)) - .unwrap(), - ) - .unwrap(), - nullable: true, - }); - } - } } addr @@ -830,54 +623,12 @@ fn compute_addr( addr_ty: ir::Type, index: ir::Value, offset: u32, - pcc: Option, ) -> ir::Value { debug_assert_eq!(pos.func.dfg.value_type(index), addr_ty); let heap_base = pos.ins().global_value(addr_ty, heap.base); - - match pcc { - None => {} - Some(AddrPcc::Static32(ty, _size)) => { - pos.func.dfg.facts[heap_base] = Some(Fact::Mem { - ty, - min_offset: 0, - max_offset: 0, - nullable: false, - }); - } - Some(AddrPcc::Dynamic(ty, _limit)) => { - pos.func.dfg.facts[heap_base] = Some(Fact::dynamic_base_ptr(ty)); - } - } - let base_and_index = pos.ins().iadd(heap_base, index); - match pcc { - None => {} - Some(AddrPcc::Static32(ty, _) | AddrPcc::Dynamic(ty, _)) => { - if let Some(idx) = pos.func.dfg.facts[index] - .as_ref() - .and_then(|f| f.as_symbol()) - .cloned() - { - pos.func.dfg.facts[base_and_index] = Some(Fact::DynamicMem { - ty, - min: idx.clone(), - max: idx, - nullable: false, - }); - } else { - pos.func.dfg.facts[base_and_index] = Some(Fact::Mem { - ty, - min_offset: 0, - max_offset: u64::from(u32::MAX), - nullable: false, - }); - } - } - } - if offset == 0 { base_and_index } else { @@ -886,47 +637,7 @@ fn compute_addr( // potentially are letting speculative execution read the whole first // 4GiB of memory. let offset_val = pos.ins().iconst(addr_ty, i64::from(offset)); - - if pcc.is_some() { - pos.func.dfg.facts[offset_val] = Some(Fact::constant( - u16::try_from(addr_ty.bits()).unwrap(), - u64::from(offset), - )); - } - - let result = pos.ins().iadd(base_and_index, offset_val); - - match pcc { - None => {} - Some(AddrPcc::Static32(ty, _) | AddrPcc::Dynamic(ty, _)) => { - if let Some(idx) = pos.func.dfg.facts[index] - .as_ref() - .and_then(|f| f.as_symbol()) - { - pos.func.dfg.facts[result] = Some(Fact::DynamicMem { - ty, - min: idx.clone(), - // Safety: adding an offset to an expression with - // zero offset -- add cannot wrap, so `unwrap()` - // cannot fail. - max: Expr::offset(idx, i64::from(offset)).unwrap(), - nullable: false, - }); - } else { - pos.func.dfg.facts[result] = Some(Fact::Mem { - ty, - min_offset: u64::from(offset), - // Safety: can't overflow -- two u32s summed in a - // 64-bit add. TODO: when memory64 is supported here, - // `u32::MAX` is no longer true, and we'll need to - // handle overflow here. - max_offset: u64::from(u32::MAX) + u64::from(offset), - nullable: false, - }); - } - } - } - result + pos.ins().iadd(base_and_index, offset_val) } } diff --git a/crates/cranelift/src/compiled_function.rs b/crates/cranelift/src/compiled_function.rs index 451b41f3ab8f..b28f2d5349a6 100644 --- a/crates/cranelift/src/compiled_function.rs +++ b/crates/cranelift/src/compiled_function.rs @@ -6,7 +6,7 @@ use cranelift_codegen::{ isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo, }; use wasmtime_environ::{ - FilePos, FrameStateSlotBuilder, InstructionAddressMap, PrimaryMap, TrapInformation, + FilePos, FrameStateSlotBuilder, InstructionAddressMap, ModulePC, PrimaryMap, TrapInformation, }; #[derive(Debug, Clone, PartialEq, Eq, Default)] @@ -67,8 +67,8 @@ pub struct CompiledFunction { metadata: CompiledFunctionMetadata, /// Debug metadata for the top-level function's state slot. pub debug_slot_descriptor: Option, - /// Debug breakpoint patches: Wasm PC, offset range in buffer. - pub breakpoint_patch_points: Vec<(u32, Range)>, + /// Debug breakpoint patches: module-relative Wasm PC, offset range in buffer. + pub breakpoint_patch_points: Vec<(ModulePC, Range)>, } impl CompiledFunction { @@ -120,7 +120,7 @@ impl CompiledFunction { // second-to-last tag will get the innermost Wasm PC (if // there are multiple nested frames due to inlining). assert!(tag.tags.len() >= 3); - let ir::DebugTag::User(wasm_pc) = tag.tags[tag.tags.len() - 2] else { + let ir::DebugTag::User(wasm_pc_raw) = tag.tags[tag.tags.len() - 2] else { panic!("invalid tag") }; @@ -128,7 +128,7 @@ impl CompiledFunction { let patchable_end = patchable_callsite.ret_addr; self.breakpoint_patch_points - .push((wasm_pc, patchable_start..patchable_end)); + .push((ModulePC::new(wasm_pc_raw), patchable_start..patchable_end)); tags.next(); patchable_callsites.next(); @@ -218,7 +218,7 @@ impl CompiledFunction { /// Returns an iterator over breakpoint patches for this function. /// /// Each tuple is (wasm PC, buffer offset range). - pub fn breakpoint_patches(&self) -> impl Iterator)> + '_ { + pub fn breakpoint_patches(&self) -> impl Iterator)> + '_ { self.breakpoint_patch_points.iter().cloned() } } diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index cb0b22526e2b..b1ce4028cd33 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -37,7 +37,7 @@ use wasmtime_environ::{ Abi, AddressMapSection, BuiltinFunctionIndex, CacheStore, CompileError, CompiledFunctionBody, DefinedFuncIndex, FlagValue, FrameInstPos, FrameStackShape, FrameStateSlotBuilder, FrameTableBuilder, FuncKey, FunctionBodyData, FunctionLoc, HostCall, InliningCompiler, - ModuleTranslation, ModuleTypesBuilder, PtrSize, StackMapSection, StaticModuleIndex, + ModulePC, ModuleTranslation, ModuleTypesBuilder, PtrSize, StackMapSection, StaticModuleIndex, TrapEncodingBuilder, TrapSentinel, TripleExt, Tunables, WasmFuncType, WasmValType, prelude::*, }; use wasmtime_unwinder::ExceptionTableBuilder; @@ -520,7 +520,7 @@ impl wasmtime_environ::Compiler for Compiler { } } - let mut breakpoint_table = Vec::new(); + let mut breakpoint_table: Vec<(ModulePC, Range)> = Vec::new(); let mut nop_units = None; let mut ret = Vec::with_capacity(funcs.len()); @@ -1623,7 +1623,7 @@ fn clif_to_env_frame_tables<'a>( for frame_tags in tag_site.tags.chunks_exact(3) { let &[ ir::DebugTag::StackSlot(slot), - ir::DebugTag::User(wasm_pc), + ir::DebugTag::User(wasm_pc_raw), ir::DebugTag::User(stack_shape), ] = frame_tags else { @@ -1645,7 +1645,7 @@ fn clif_to_env_frame_tables<'a>( }); frames.push(( - wasm_pc, + ModulePC::new(wasm_pc_raw), frame_descriptor, FrameStackShape::from_raw(stack_shape), )); @@ -1669,8 +1669,8 @@ fn clif_to_env_frame_tables<'a>( /// Wasmtime's serialized metadata. fn clif_to_env_breakpoints( range: Range, - breakpoint_patches: impl Iterator)>, - patch_table: &mut Vec<(u32, Range)>, + breakpoint_patches: impl Iterator)>, + patch_table: &mut Vec<(ModulePC, Range)>, ) -> Result<()> { patch_table.extend(breakpoint_patches.map(|(wasm_pc, offset_range)| { let start = offset_range.start + u32::try_from(range.start).unwrap(); diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index 22849dbb1621..1bde277a33f1 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -11,7 +11,6 @@ use crate::trap::TranslateTrap; use cranelift_codegen::cursor::FuncCursor; use cranelift_codegen::ir::condcodes::{FloatCC, IntCC}; use cranelift_codegen::ir::immediates::{Imm64, Offset32, V128Imm}; -use cranelift_codegen::ir::pcc::Fact; use cranelift_codegen::ir::{ self, BlockArg, Endianness, ExceptionTableData, ExceptionTableItem, types, }; @@ -27,12 +26,12 @@ use std::mem; use wasmparser::{FuncValidator, Operator, WasmFeatures, WasmModuleResources}; use wasmtime_core::math::f64_cvt_to_int_bounds; use wasmtime_environ::{ - BuiltinFunctionIndex, DataIndex, DefinedFuncIndex, ElemIndex, EngineOrModuleTypeIndex, - FrameStateSlotBuilder, FrameValType, FuncIndex, FuncKey, GlobalConstValue, GlobalIndex, - IndexType, Memory, MemoryIndex, Module, ModuleInternedTypeIndex, ModuleTranslation, - ModuleTypesBuilder, PtrSize, Table, TableIndex, TagIndex, Tunables, TypeConvert, TypeIndex, - VMOffsets, WasmCompositeInnerType, WasmFuncType, WasmHeapTopType, WasmHeapType, WasmRefType, - WasmResult, WasmValType, + BuiltinFunctionIndex, ComponentPC, DataIndex, DefinedFuncIndex, ElemIndex, + EngineOrModuleTypeIndex, FrameStateSlotBuilder, FrameValType, FuncIndex, FuncKey, + GlobalConstValue, GlobalIndex, IndexType, Memory, MemoryIndex, Module, ModuleInternedTypeIndex, + ModuleTranslation, ModuleTypesBuilder, PtrSize, Table, TableIndex, TagIndex, Tunables, + TypeConvert, TypeIndex, VMOffsets, WasmCompositeInnerType, WasmFuncType, WasmHeapTopType, + WasmHeapType, WasmRefType, WasmResult, WasmValType, }; use wasmtime_environ::{FUNCREF_INIT_BIT, FUNCREF_MASK}; @@ -137,6 +136,11 @@ pub struct FuncEnvironment<'module_environment> { needs_gc_heap: bool, entities: WasmEntities, + /// The byte offset of the module's wasm binary within the outer + /// binary (e.g. a component). Used to make source locations in + /// guest-debug frame tables module-relative. + pub(crate) wasm_module_offset: u64, + /// Translation state at the given point. pub(crate) stacks: FuncTranslationStacks, @@ -168,10 +172,6 @@ pub struct FuncEnvironment<'module_environment> { /// The Cranelift global for our vmctx's `*mut VMStoreContext`. vm_store_context: Option, - /// The PCC memory type describing the vmctx layout, if we're - /// using PCC. - pcc_vmctx_memtype: Option, - /// Caches of signatures for builtin functions. builtin_functions: BuiltinFunctions, @@ -223,6 +223,12 @@ pub struct FuncEnvironment<'module_environment> { /// The stack-slot used for exposing Wasm state via debug /// instrumentation, if any, and the builder containing its metadata. pub(crate) state_slot: Option<(ir::StackSlot, FrameStateSlotBuilder)>, + + /// The next-srcloc: the location of the operator *after* this one + /// (in original bytecode order, i.e., not accounting for + /// nonlinear control flow). This is useful in cases where we need + /// to e.g. record the return-address of a callsite for debuginfo. + pub(crate) next_srcloc: ir::SourceLoc, } impl<'module_environment> FuncEnvironment<'module_environment> { @@ -264,7 +270,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> { heaps: PrimaryMap::default(), vmctx: None, vm_store_context: None, - pcc_vmctx_memtype: None, builtin_functions, offsets: VMOffsets::new(compiler.isa().pointer_bytes(), &translation.module), tunables, @@ -284,6 +289,8 @@ impl<'module_environment> FuncEnvironment<'module_environment> { stack_switching_values_buffer: None, state_slot: None, + next_srcloc: ir::SourceLoc::default(), + wasm_module_offset: translation.wasm_module_offset, } } @@ -294,24 +301,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> { pub(crate) fn vmctx(&mut self, func: &mut Function) -> ir::GlobalValue { self.vmctx.unwrap_or_else(|| { let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); - if self.isa.flags().enable_pcc() { - // Create a placeholder memtype for the vmctx; we'll - // add fields to it as we lazily create HeapData - // structs and global values. - let vmctx_memtype = func.create_memory_type(ir::MemoryTypeData::Struct { - size: 0, - fields: vec![], - }); - - self.pcc_vmctx_memtype = Some(vmctx_memtype); - func.global_value_facts[vmctx] = Some(Fact::Mem { - ty: vmctx_memtype, - min_offset: 0, - max_offset: 0, - nullable: false, - }); - } - self.vmctx = Some(vmctx); vmctx }) @@ -940,97 +929,32 @@ impl<'module_environment> FuncEnvironment<'module_environment> { .copied() } - /// Proof-carrying code: create a memtype describing an empty - /// runtime struct (to be updated later). - fn create_empty_struct_memtype(&self, func: &mut ir::Function) -> ir::MemoryType { - func.create_memory_type(ir::MemoryTypeData::Struct { - size: 0, - fields: vec![], - }) - } - - /// Proof-carrying code: add a new field to a memtype used to - /// describe a runtime struct. A memory region of type `memtype` - /// will have a pointer at `offset` pointing to another memory - /// region of type `pointee`. `readonly` indicates whether the - /// PCC-checked code is expected to update this field or not. - fn add_field_to_memtype( - &self, - func: &mut ir::Function, - memtype: ir::MemoryType, - offset: u32, - pointee: ir::MemoryType, - readonly: bool, - ) { - let ptr_size = self.pointer_type().bytes(); - match &mut func.memory_types[memtype] { - ir::MemoryTypeData::Struct { size, fields } => { - *size = std::cmp::max(*size, offset.checked_add(ptr_size).unwrap().into()); - fields.push(ir::MemoryTypeField { - ty: self.pointer_type(), - offset: offset.into(), - readonly, - fact: Some(ir::Fact::Mem { - ty: pointee, - min_offset: 0, - max_offset: 0, - nullable: false, - }), - }); - - // Sort fields by offset -- we need to do this now - // because we may create an arbitrary number of - // memtypes for imported memories and we don't - // otherwise track them. - fields.sort_by_key(|f| f.offset); - } - _ => panic!("Cannot add field to non-struct memtype"), - } - } - - /// Create an `ir::Global` that does `load(ptr + offset)` and, when PCC and - /// memory types are enabled, adds a field to the pointer's memory type for - /// this value we are loading. - pub(crate) fn global_load_with_memory_type( + /// Create an `ir::Global` that does `load(ptr + offset)`. + pub(crate) fn global_load( &mut self, func: &mut ir::Function, ptr: ir::GlobalValue, offset: u32, flags: ir::MemFlags, - ptr_mem_ty: Option, - ) -> (ir::GlobalValue, Option) { - let pointee = func.create_global_value(ir::GlobalValueData::Load { + ) -> ir::GlobalValue { + func.create_global_value(ir::GlobalValueData::Load { base: ptr, offset: Offset32::new(i32::try_from(offset).unwrap()), global_type: self.pointer_type(), flags, - }); - - let pointee_mem_ty = ptr_mem_ty.map(|ptr_mem_ty| { - let pointee_mem_ty = self.create_empty_struct_memtype(func); - self.add_field_to_memtype(func, ptr_mem_ty, offset, pointee_mem_ty, flags.readonly()); - func.global_value_facts[pointee] = Some(Fact::Mem { - ty: pointee_mem_ty, - min_offset: 0, - max_offset: 0, - nullable: false, - }); - pointee_mem_ty - }); - - (pointee, pointee_mem_ty) + }) } - /// Like `global_load_with_memory_type` but specialized for loads out of the + /// Like `global_load` but specialized for loads out of the /// `vmctx`. - pub(crate) fn global_load_from_vmctx_with_memory_type( + pub(crate) fn global_load_from_vmctx( &mut self, func: &mut ir::Function, offset: u32, flags: ir::MemFlags, - ) -> (ir::GlobalValue, Option) { + ) -> ir::GlobalValue { let vmctx = self.vmctx(func); - self.global_load_with_memory_type(func, vmctx, offset, flags, self.pcc_vmctx_memtype) + self.global_load(func, vmctx, offset, flags) } /// Helper used when `!self.clif_instruction_traps_enabled()` is enabled to @@ -1311,10 +1235,18 @@ impl<'module_environment> FuncEnvironment<'module_environment> { .last() .map(|s| s.raw()) .unwrap_or(u32::MAX); - let pc = srcloc.bits(); + // Convert component-relative srcloc to module-relative + // Wasm PC for the frame table. The srcloc on the builder + // remains component-relative for native DWARF and other + // purposes, but the frame table must be module-relative + // because the guest-debug API presents a purely core-Wasm + // view of the world where components are deconstructed + // into core Wasm modules. + let component_pc = ComponentPC::new(srcloc.bits()); + let module_pc = component_pc.to_module_pc(self.wasm_module_offset); vec![ ir::DebugTag::StackSlot(*slot), - ir::DebugTag::User(pc), + ir::DebugTag::User(module_pc.raw()), ir::DebugTag::User(stack_shape), ] } else { @@ -1364,6 +1296,17 @@ impl<'module_environment> FuncEnvironment<'module_environment> { builder.ins().stack_store(vmctx, slot, 0); } } + + pub(crate) fn val_ty_needs_stack_map(&self, ty: WasmValType) -> bool { + match ty { + WasmValType::Ref(r) => self.heap_ty_needs_stack_map(r.heap_type), + _ => false, + } + } + + pub(crate) fn heap_ty_needs_stack_map(&self, ty: WasmHeapType) -> bool { + ty.is_vmgcref_type_and_not_i31() && !ty.is_bottom() + } } impl TranslateTrap for FuncEnvironment<'_> { @@ -1384,6 +1327,10 @@ impl TranslateTrap for FuncEnvironment<'_> { ) -> ir::FuncRef { self.builtin_functions.load_builtin(builder.func, index) } + + fn debug_tags(&self, srcloc: ir::SourceLoc) -> Vec { + FuncEnvironment::debug_tags(self, srcloc) + } } #[derive(Default)] @@ -1564,7 +1511,7 @@ impl FuncEnvironment<'_> { let memory = self.module.memories[index]; let is_shared = memory.shared; - let (base_ptr, base_offset, current_length_offset, ptr_memtype) = { + let (base_ptr, base_offset, current_length_offset) = { let vmctx = self.vmctx(func); if let Some(def_index) = self.module.defined_memory_index(index) { if is_shared { @@ -1573,7 +1520,7 @@ impl FuncEnvironment<'_> { // VMMemoryDefinition` to it and dereference that when // atomically growing it. let from_offset = self.offsets.vmctx_vmmemory_pointer(def_index); - let (memory, def_mt) = self.global_load_from_vmctx_with_memory_type( + let memory = self.global_load_from_vmctx( func, from_offset, ir::MemFlags::trusted().with_readonly().with_can_move(), @@ -1581,7 +1528,7 @@ impl FuncEnvironment<'_> { let base_offset = i32::from(self.offsets.ptr.vmmemory_definition_base()); let current_length_offset = i32::from(self.offsets.ptr.vmmemory_definition_current_length()); - (memory, base_offset, current_length_offset, def_mt) + (memory, base_offset, current_length_offset) } else { let owned_index = self.module.owned_memory_index(def_index); let owned_base_offset = @@ -1591,16 +1538,11 @@ impl FuncEnvironment<'_> { .vmctx_vmmemory_definition_current_length(owned_index); let current_base_offset = i32::try_from(owned_base_offset).unwrap(); let current_length_offset = i32::try_from(owned_length_offset).unwrap(); - ( - vmctx, - current_base_offset, - current_length_offset, - self.pcc_vmctx_memtype, - ) + (vmctx, current_base_offset, current_length_offset) } } else { let from_offset = self.offsets.vmctx_vmmemory_import_from(index); - let (memory, def_mt) = self.global_load_from_vmctx_with_memory_type( + let memory = self.global_load_from_vmctx( func, from_offset, ir::MemFlags::trusted().with_readonly().with_can_move(), @@ -1608,7 +1550,7 @@ impl FuncEnvironment<'_> { let base_offset = i32::from(self.offsets.ptr.vmmemory_definition_base()); let current_length_offset = i32::from(self.offsets.ptr.vmmemory_definition_current_length()); - (memory, base_offset, current_length_offset, def_mt) + (memory, base_offset, current_length_offset) } }; @@ -1619,21 +1561,11 @@ impl FuncEnvironment<'_> { flags: MemFlags::trusted(), }); - let (base_fact, pcc_memory_type) = self.make_pcc_base_fact_and_type_for_memory( - func, - memory, - base_offset, - current_length_offset, - ptr_memtype, - bound, - ); - - let base = self.make_heap_base(func, memory, base_ptr, base_offset, base_fact); + let base = self.make_heap_base(func, memory, base_ptr, base_offset); self.heaps.push(HeapData { base, bound, - pcc_memory_type, memory, }) } @@ -1644,11 +1576,10 @@ impl FuncEnvironment<'_> { memory: Memory, ptr: ir::GlobalValue, offset: i32, - fact: Option, ) -> ir::GlobalValue { let pointer_type = self.pointer_type(); - let mut flags = ir::MemFlags::trusted().with_checked().with_can_move(); + let mut flags = ir::MemFlags::trusted().with_can_move(); if !memory.memory_may_move(self.tunables) { flags.set_readonly(); } @@ -1659,128 +1590,9 @@ impl FuncEnvironment<'_> { global_type: pointer_type, flags, }); - func.global_value_facts[heap_base] = fact; heap_base } - pub(crate) fn make_pcc_base_fact_and_type_for_memory( - &mut self, - func: &mut Function, - memory: Memory, - base_offset: i32, - current_length_offset: i32, - ptr_memtype: Option, - heap_bound: ir::GlobalValue, - ) -> (Option, Option) { - // If we have a declared maximum, we can make this a "static" heap, which is - // allocated up front and never moved. - let host_page_size_log2 = self.target_config().page_size_align_log2; - let (base_fact, memory_type) = if !memory - .can_elide_bounds_check(self.tunables, host_page_size_log2) - { - if let Some(ptr_memtype) = ptr_memtype { - // Create a memtype representing the untyped memory region. - let data_mt = func.create_memory_type(ir::MemoryTypeData::DynamicMemory { - gv: heap_bound, - size: self.tunables.memory_guard_size, - }); - // This fact applies to any pointer to the start of the memory. - let base_fact = ir::Fact::dynamic_base_ptr(data_mt); - // This fact applies to the length. - let length_fact = ir::Fact::global_value( - u16::try_from(self.isa.pointer_type().bits()).unwrap(), - heap_bound, - ); - // Create a field in the vmctx for the base pointer. - match &mut func.memory_types[ptr_memtype] { - ir::MemoryTypeData::Struct { size, fields } => { - let base_offset = u64::try_from(base_offset).unwrap(); - fields.push(ir::MemoryTypeField { - offset: base_offset, - ty: self.isa.pointer_type(), - // Read-only field from the PoV of PCC checks: - // don't allow stores to this field. (Even if - // it is a dynamic memory whose base can - // change, that update happens inside the - // runtime, not in generated code.) - readonly: true, - fact: Some(base_fact.clone()), - }); - let current_length_offset = u64::try_from(current_length_offset).unwrap(); - fields.push(ir::MemoryTypeField { - offset: current_length_offset, - ty: self.isa.pointer_type(), - // As above, read-only; only the runtime modifies it. - readonly: true, - fact: Some(length_fact), - }); - - let pointer_size = u64::from(self.isa.pointer_type().bytes()); - let fields_end = std::cmp::max( - base_offset + pointer_size, - current_length_offset + pointer_size, - ); - *size = std::cmp::max(*size, fields_end); - } - _ => { - panic!("Bad memtype"); - } - } - // Apply a fact to the base pointer. - (Some(base_fact), Some(data_mt)) - } else { - (None, None) - } - } else { - if let Some(ptr_memtype) = ptr_memtype { - // Create a memtype representing the untyped memory region. - let data_mt = func.create_memory_type(ir::MemoryTypeData::Memory { - size: self - .tunables - .memory_reservation - .checked_add(self.tunables.memory_guard_size) - .expect("Memory plan has overflowing size plus guard"), - }); - // This fact applies to any pointer to the start of the memory. - let base_fact = Fact::Mem { - ty: data_mt, - min_offset: 0, - max_offset: 0, - nullable: false, - }; - // Create a field in the vmctx for the base pointer. - match &mut func.memory_types[ptr_memtype] { - ir::MemoryTypeData::Struct { size, fields } => { - let offset = u64::try_from(base_offset).unwrap(); - fields.push(ir::MemoryTypeField { - offset, - ty: self.isa.pointer_type(), - // Read-only field from the PoV of PCC checks: - // don't allow stores to this field. (Even if - // it is a dynamic memory whose base can - // change, that update happens inside the - // runtime, not in generated code.) - readonly: true, - fact: Some(base_fact.clone()), - }); - *size = std::cmp::max( - *size, - offset + u64::from(self.isa.pointer_type().bytes()), - ); - } - _ => { - panic!("Bad memtype"); - } - } - // Apply a fact to the base pointer. - (Some(base_fact), Some(data_mt)) - } else { - (None, None) - } - }; - (base_fact, memory_type) - } - fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> TableData { let pointer_type = self.pointer_type(); @@ -2541,13 +2353,7 @@ impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environm fn reference_type(&self, wasm_ty: WasmHeapType) -> (ir::Type, bool) { let ty = crate::reference_type(wasm_ty, self.pointer_type()); - let needs_stack_map = match wasm_ty.top() { - WasmHeapTopType::Extern | WasmHeapTopType::Any | WasmHeapTopType::Exn => true, - WasmHeapTopType::Func => false, - // TODO(#10248) Once continuations can be stored on the GC heap, we - // will need stack maps for continuation objects. - WasmHeapTopType::Cont => false, - }; + let needs_stack_map = self.heap_ty_needs_stack_map(wasm_ty); (ty, needs_stack_map) } @@ -2555,10 +2361,6 @@ impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environm self.isa.flags().enable_heap_access_spectre_mitigation() } - fn proof_carrying_code(&self) -> bool { - self.isa.flags().enable_pcc() - } - fn tunables(&self) -> &Tunables { self.compiler.tunables() } @@ -2940,7 +2742,9 @@ impl FuncEnvironment<'_> { libcall, &[vmctx, interned_type_index, data_index, data_offset, len], ); - Ok(builder.func.dfg.first_result(call_inst)) + let array_ref = builder.func.dfg.first_result(call_inst); + builder.declare_value_needs_stack_map(array_ref); + Ok(array_ref) } pub fn translate_array_new_elem( @@ -2962,7 +2766,9 @@ impl FuncEnvironment<'_> { libcall, &[vmctx, interned_type_index, elem_index, elem_offset, len], ); - Ok(builder.func.dfg.first_result(call_inst)) + let array_ref = builder.func.dfg.first_result(call_inst); + builder.declare_value_needs_stack_map(array_ref); + Ok(array_ref) } pub fn translate_array_copy( diff --git a/crates/cranelift/src/func_environ/gc/enabled.rs b/crates/cranelift/src/func_environ/gc/enabled.rs index 06b44b513281..45f52d67695f 100644 --- a/crates/cranelift/src/func_environ/gc/enabled.rs +++ b/crates/cranelift/src/func_environ/gc/enabled.rs @@ -94,8 +94,55 @@ fn unbarriered_store_gc_ref( Ok(()) } -/// Emit code to read a struct field or array element from its raw address in -/// the GC heap. +/// Emit inline CLIF code that asserts an object's `VMGcKind` matches the +/// expected kind. Only emits code when `cfg(gc_zeal)` is enabled. +/// +/// `gc_ref` must be a non-null, non-i31 GC reference (i32 heap index). +fn emit_gc_kind_assert( + func_env: &mut FuncEnvironment<'_>, + builder: &mut FunctionBuilder<'_>, + gc_ref: ir::Value, + expected_kind: VMGcKind, +) { + if !cfg!(gc_zeal) { + return; + } + + func_env.trapz(builder, gc_ref, crate::TRAP_NULL_REFERENCE); + + let kind_addr = func_env.prepare_gc_ref_access( + builder, + gc_ref, + BoundsCheck::StaticObjectField { + offset: wasmtime_environ::VM_GC_HEADER_KIND_OFFSET, + access_size: wasmtime_environ::VM_GC_KIND_SIZE, + object_size: wasmtime_environ::VM_GC_HEADER_SIZE, + }, + ); + let kind_and_reserved_bits = builder.ins().load( + ir::types::I32, + ir::MemFlags::trusted().with_readonly(), + kind_addr, + 0, + ); + let kind_mask = builder + .ins() + .iconst(ir::types::I32, i64::from(VMGcKind::MASK)); + let actual_kind = builder.ins().band(kind_and_reserved_bits, kind_mask); + + let expected_kind = builder + .ins() + .iconst(ir::types::I32, i64::from(expected_kind.as_u32())); + + // NB: Do a subtype check rather than a strict equality check. See + // `VMGcKind::matches` for details. + let and = builder.ins().band(actual_kind, expected_kind); + let matches = builder.ins().icmp(IntCC::Equal, and, expected_kind); + + builder.ins().trapz(matches, TRAP_INTERNAL_ASSERT); +} + +/// Read a struct field or array element from its raw address in the GC heap. /// /// The given address MUST have already been bounds-checked via /// `prepare_gc_ref_access`. @@ -331,6 +378,8 @@ pub fn translate_struct_get( // type info from `wasmparser` and through to here is a bit funky. func_env.trapz(builder, struct_ref, crate::TRAP_NULL_REFERENCE); + emit_gc_kind_assert(func_env, builder, struct_ref, VMGcKind::StructRef); + let field_index = usize::try_from(field_index).unwrap(); let interned_type_index = func_env.module.types[struct_type_index].unwrap_module_type_index(); @@ -378,6 +427,8 @@ pub fn translate_struct_set( // TODO: See comment in `translate_struct_get` about the `trapz`. func_env.trapz(builder, struct_ref, crate::TRAP_NULL_REFERENCE); + emit_gc_kind_assert(func_env, builder, struct_ref, VMGcKind::StructRef); + let field_index = usize::try_from(field_index).unwrap(); let interned_type_index = func_env.module.types[struct_type_index].unwrap_module_type_index(); @@ -979,6 +1030,8 @@ pub fn translate_array_get( ) -> WasmResult { log::trace!("translate_array_get({array_type_index:?}, {array_ref:?}, {index:?})"); + emit_gc_kind_assert(func_env, builder, array_ref, VMGcKind::ArrayRef); + let array_type_index = func_env.module.types[array_type_index].unwrap_module_type_index(); let elem_addr = array_elem_addr(func_env, builder, array_type_index, array_ref, index); @@ -1000,6 +1053,8 @@ pub fn translate_array_set( ) -> WasmResult<()> { log::trace!("translate_array_set({array_type_index:?}, {array_ref:?}, {index:?}, {value:?})"); + emit_gc_kind_assert(func_env, builder, array_ref, VMGcKind::ArrayRef); + let array_type_index = func_env.module.types[array_type_index].unwrap_module_type_index(); let elem_addr = array_elem_addr(func_env, builder, array_type_index, array_ref, index); @@ -1456,7 +1511,6 @@ impl FuncEnvironment<'_> { let heap = self.heaps.push(HeapData { base, bound, - pcc_memory_type: None, memory, }); self.gc_heap = Some(heap); diff --git a/crates/cranelift/src/func_environ/gc/enabled/drc.rs b/crates/cranelift/src/func_environ/gc/enabled/drc.rs index cbda4ebb5fd7..037e09d55146 100644 --- a/crates/cranelift/src/func_environ/gc/enabled/drc.rs +++ b/crates/cranelift/src/func_environ/gc/enabled/drc.rs @@ -261,8 +261,7 @@ impl DrcCompiler { new_val: ir::Value, flags: ir::MemFlags, ) -> WasmResult<()> { - let (ref_ty, needs_stack_map) = func_env.reference_type(ty.heap_type); - debug_assert!(needs_stack_map); + let (ref_ty, _) = func_env.reference_type(ty.heap_type); // Special case for references to uninhabited bottom types: see // `translate_write_gc_reference` for details. @@ -585,8 +584,7 @@ impl GcCompiler for DrcCompiler { assert!(ty.is_vmgcref_type()); - let (reference_type, needs_stack_map) = func_env.reference_type(ty.heap_type); - debug_assert!(needs_stack_map); + let (reference_type, _) = func_env.reference_type(ty.heap_type); // Special case for references to uninhabited bottom types: the // reference must either be nullable and we can just eagerly return @@ -737,8 +735,7 @@ impl GcCompiler for DrcCompiler { ) -> WasmResult<()> { assert!(ty.is_vmgcref_type()); - let (ref_ty, needs_stack_map) = func_env.reference_type(ty.heap_type); - debug_assert!(needs_stack_map); + let (ref_ty, _) = func_env.reference_type(ty.heap_type); // Special case for references to uninhabited bottom types: either the // reference is nullable and we can just eagerly store null into `dst` diff --git a/crates/cranelift/src/func_environ/stack_switching/instructions.rs b/crates/cranelift/src/func_environ/stack_switching/instructions.rs index c93f6957628f..9579dd3583aa 100644 --- a/crates/cranelift/src/func_environ/stack_switching/instructions.rs +++ b/crates/cranelift/src/func_environ/stack_switching/instructions.rs @@ -1270,7 +1270,7 @@ pub(crate) fn translate_resume<'a>( // user-defined handler blocks, based on the handler index // provided on suspension. Note that we do not jump to the // handler blocks directly. Instead, each handler block has a - // corresponding premable block, which we jump to in order to + // corresponding preamble block, which we jump to in order to // reach a particular handler block. The preamble block prepares // the arguments and continuation object to be passed to the // actual handler block. @@ -1546,7 +1546,7 @@ pub(crate) fn translate_resume<'a>( preamble_blocks }; - // Dispatch block. All it does is jump to the right premable block based on + // Dispatch block. All it does is jump to the right preamble block based on // the handler index. { builder.switch_to_block(dispatch_block); diff --git a/crates/cranelift/src/translate/code_translator.rs b/crates/cranelift/src/translate/code_translator.rs index 79d8edf56cb4..3bf231f7b8aa 100644 --- a/crates/cranelift/src/translate/code_translator.rs +++ b/crates/cranelift/src/translate/code_translator.rs @@ -203,31 +203,47 @@ pub fn translate_operator( Operator::Drop => { environ.stacks.pop1(); } - Operator::Select => { - let (mut arg1, mut arg2, cond) = environ.stacks.pop3(); - if builder.func.dfg.value_type(arg1).is_vector() { - arg1 = optionally_bitcast_vector(arg1, I8X16, builder); - } - if builder.func.dfg.value_type(arg2).is_vector() { - arg2 = optionally_bitcast_vector(arg2, I8X16, builder); - } - environ.stacks.push1(builder.ins().select(cond, arg1, arg2)); + Operator::Nop => { + // We do nothing } - Operator::TypedSelect { ty: _ } => { + Operator::Select + | Operator::TypedSelect { // We ignore the explicit type parameter as it is only needed for // validation, which we require to have been performed before // translation. + ty: _, + } => { let (mut arg1, mut arg2, cond) = environ.stacks.pop3(); + if builder.func.dfg.value_type(arg1).is_vector() { arg1 = optionally_bitcast_vector(arg1, I8X16, builder); } if builder.func.dfg.value_type(arg2).is_vector() { arg2 = optionally_bitcast_vector(arg2, I8X16, builder); } - environ.stacks.push1(builder.ins().select(cond, arg1, arg2)); - } - Operator::Nop => { - // We do nothing + + let val = builder.ins().select(cond, arg1, arg2); + + // If either of the input types need inclusion in stack maps, then + // the result will as well. + // + // Note that we don't need to check whether the result's type needs + // inclusion in stack maps (that would be a conservative over + // approximation) because the input types give us more-precise + // information than the result type does. For example, the result + // does not need inclusion in stack maps in the scenario where both + // inputs are `i31ref`s and the result is an `anyref`. Even though + // `anyref`s normally do require inclusion in stack maps, in this + // particular case, we know that we are dealing with an `anyref` + // that doesn't actually require inclusion. + if operand_types + .iter() + .any(|ty| environ.val_ty_needs_stack_map(*ty)) + { + builder.declare_value_needs_stack_map(val); + } + + environ.stacks.push1(val); } Operator::Unreachable => { environ.trap(builder, crate::TRAP_UNREACHABLE); @@ -664,8 +680,13 @@ pub fn translate_operator( let mut args = environ.stacks.peekn(num_args).to_vec(); bitcast_wasm_params(environ, sig_ref, &mut args, builder); - let inst_results = - environ.translate_call(builder, srcloc, function_index, sig_ref, &args)?; + let inst_results = environ.translate_call( + builder, + environ.next_srcloc, + function_index, + sig_ref, + &args, + )?; debug_assert_eq!( inst_results.len(), @@ -693,7 +714,7 @@ pub fn translate_operator( let inst_results = environ.translate_call_indirect( builder, - srcloc, + environ.next_srcloc, validator.features(), TableIndex::from_u32(*table_index), type_index, @@ -2632,7 +2653,7 @@ pub fn translate_operator( bitcast_wasm_params(environ, sigref, &mut args, builder); let inst_results = - environ.translate_call_ref(builder, srcloc, sigref, callee, &args)?; + environ.translate_call_ref(builder, environ.next_srcloc, sigref, callee, &args)?; debug_assert_eq!( inst_results.len(), @@ -3615,11 +3636,6 @@ fn prepare_addr( let mut flags = MemFlags::new(); flags.set_endianness(ir::Endianness::Little); - if heap.pcc_memory_type.is_some() { - // Proof-carrying code is enabled; check this memory access. - flags.set_checked(); - } - // The access occurs to the `heap` disjoint category of abstract // state. This may allow alias analysis to merge redundant loads, // etc. when heap accesses occur interleaved with other (table, @@ -3646,8 +3662,7 @@ fn align_atomic_addr( // alignment check itself. This can probably be optimized better and we // should do so in the future as well. if loaded_bytes > 1 { - let addr = environ.stacks.pop1(); // "peek" via pop then push - environ.stacks.push1(addr); + let addr = environ.stacks.peek1(); let effective_addr = if memarg.offset == 0 { addr } else { diff --git a/crates/cranelift/src/translate/environ/spec.rs b/crates/cranelift/src/translate/environ/spec.rs index 68806c1524fd..bad8dc1d8a65 100644 --- a/crates/cranelift/src/translate/environ/spec.rs +++ b/crates/cranelift/src/translate/environ/spec.rs @@ -43,9 +43,6 @@ pub trait TargetEnvironment: TypeConvert { /// Whether to enable Spectre mitigations for heap accesses. fn heap_access_spectre_mitigation(&self) -> bool; - /// Whether to add proof-carrying-code facts to verify memory accesses. - fn proof_carrying_code(&self) -> bool; - /// Get the Cranelift reference type to use for the given Wasm reference /// type. /// diff --git a/crates/cranelift/src/translate/func_translator.rs b/crates/cranelift/src/translate/func_translator.rs index bda7dd4debe5..b6875745504f 100644 --- a/crates/cranelift/src/translate/func_translator.rs +++ b/crates/cranelift/src/translate/func_translator.rs @@ -257,6 +257,7 @@ fn parse_function_body( builder.set_srcloc(cur_srcloc(&reader.get_binary_reader())); let op = reader.read()?; + environ.next_srcloc = cur_srcloc(&reader.get_binary_reader()); let operand_types = validate_op_and_get_operand_types(validator, environ, &mut operand_types, &op, pos)?; diff --git a/crates/cranelift/src/translate/heap.rs b/crates/cranelift/src/translate/heap.rs index cdedff9d2dd3..32992dd19171 100644 --- a/crates/cranelift/src/translate/heap.rs +++ b/crates/cranelift/src/translate/heap.rs @@ -1,6 +1,6 @@ //! Heaps to implement WebAssembly linear memories. -use cranelift_codegen::ir::{self, GlobalValue, MemoryType, Type}; +use cranelift_codegen::ir::{self, GlobalValue, Type}; use cranelift_entity::entity_impl; use wasmtime_environ::{IndexType, Memory}; @@ -45,9 +45,6 @@ pub struct HeapData { /// The type of wasm memory that this heap is operating on. pub memory: Memory, - - /// The memory type for the pointed-to memory, if using proof-carrying code. - pub pcc_memory_type: Option, } impl HeapData { diff --git a/crates/cranelift/src/trap.rs b/crates/cranelift/src/trap.rs index 811835c654c6..3b72f0282ab1 100644 --- a/crates/cranelift/src/trap.rs +++ b/crates/cranelift/src/trap.rs @@ -20,6 +20,9 @@ pub trait TranslateTrap { builder: &mut FunctionBuilder<'_>, index: BuiltinFunctionIndex, ) -> ir::FuncRef; + fn debug_tags(&self, _srcloc: ir::SourceLoc) -> Vec { + vec![] + } fn trap(&mut self, builder: &mut FunctionBuilder, trap: ir::TrapCode) { match ( @@ -36,12 +39,14 @@ pub trait TranslateTrap { // pass in our trap code. Leave a debug `unreachable` in place // afterwards as a defense-in-depth measure. (false, Some(trap)) => { + let debug_tags = self.debug_tags(builder.srcloc()); let trap_libcall = self.builtin_funcref(builder, BuiltinFunctionIndex::trap()); let vmctx = self.vmctx_val(&mut builder.cursor()); let trap_code = builder.ins().iconst(I8, i64::from(trap as u8)); builder.ins().call(trap_libcall, &[vmctx, trap_code]); let raise_libcall = self.builtin_funcref(builder, BuiltinFunctionIndex::raise()); - builder.ins().call(raise_libcall, &[vmctx]); + let inst = builder.ins().call(raise_libcall, &[vmctx]); + builder.func.debug_tags.set(inst, debug_tags); builder.ins().trap(TRAP_INTERNAL_ASSERT); } } diff --git a/crates/debugger/Cargo.toml b/crates/debugger/Cargo.toml index 466b24c82b94..9aeb815d969a 100644 --- a/crates/debugger/Cargo.toml +++ b/crates/debugger/Cargo.toml @@ -12,9 +12,12 @@ rust-version.workspace = true workspace = true [dependencies] -wasmtime = { workspace = true, features = ["debug", "std", "async"] } +wasmtime = { workspace = true, features = ["debug", "std", "async", "component-model", "gc", "gc-drc"] } +wasmtime-wasi = { workspace = true } +wasmtime-wasi-io = { workspace = true } tokio = { workspace = true, features = ["rt", "sync", "macros"] } log = { workspace = true } +async-trait = { workspace = true } [dev-dependencies] # Depend on `wasmtime` again to get `cranelift` and `wat` so we can diff --git a/crates/debugger/src/host.rs b/crates/debugger/src/host.rs new file mode 100644 index 000000000000..df6741d198e1 --- /dev/null +++ b/crates/debugger/src/host.rs @@ -0,0 +1,49 @@ +//! Host implementation for the debugger world. + +use wasmtime::{ + Result, + component::{Resource, ResourceTable}, +}; + +mod api; +mod bindings; +mod opaque; + +pub use api::Debuggee; +pub use bindings::DebugMain as DebuggerComponent; +pub use bindings::bytecodealliance::wasmtime::debuggee as wit; +use opaque::OpaqueDebugger; + +/// Register a debuggee in a resource table. +pub fn add_debuggee( + table: &mut ResourceTable, + debuggee: crate::Debuggee, +) -> Result> { + let engine = debuggee.engine().clone(); + let interrupt_pending = debuggee.interrupt_pending().clone(); + let inner: Option> = Some(Box::new(debuggee)); + Ok(table.push(Debuggee { + inner, + engine, + interrupt_pending, + })?) +} + +impl bindings::DebugMainImports for ResourceTable { + async fn print_debugger_info(&mut self, message: String) -> wasmtime::Result<()> { + eprintln!("Debugger: {message}"); + Ok(()) + } +} + +/// Add the debugger world's host functions to a [`wasmtime::component::Linker`]. +pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + f: fn(&mut T) -> &mut ResourceTable, +) -> wasmtime::Result<()> { + wit::add_to_linker::<_, wasmtime::component::HasSelf>(linker, f)?; + bindings::DebugMain::add_to_linker_imports::<_, wasmtime::component::HasSelf>( + linker, f, + )?; + Ok(()) +} diff --git a/crates/debugger/src/host/api.rs b/crates/debugger/src/host/api.rs new file mode 100644 index 000000000000..e3a6280c2120 --- /dev/null +++ b/crates/debugger/src/host/api.rs @@ -0,0 +1,1055 @@ +//! Implementations of the host API traits. + +use crate::host::opaque::OpaqueDebugger; +use crate::host::wit; +use crate::{DebugRunResult, host::bindings::wasm_type_to_val_type}; +use std::pin::Pin; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use wasmtime::{ + Engine, ExnRef, FrameHandle, Func, Global, Instance, Memory, Module, OwnedRooted, Result, + Table, Tag, Val, component::Resource, component::ResourceTable, +}; +use wasmtime_wasi::p2::{DynPollable, Pollable, subscribe}; + +/// Representation of one debuggee: a store with debugged code inside, +/// under the control of the debugger. +pub struct Debuggee { + /// The type-erased debugger implementation. This field is `Some` + /// when execution is paused, and `None`, with ownership of the + /// debugger (hence debuggee's store) passed to the future when + /// executing. + pub(crate) inner: Option>, + + /// A separate handle to the Engine, allowing incrementing the + /// epoch (hence interrupting a running debuggee) without taking + /// the mutex. + pub(crate) engine: Engine, + + /// Shared flag: set to `true` by `interrupt()` so the inner + /// handler treats the next epoch yield as an `Interrupted` event. + pub(crate) interrupt_pending: Arc, +} + +impl Debuggee { + /// Finish execution of the debuggee before returning. + pub async fn finish(&mut self) -> Result<()> { + if let Some(inner) = self.inner.as_mut() { + inner.finish().await?; + } + Ok(()) + } +} + +impl WasmValue { + pub(crate) fn new(store: impl wasmtime::AsContextMut, val: Val) -> Result { + Ok(match val { + Val::ExnRef(Some(rooted)) => { + WasmValue::Exn(Some(rooted.to_owned_rooted(store).unwrap())) + } + Val::ExnRef(None) => WasmValue::Exn(None), + Val::FuncRef(Some(f)) => WasmValue::Func(Some(f)), + Val::FuncRef(None) => WasmValue::Func(None), + Val::ExternRef(_) | Val::AnyRef(_) | Val::ContRef(_) => { + return Err(wit::Error::UnsupportedType.into()); + } + Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => { + WasmValue::Primitive(val) + } + }) + } + + pub(crate) fn into_val(self, store: impl wasmtime::AsContextMut) -> Val { + match self { + WasmValue::Primitive(v) => v, + WasmValue::Exn(Some(owned)) => Val::ExnRef(Some(owned.to_rooted(store))), + WasmValue::Exn(None) => Val::ExnRef(None), + WasmValue::Func(Some(f)) => Val::FuncRef(Some(f)), + WasmValue::Func(None) => Val::FuncRef(None), + } + } +} + +/// Representation of an async debug event that the debugger is +/// waiting on. +/// +/// Cancel-safety: the non-cancel-safe OpaqueDebugger async methods +/// are called inside an `async move` block that owns the debugger. +/// `ready()` merely polls this stored future, which is always safe to +/// re-poll after cancelation. The debugger is returned in the `Done` +/// state and extracted by `finish()`. +pub struct EventFuture { + state: EventFutureState, +} + +enum EventFutureState { + /// The future is running; owns the debugger. + Running( + Pin< + Box< + dyn Future< + Output = ( + Box, + Result, + ), + > + Send, + >, + >, + ), + /// The future has completed; debugger is ready to be returned. + Done { + inner: Box, + result: Option>, + }, +} + +impl EventFuture { + fn new_single_step( + mut inner: Box, + resumption: wit::ResumptionValue, + ) -> Self { + EventFuture { + state: EventFutureState::Running(Box::pin(async move { + if let Err(e) = inner.handle_resumption(&resumption).await { + return (inner, Err(e)); + } + let result = inner.single_step().await; + (inner, result) + })), + } + } + + fn new_continue( + mut inner: Box, + resumption: wit::ResumptionValue, + ) -> Self { + EventFuture { + state: EventFutureState::Running(Box::pin(async move { + if let Err(e) = inner.handle_resumption(&resumption).await { + return (inner, Err(e)); + } + let result = inner.continue_().await; + (inner, result) + })), + } + } +} + +#[async_trait::async_trait] +impl wasmtime_wasi_io::poll::Pollable for EventFuture { + async fn ready(&mut self) { + match &mut self.state { + EventFutureState::Running(future) => { + let (inner, result) = future.await; + self.state = EventFutureState::Done { + inner, + result: Some(result), + }; + } + EventFutureState::Done { .. } => {} + } + } +} + +/// Representation of a frame within a debuggee. +#[derive(Clone)] +pub struct Frame(FrameHandle); + +/// Representation of a Wasm exception object. +#[derive(Clone)] +pub struct WasmException(OwnedRooted); + +/// Representation of a Wasm value. +/// +/// This is distinct from `wasmtime::Val` because we need the Owned +/// variants of GC references here. +#[derive(Clone)] +pub enum WasmValue { + /// A primitive (non-GC) value. + Primitive(Val), + /// An exception object. + Exn(Option>), + /// A funcref. + Func(Option), + // TODO: GC structs and arrays. +} + +/// Get the `OpaqueDebugger` or raise an error. +fn debugger<'a>( + table: &'a mut ResourceTable, + debuggee: &Resource, +) -> Result<&'a mut dyn OpaqueDebugger> { + let d = table.get_mut(&debuggee)?.inner.as_mut().ok_or_else(|| { + wasmtime::error::format_err!("Attempt to use debuggee API while a future is pending") + })?; + Ok(&mut **d) +} + +impl wit::HostDebuggee for ResourceTable { + async fn all_modules(&mut self, debuggee: Resource) -> Result>> { + let d = debugger(self, &debuggee)?; + let modules = d.all_modules().await?; + let mut resources = vec![]; + for module in modules { + resources.push(self.push_child(module, &debuggee)?); + } + Ok(resources) + } + + async fn all_instances( + &mut self, + debuggee: Resource, + ) -> Result>> { + let d = debugger(self, &debuggee)?; + let instances = d.all_instances().await?; + let mut resources = vec![]; + for instance in instances { + resources.push(self.push_child(instance, &debuggee)?); + } + Ok(resources) + } + + async fn interrupt(&mut self, debuggee: Resource) -> Result<()> { + let d = self.get_mut(&debuggee)?; + d.interrupt_pending.store(true, Ordering::SeqCst); + d.engine.increment_epoch(); + Ok(()) + } + + async fn single_step( + &mut self, + debuggee: Resource, + resumption: wit::ResumptionValue, + ) -> Result> { + let d = self.get_mut(&debuggee).unwrap().inner.take().unwrap(); + Ok(self.push_child(EventFuture::new_single_step(d, resumption), &debuggee)?) + } + + async fn continue_( + &mut self, + debuggee: Resource, + resumption: wit::ResumptionValue, + ) -> Result> { + let d = self.get_mut(&debuggee).unwrap().inner.take().unwrap(); + Ok(self.push_child(EventFuture::new_continue(d, resumption), &debuggee)?) + } + + async fn exit_frames(&mut self, debuggee: Resource) -> Result>> { + let d = debugger(self, &debuggee)?; + let frames = d.exit_frames().await?; + let mut result = vec![]; + for frame in frames { + result.push(self.push_child(Frame(frame), &debuggee)?); + } + Ok(result) + } + + async fn drop(&mut self, debuggee: Resource) -> Result<()> { + self.delete(debuggee)?; + Ok(()) + } +} + +fn result_to_event(table: &mut ResourceTable, value: DebugRunResult) -> Result { + Ok(match value { + DebugRunResult::Finished => wit::Event::Complete, + DebugRunResult::HostcallError => wit::Event::Trap, + DebugRunResult::Trap(_t) => wit::Event::Trap, + DebugRunResult::Breakpoint => wit::Event::Breakpoint, + DebugRunResult::EpochYield => wit::Event::Interrupted, + DebugRunResult::CaughtExceptionThrown(e) => { + let e = table.push(WasmException(e))?; + wit::Event::CaughtExceptionThrown(e) + } + DebugRunResult::UncaughtExceptionThrown(e) => { + let e = table.push(WasmException(e))?; + wit::Event::UncaughtExceptionThrown(e) + } + }) +} + +impl wit::HostEventFuture for ResourceTable { + async fn finish( + &mut self, + self_: Resource, + debuggee: Resource, + ) -> Result { + let mut f = self.delete(self_)?; + f.ready().await; + match f.state { + EventFutureState::Running(..) => { + unreachable!("ready() cannot return until setting Done state") + } + EventFutureState::Done { inner, result } => { + self.get_mut(&debuggee)?.inner = Some(inner); + match result.unwrap() { + Ok(result) => Ok(result_to_event(self, result)?), + Err(e) => Err(e), + } + } + } + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } + + async fn subscribe(&mut self, self_: Resource) -> Result> { + subscribe(self, self_) + } +} + +impl wit::HostInstance for ResourceTable { + async fn get_module( + &mut self, + self_: Resource, + d: Resource, + ) -> Result> { + let i = *self.get(&self_)?; + let d = debugger(self, &d)?; + let module = d.get_instance_module(i).await?; + let module = self.push(module)?; + Ok(module) + } + + async fn get_memory( + &mut self, + self_: Resource, + d: Resource, + memory_index: u32, + ) -> Result> { + let instance = *self.get(&self_)?; + let d = debugger(self, &d)?; + let memory = d + .instance_get_memory(instance, memory_index) + .await? + .ok_or(wit::Error::InvalidEntity)?; + Ok(self.push(memory)?) + } + + async fn get_global( + &mut self, + self_: Resource, + d: Resource, + global_index: u32, + ) -> Result> { + let instance = *self.get(&self_)?; + let d = debugger(self, &d)?; + let global = d + .instance_get_global(instance, global_index) + .await? + .ok_or(wit::Error::InvalidEntity)?; + Ok(self.push(global)?) + } + + async fn get_table( + &mut self, + self_: Resource, + d: Resource, + table_index: u32, + ) -> Result> { + let instance = *self.get(&self_)?; + let d = debugger(self, &d)?; + let table = d + .instance_get_table(instance, table_index) + .await? + .ok_or(wit::Error::InvalidEntity)?; + Ok(self.push(table)?) + } + + async fn get_func( + &mut self, + self_: Resource, + d: Resource, + func_index: u32, + ) -> Result> { + let instance = *self.get(&self_)?; + let d = debugger(self, &d)?; + let func = d + .instance_get_func(instance, func_index) + .await? + .ok_or(wit::Error::InvalidEntity)?; + Ok(self.push(func)?) + } + + async fn get_tag( + &mut self, + self_: Resource, + d: Resource, + tag_index: u32, + ) -> Result> { + let instance = *self.get(&self_)?; + let d = debugger(self, &d)?; + let tag = d + .instance_get_tag(instance, tag_index) + .await? + .ok_or(wit::Error::InvalidEntity)?; + Ok(self.push(tag)?) + } + + async fn clone(&mut self, self_: Resource) -> Result> { + let instance = *self.get(&self_)?; + Ok(self.push(instance)?) + } + + async fn unique_id(&mut self, self_: Resource) -> Result { + let instance = self.get(&self_)?; + Ok(u64::from(instance.debug_index_in_store())) + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::HostModule for ResourceTable { + async fn add_breakpoint( + &mut self, + self_: Resource, + d: Resource, + pc: u32, + ) -> Result<()> { + let module = self.get(&self_)?.clone(); + let d = debugger(self, &d)?; + d.module_add_breakpoint(module, pc).await + } + + async fn remove_breakpoint( + &mut self, + self_: Resource, + d: Resource, + pc: u32, + ) -> Result<()> { + let module = self.get(&self_)?.clone(); + let d = debugger(self, &d)?; + d.module_remove_breakpoint(module, pc).await + } + + async fn bytecode(&mut self, self_: Resource) -> Result>> { + let module = self.get(&self_)?; + Ok(module.debug_bytecode().map(|b| b.to_vec())) + } + + async fn clone(&mut self, self_: Resource) -> Result> { + let module = self.get(&self_)?.clone(); + Ok(self.push(module)?) + } + + async fn unique_id(&mut self, self_: Resource) -> Result { + let module = self.get(&self_)?; + Ok(module.debug_index_in_engine()) + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::HostMemory for ResourceTable { + async fn size_bytes(&mut self, self_: Resource, d: Resource) -> Result { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.memory_size_bytes(memory).await + } + + async fn page_size_bytes( + &mut self, + self_: Resource, + d: Resource, + ) -> Result { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.memory_page_size(memory).await + } + + async fn grow_to_bytes( + &mut self, + self_: Resource, + d: Resource, + delta_bytes: u64, + ) -> Result { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.memory_grow(memory, delta_bytes).await + } + + async fn get_bytes( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + len: u64, + ) -> Result> { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + Ok(d.memory_read_bytes(memory, addr, len) + .await? + .ok_or(wit::Error::OutOfBounds)?) + } + + async fn set_bytes( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + bytes: Vec, + ) -> Result<()> { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.memory_write_bytes(memory, addr, bytes) + .await? + .ok_or(wit::Error::OutOfBounds)?; + Ok(()) + } + + async fn get_u8( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + ) -> Result { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + Ok(d.memory_read_u8(memory, addr) + .await? + .ok_or(wit::Error::OutOfBounds)?) + } + + async fn get_u16( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + ) -> Result { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + Ok(d.memory_read_u16(memory, addr) + .await? + .ok_or(wit::Error::OutOfBounds)?) + } + + async fn get_u32( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + ) -> Result { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + Ok(d.memory_read_u32(memory, addr) + .await? + .ok_or(wit::Error::OutOfBounds)?) + } + + async fn get_u64( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + ) -> Result { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + Ok(d.memory_read_u64(memory, addr) + .await? + .ok_or(wit::Error::OutOfBounds)?) + } + + async fn set_u8( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + value: u8, + ) -> Result<()> { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.memory_write_u8(memory, addr, value) + .await? + .ok_or(wit::Error::OutOfBounds)?; + Ok(()) + } + + async fn set_u16( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + value: u16, + ) -> Result<()> { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.memory_write_u16(memory, addr, value) + .await? + .ok_or(wit::Error::OutOfBounds)?; + Ok(()) + } + + async fn set_u32( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + value: u32, + ) -> Result<()> { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.memory_write_u32(memory, addr, value) + .await? + .ok_or(wit::Error::OutOfBounds)?; + Ok(()) + } + + async fn set_u64( + &mut self, + self_: Resource, + d: Resource, + addr: u64, + value: u64, + ) -> Result<()> { + let memory = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.memory_write_u64(memory, addr, value) + .await? + .ok_or(wit::Error::OutOfBounds)?; + Ok(()) + } + + async fn clone(&mut self, self_: Resource) -> Result> { + let memory = *self.get(&self_)?; + Ok(self.push(memory)?) + } + + async fn unique_id(&mut self, self_: Resource) -> Result { + Ok(self.get(&self_)?.debug_index_in_store()) + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::HostGlobal for ResourceTable { + async fn get( + &mut self, + self_: Resource, + d: Resource, + ) -> Result> { + // N.B.: we use UFCS here because `HostGlobal::get` conflicts + // with `ResourceTable::get` and we're implementing the WIT + // trait directly on the `ResourceTable`. + let global = *ResourceTable::get(self, &self_)?; + let d = debugger(self, &d)?; + let value = d.global_get(global).await?; + Ok(self.push(value)?) + } + + async fn set( + &mut self, + self_: Resource, + d: Resource, + val: Resource, + ) -> Result<()> { + let global = *ResourceTable::get(self, &self_)?; + let value = ResourceTable::get(self, &val)?.clone(); + let d = debugger(self, &d)?; + d.global_set(global, value).await + } + + async fn clone(&mut self, self_: Resource) -> Result> { + let global = *ResourceTable::get(self, &self_)?; + Ok(self.push(global)?) + } + + async fn unique_id(&mut self, self_: Resource) -> Result { + let global = *ResourceTable::get(self, &self_)?; + Ok(global.debug_index_in_store()) + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::HostTable for ResourceTable { + async fn len(&mut self, self_: Resource, d: Resource) -> Result { + let table = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.table_len(table).await + } + + async fn get_element( + &mut self, + self_: Resource
, + d: Resource, + index: u64, + ) -> Result> { + let table = *self.get(&self_)?; + let d = debugger(self, &d)?; + let value = d.table_get_element(table, index).await?; + Ok(self.push(value)?) + } + + async fn set_element( + &mut self, + self_: Resource
, + d: Resource, + index: u64, + val: Resource, + ) -> Result<()> { + let table = *self.get(&self_)?; + let value = self.get(&val)?.clone(); + let d = debugger(self, &d)?; + d.table_set_element(table, index, value).await + } + + async fn clone(&mut self, self_: Resource
) -> Result> { + let table = *self.get(&self_)?; + Ok(self.push(table)?) + } + + async fn unique_id(&mut self, self_: Resource
) -> Result { + Ok(self.get(&self_)?.debug_index_in_store()) + } + + async fn drop(&mut self, rep: Resource
) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::HostWasmFunc for ResourceTable { + async fn params( + &mut self, + self_: Resource, + d: Resource, + ) -> Result> { + let func = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.func_params(func).await + } + + async fn results( + &mut self, + self_: Resource, + d: Resource, + ) -> Result> { + let func = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.func_results(func).await + } + + async fn clone(&mut self, self_: Resource) -> Result> { + let func = *self.get(&self_)?; + Ok(self.push(func)?) + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::HostWasmException for ResourceTable { + async fn get_tag( + &mut self, + self_: Resource, + d: Resource, + ) -> Result> { + let exn = self.get(&self_)?.clone(); + let d = debugger(self, &d)?; + let tag = d.exnref_get_tag(exn.0).await?; + Ok(self.push(tag)?) + } + + async fn get_values( + &mut self, + self_: Resource, + d: Resource, + ) -> Result>> { + let exn = self.get(&self_)?.clone(); + let d = debugger(self, &d)?; + let values = d.exnref_get_fields(exn.0).await?; + let mut resources = vec![]; + for v in values { + resources.push(self.push(v)?); + } + Ok(resources) + } + + async fn clone( + &mut self, + self_: Resource, + _d: Resource, + ) -> Result> { + let exn = self.get(&self_)?.clone(); + Ok(self.push(exn)?) + } + + async fn make( + &mut self, + d: Resource, + tag: Resource, + values: Vec>, + ) -> Result> { + let tag_val = *self.get(&tag)?; + let mut wasm_values = vec![]; + for v in &values { + wasm_values.push(self.get(v)?.clone()); + } + let d = debugger(self, &d)?; + let owned = d.exnref_new(tag_val, wasm_values).await?; + Ok(self.push(WasmException(owned))?) + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::HostWasmTag for ResourceTable { + async fn params( + &mut self, + self_: Resource, + d: Resource, + ) -> Result> { + let tag = *self.get(&self_)?; + let d = debugger(self, &d)?; + d.tag_params(tag).await + } + + async fn unique_id(&mut self, self_: Resource) -> Result { + Ok(self.get(&self_)?.debug_index_in_store()) + } + + async fn clone(&mut self, self_: Resource) -> Result> { + let tag = *self.get(&self_)?; + Ok(self.push(tag)?) + } + + async fn make( + &mut self, + d: Resource, + params: Vec, + ) -> Result> { + let engine = self.get(&d)?.engine.clone(); + let val_types = params.into_iter().map(wasm_type_to_val_type).collect(); + let d = debugger(self, &d)?; + let tag = d.tag_new(engine, val_types).await?; + Ok(self.push(tag)?) + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::HostFrame for ResourceTable { + async fn get_instance( + &mut self, + self_: Resource, + d: Resource, + ) -> Result> { + let frame = self.get(&self_)?.0.clone(); + let d = debugger(self, &d)?; + let instance = d.frame_instance(frame).await?; + Ok(self.push(instance)?) + } + + async fn get_func_index( + &mut self, + self_: Resource, + d: Resource, + ) -> Result { + let frame = self.get(&self_)?.0.clone(); + let d = debugger(self, &d)?; + let (f, _) = d.frame_func_and_pc(frame).await?; + Ok(f) + } + + async fn get_pc(&mut self, self_: Resource, d: Resource) -> Result { + let frame = self.get(&self_)?.0.clone(); + let d = debugger(self, &d)?; + let (_, pc) = d.frame_func_and_pc(frame).await?; + Ok(pc) + } + + async fn get_locals( + &mut self, + self_: Resource, + d: Resource, + ) -> Result>> { + let frame = self.get(&self_)?.0.clone(); + let d = debugger(self, &d)?; + let locals = d.frame_locals(frame).await?; + let mut resources = vec![]; + for local in locals { + resources.push(self.push(local)?); + } + Ok(resources) + } + + async fn get_stack( + &mut self, + self_: Resource, + d: Resource, + ) -> Result>> { + let frame = self.get(&self_)?.0.clone(); + let d = debugger(self, &d)?; + let stacks = d.frame_stack(frame).await?; + let mut resources = vec![]; + for val in stacks { + resources.push(self.push(val)?); + } + Ok(resources) + } + + async fn parent_frame( + &mut self, + self_: Resource, + d: Resource, + ) -> Result>> { + let frame = self.get(&self_)?.0.clone(); + let d = debugger(self, &d)?; + let parent = d.frame_parent(frame).await?; + match parent { + Some(p) => Ok(Some(self.push(Frame(p))?)), + None => Ok(None), + } + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::HostWasmValue for ResourceTable { + async fn get_type(&mut self, self_: Resource) -> Result { + let value = self.get(&self_)?; + match value { + WasmValue::Primitive(Val::I32(_)) => Ok(wit::WasmType::WasmI32), + WasmValue::Primitive(Val::I64(_)) => Ok(wit::WasmType::WasmI64), + WasmValue::Primitive(Val::F32(_)) => Ok(wit::WasmType::WasmF32), + WasmValue::Primitive(Val::F64(_)) => Ok(wit::WasmType::WasmF64), + WasmValue::Primitive(Val::V128(_)) => Ok(wit::WasmType::WasmV128), + WasmValue::Func(_) => Ok(wit::WasmType::WasmFuncref), + WasmValue::Exn(_) => Ok(wit::WasmType::WasmExnref), + WasmValue::Primitive(_) => unreachable!(), + } + } + + async fn unwrap_i32(&mut self, self_: Resource) -> Result { + let value = self.get(&self_)?; + match value { + WasmValue::Primitive(Val::I32(x)) => Ok(x.cast_unsigned()), + _ => wasmtime::bail!("Wasm value is not an i32."), + } + } + + async fn unwrap_i64(&mut self, self_: Resource) -> Result { + let value = self.get(&self_)?; + match value { + WasmValue::Primitive(Val::I64(x)) => Ok(x.cast_unsigned()), + _ => wasmtime::bail!("Wasm value is not an i64."), + } + } + + async fn unwrap_f32(&mut self, self_: Resource) -> Result { + let value = self.get(&self_)?; + match value { + WasmValue::Primitive(Val::F32(x)) => Ok(f32::from_bits(*x)), + _ => wasmtime::bail!("Wasm value is not an f32."), + } + } + + async fn unwrap_f64(&mut self, self_: Resource) -> Result { + let value = self.get(&self_)?; + match value { + WasmValue::Primitive(Val::F64(x)) => Ok(f64::from_bits(*x)), + _ => wasmtime::bail!("Wasm value is not an f64."), + } + } + + async fn unwrap_v128(&mut self, self_: Resource) -> Result> { + let value = self.get(&self_)?; + match value { + WasmValue::Primitive(Val::V128(x)) => Ok(x.as_u128().to_le_bytes().to_vec()), + _ => wasmtime::bail!("Wasm value is not a v128."), + } + } + + async fn unwrap_func(&mut self, self_: Resource) -> Result>> { + let value = self.get(&self_)?; + match value { + WasmValue::Func(Some(f)) => { + let f = *f; + Ok(Some(self.push(f)?)) + } + WasmValue::Func(None) => Ok(None), + _ => wasmtime::bail!("Wasm value is not a funcref."), + } + } + + async fn unwrap_exception( + &mut self, + self_: Resource, + ) -> Result>> { + let value = self.get(&self_)?; + match value { + WasmValue::Exn(Some(e)) => { + let e = e.clone(); + Ok(Some(self.push(WasmException(e))?)) + } + WasmValue::Exn(None) => Ok(None), + _ => wasmtime::bail!("Wasm value is not an exnref."), + } + } + + async fn make_i32(&mut self, value: u32) -> Result> { + Ok(self.push(WasmValue::Primitive(Val::I32(value.cast_signed())))?) + } + + async fn make_i64(&mut self, value: u64) -> Result> { + Ok(self.push(WasmValue::Primitive(Val::I64(value.cast_signed())))?) + } + + async fn make_f32(&mut self, value: f32) -> Result> { + Ok(self.push(WasmValue::Primitive(Val::F32(value.to_bits())))?) + } + + async fn make_f64(&mut self, value: f64) -> Result> { + Ok(self.push(WasmValue::Primitive(Val::F64(value.to_bits())))?) + } + + async fn make_v128(&mut self, value: Vec) -> Result> { + let bytes: [u8; 16] = value + .try_into() + .map_err(|_| wasmtime::format_err!("v128 requires exactly 16 bytes"))?; + Ok(self.push(WasmValue::Primitive(Val::V128( + u128::from_le_bytes(bytes).into(), + )))?) + } + + async fn clone(&mut self, self_: Resource) -> Result> { + let value = self.get(&self_)?.clone(); + Ok(self.push(value)?) + } + + async fn drop(&mut self, rep: Resource) -> Result<()> { + self.delete(rep)?; + Ok(()) + } +} + +impl wit::Host for ResourceTable { + fn convert_error(&mut self, err: wasmtime::Error) -> Result { + err.downcast() + } +} diff --git a/crates/debugger/src/host/bindings.rs b/crates/debugger/src/host/bindings.rs new file mode 100644 index 000000000000..fbf698bf2819 --- /dev/null +++ b/crates/debugger/src/host/bindings.rs @@ -0,0 +1,65 @@ +//! wit-bindgen-generated host binding types. + +use wasmtime::{Result, ValType}; + +wasmtime::component::bindgen!({ + path: "wit", + world: "bytecodealliance:wasmtime/debug-main", + imports: { + // Everything is async, even the seemingly simple things + // like unwrapping a Wasm value, because we need to access + // the Store in many places and that is an async access + // via channels within the debuggee. + default: async | trappable + }, + exports: { + default: async, + }, + with: { + "bytecodealliance:wasmtime/debuggee.debuggee": super::api::Debuggee, + "bytecodealliance:wasmtime/debuggee.event-future": super::api::EventFuture, + "bytecodealliance:wasmtime/debuggee.frame": super::api::Frame, + "bytecodealliance:wasmtime/debuggee.instance": wasmtime::Instance, + "bytecodealliance:wasmtime/debuggee.module": wasmtime::Module, + "bytecodealliance:wasmtime/debuggee.table": wasmtime::Table, + "bytecodealliance:wasmtime/debuggee.global": wasmtime::Global, + "bytecodealliance:wasmtime/debuggee.memory": wasmtime::Memory, + "bytecodealliance:wasmtime/debuggee.wasm-tag": wasmtime::Tag, + "bytecodealliance:wasmtime/debuggee.wasm-func": wasmtime::Func, + "bytecodealliance:wasmtime/debuggee.wasm-exception": super::api::WasmException, + "bytecodealliance:wasmtime/debuggee.wasm-value": super::api::WasmValue, + + "wasi": wasmtime_wasi::p2::bindings, + }, + trappable_error_type: { + "bytecodealliance:wasmtime/debuggee.error" => wasmtime::Error, + }, + require_store_data_send: true, +}); + +use bytecodealliance::wasmtime::debuggee as wit; + +pub(crate) fn val_type_to_wasm_type(vt: &ValType) -> Result { + match vt { + ValType::I32 => Ok(wit::WasmType::WasmI32), + ValType::I64 => Ok(wit::WasmType::WasmI64), + ValType::F32 => Ok(wit::WasmType::WasmF32), + ValType::F64 => Ok(wit::WasmType::WasmF64), + ValType::V128 => Ok(wit::WasmType::WasmV128), + ValType::Ref(rt) if rt.heap_type().is_exn() => Ok(wit::WasmType::WasmExnref), + ValType::Ref(rt) if rt.heap_type().is_func() => Ok(wit::WasmType::WasmFuncref), + ValType::Ref(_) => Err(wit::Error::UnsupportedType.into()), + } +} + +pub(crate) fn wasm_type_to_val_type(wt: wit::WasmType) -> ValType { + match wt { + wit::WasmType::WasmI32 => ValType::I32, + wit::WasmType::WasmI64 => ValType::I64, + wit::WasmType::WasmF32 => ValType::F32, + wit::WasmType::WasmF64 => ValType::F64, + wit::WasmType::WasmV128 => ValType::V128, + wit::WasmType::WasmFuncref => ValType::FUNCREF, + wit::WasmType::WasmExnref => ValType::EXNREF, + } +} diff --git a/crates/debugger/src/host/opaque.rs b/crates/debugger/src/host/opaque.rs new file mode 100644 index 000000000000..cce93b9d96c5 --- /dev/null +++ b/crates/debugger/src/host/opaque.rs @@ -0,0 +1,582 @@ +//! A type-erased trait wrapping the `Debugger` to permit its use +//! within a resource. + +use crate::host::wit; +use crate::host::{api::WasmValue, bindings::val_type_to_wasm_type}; +use wasmtime::{ + Engine, ExnRef, ExnRefPre, ExnType, FrameHandle, Func, FuncType, Global, Instance, Memory, + Module, OwnedRooted, Result, Table, Tag, TagType, Val, ValType, +}; + +/// Type-erased interface to the `Debugger` implementing all +/// functionality necessary for the interfaces here. This needs to be +/// type-erased because the host-side resource APIs do not support +/// type-parameterized resource kinds -- e.g., we cannot have a +/// resource for a `Debugger`, only a `Debugger`, so the debuggee +/// resource essentially needs to carry a vtable for the kind of store +/// the debuggee has. +/// +/// Methods here return `wasmtime::Result`, where `Err` may wrap +/// either a `wit::Error` (which `convert_error` will extract and +/// return as an in-band WIT-level error to the component) or any +/// other error (which becomes a trap). +/// +/// These methods do not handle the "wrong state" errors (i.e., +/// execution is continuing so we cannot query store state): those are +/// handled one level up, via moving ownership of the instance of this +/// trait between the execution future and the debuggee resource +/// itself. +#[async_trait::async_trait] +pub(crate) trait OpaqueDebugger { + async fn all_instances(&mut self) -> Result>; + async fn all_modules(&mut self) -> Result>; + async fn handle_resumption(&mut self, resumption: &wit::ResumptionValue) -> Result<()>; + async fn single_step(&mut self) -> Result; + async fn continue_(&mut self) -> Result; + async fn exit_frames(&mut self) -> Result>; + async fn get_instance_module(&mut self, instance: Instance) -> Result; + + async fn instance_get_memory(&mut self, instance: Instance, idx: u32) + -> Result>; + async fn instance_get_global(&mut self, instance: Instance, idx: u32) + -> Result>; + async fn instance_get_table(&mut self, instance: Instance, idx: u32) -> Result>; + async fn instance_get_func(&mut self, instance: Instance, idx: u32) -> Result>; + async fn instance_get_tag(&mut self, instance: Instance, idx: u32) -> Result>; + + async fn memory_size_bytes(&mut self, memory: Memory) -> Result; + async fn memory_page_size(&mut self, memory: Memory) -> Result; + async fn memory_grow(&mut self, memory: Memory, delta_bytes: u64) -> Result; + async fn memory_read_bytes( + &mut self, + memory: Memory, + addr: u64, + len: u64, + ) -> Result>>; + async fn memory_write_bytes( + &mut self, + memory: Memory, + addr: u64, + bytes: Vec, + ) -> Result>; + async fn memory_read_u8(&mut self, memory: Memory, addr: u64) -> Result>; + async fn memory_read_u16(&mut self, memory: Memory, addr: u64) -> Result>; + async fn memory_read_u32(&mut self, memory: Memory, addr: u64) -> Result>; + async fn memory_read_u64(&mut self, memory: Memory, addr: u64) -> Result>; + async fn memory_write_u8(&mut self, memory: Memory, addr: u64, data: u8) -> Result>; + async fn memory_write_u16( + &mut self, + memory: Memory, + addr: u64, + data: u16, + ) -> Result>; + async fn memory_write_u32( + &mut self, + memory: Memory, + addr: u64, + data: u32, + ) -> Result>; + async fn memory_write_u64( + &mut self, + memory: Memory, + addr: u64, + data: u64, + ) -> Result>; + + async fn global_get(&mut self, global: Global) -> Result; + async fn global_set(&mut self, global: Global, val: WasmValue) -> Result<()>; + + async fn table_len(&mut self, table: Table) -> Result; + async fn table_get_element(&mut self, table: Table, index: u64) -> Result; + async fn table_set_element(&mut self, table: Table, index: u64, val: WasmValue) -> Result<()>; + + async fn func_params(&mut self, func: Func) -> Result>; + async fn func_results(&mut self, func: Func) -> Result>; + + async fn tag_params(&mut self, tag: Tag) -> Result>; + async fn tag_new(&mut self, engine: Engine, params: Vec) -> Result; + + async fn exnref_get_tag(&mut self, exn: OwnedRooted) -> Result; + async fn exnref_get_fields(&mut self, exn: OwnedRooted) -> Result>; + async fn exnref_new(&mut self, tag: Tag, fields: Vec) + -> Result>; + + async fn frame_instance(&mut self, frame: FrameHandle) -> Result; + async fn frame_func_and_pc(&mut self, frame: FrameHandle) -> Result<(u32, u32)>; + async fn frame_locals(&mut self, frame: FrameHandle) -> Result>; + async fn frame_stack(&mut self, frame: FrameHandle) -> Result>; + async fn frame_parent(&mut self, frame: FrameHandle) -> Result>; + + async fn module_add_breakpoint(&mut self, module: Module, pc: u32) -> Result<()>; + async fn module_remove_breakpoint(&mut self, module: Module, pc: u32) -> Result<()>; + + async fn finish(&mut self) -> Result<()>; +} + +#[async_trait::async_trait] +impl OpaqueDebugger for crate::Debuggee { + async fn all_instances(&mut self) -> Result> { + self.with_store(|store| store.debug_all_instances()).await + } + + async fn all_modules(&mut self) -> Result> { + self.with_store(|store| store.debug_all_modules()).await + } + + async fn single_step(&mut self) -> Result { + self.with_store(|store| store.edit_breakpoints().unwrap().single_step(true).unwrap()) + .await?; + + self.run().await + } + + async fn continue_(&mut self) -> Result { + self.with_store(|store| { + store + .edit_breakpoints() + .unwrap() + .single_step(false) + .unwrap() + }) + .await?; + + self.run().await + } + + async fn handle_resumption(&mut self, resumption: &wit::ResumptionValue) -> Result<()> { + match resumption { + wit::ResumptionValue::Normal => {} + _ => { + unimplemented!("Non-`Normal` resumption not yet supported"); + } + } + Ok(()) + } + + async fn exit_frames(&mut self) -> Result> { + self.with_store(|mut store| store.debug_exit_frames().collect::>()) + .await + } + + async fn get_instance_module(&mut self, instance: Instance) -> Result { + self.with_store(move |store| instance.module(&store).clone()) + .await + } + + async fn instance_get_memory( + &mut self, + instance: Instance, + idx: u32, + ) -> Result> { + self.with_store(move |mut store| instance.debug_memory(&mut store, idx)) + .await + } + + async fn instance_get_global( + &mut self, + instance: Instance, + idx: u32, + ) -> Result> { + self.with_store(move |mut store| instance.debug_global(&mut store, idx)) + .await + } + + async fn instance_get_table(&mut self, instance: Instance, idx: u32) -> Result> { + self.with_store(move |mut store| instance.debug_table(&mut store, idx)) + .await + } + + async fn instance_get_func(&mut self, instance: Instance, idx: u32) -> Result> { + self.with_store(move |mut store| instance.debug_function(&mut store, idx)) + .await + } + + async fn instance_get_tag(&mut self, instance: Instance, idx: u32) -> Result> { + self.with_store(move |mut store| instance.debug_tag(&mut store, idx)) + .await + } + + async fn memory_size_bytes(&mut self, memory: Memory) -> Result { + self.with_store(move |store| u64::try_from(memory.data_size(&store)).unwrap()) + .await + } + + async fn memory_page_size(&mut self, memory: Memory) -> Result { + self.with_store(move |store| memory.page_size(&store)).await + } + + async fn memory_grow(&mut self, memory: Memory, delta_bytes: u64) -> Result { + self.with_store(move |mut store| -> Result { + let page_size = memory.page_size(&store); + if delta_bytes & (page_size - 1) != 0 { + return Err(wit::Error::MemoryGrowFailure.into()); + } + let delta_pages = delta_bytes / page_size; + let old_pages = memory + .grow(&mut store, delta_pages) + .map_err(|_| wit::Error::MemoryGrowFailure)?; + Ok(old_pages * page_size) + }) + .await? + } + + async fn memory_read_bytes( + &mut self, + memory: Memory, + addr: u64, + len: u64, + ) -> Result>> { + self.with_store(move |store| { + let data = memory.data(&store); + let addr = usize::try_from(addr).unwrap(); + let len = usize::try_from(len).unwrap(); + data.get(addr..addr + len).map(|s| s.to_vec()) + }) + .await + } + + async fn memory_write_bytes( + &mut self, + memory: Memory, + addr: u64, + bytes: Vec, + ) -> Result> { + self.with_store(move |mut store| { + let data = memory.data_mut(&mut store); + let addr = usize::try_from(addr).unwrap(); + let dest = data.get_mut(addr..addr + bytes.len())?; + dest.copy_from_slice(&bytes); + Some(()) + }) + .await + } + + async fn memory_read_u8(&mut self, memory: Memory, addr: u64) -> Result> { + self.with_store(move |store| { + let data = memory.data(&store); + let addr = usize::try_from(addr).unwrap(); + Some(*data.get(addr)?) + }) + .await + } + + async fn memory_read_u16(&mut self, memory: Memory, addr: u64) -> Result> { + self.with_store(move |store| { + let data = memory.data(&store); + let addr = usize::try_from(addr).unwrap(); + Some(u16::from_le_bytes([*data.get(addr)?, *data.get(addr + 1)?])) + }) + .await + } + + async fn memory_read_u32(&mut self, memory: Memory, addr: u64) -> Result> { + self.with_store(move |store| { + let data = memory.data(&store); + let addr = usize::try_from(addr).unwrap(); + Some(u32::from_le_bytes([ + *data.get(addr)?, + *data.get(addr + 1)?, + *data.get(addr + 2)?, + *data.get(addr + 3)?, + ])) + }) + .await + } + + async fn memory_read_u64(&mut self, memory: Memory, addr: u64) -> Result> { + self.with_store(move |store| { + let data = memory.data(&store); + let addr = usize::try_from(addr).unwrap(); + Some(u64::from_le_bytes([ + *data.get(addr)?, + *data.get(addr + 1)?, + *data.get(addr + 2)?, + *data.get(addr + 3)?, + *data.get(addr + 4)?, + *data.get(addr + 5)?, + *data.get(addr + 6)?, + *data.get(addr + 7)?, + ])) + }) + .await + } + + async fn memory_write_u8( + &mut self, + memory: Memory, + addr: u64, + value: u8, + ) -> Result> { + self.with_store(move |mut store| { + let data = memory.data_mut(&mut store); + let addr = usize::try_from(addr).unwrap(); + *data.get_mut(addr)? = value; + Some(()) + }) + .await + } + + async fn memory_write_u16( + &mut self, + memory: Memory, + addr: u64, + value: u16, + ) -> Result> { + self.with_store(move |mut store| { + let data = memory.data_mut(&mut store); + let addr = usize::try_from(addr).unwrap(); + data.get_mut(addr..(addr + 2))? + .copy_from_slice(&value.to_le_bytes()); + Some(()) + }) + .await + } + + async fn memory_write_u32( + &mut self, + memory: Memory, + addr: u64, + value: u32, + ) -> Result> { + self.with_store(move |mut store| { + let data = memory.data_mut(&mut store); + let addr = usize::try_from(addr).unwrap(); + data.get_mut(addr..(addr + 4))? + .copy_from_slice(&value.to_le_bytes()); + Some(()) + }) + .await + } + + async fn memory_write_u64( + &mut self, + memory: Memory, + addr: u64, + value: u64, + ) -> Result> { + self.with_store(move |mut store| { + let data = memory.data_mut(&mut store); + let addr = usize::try_from(addr).unwrap(); + data.get_mut(addr..(addr + 8))? + .copy_from_slice(&value.to_le_bytes()); + Some(()) + }) + .await + } + + async fn global_get(&mut self, global: Global) -> Result { + self.with_store(move |mut store| { + let val = global.get(&mut store); + WasmValue::new(&mut store, val) + }) + .await? + } + + async fn global_set(&mut self, global: Global, val: WasmValue) -> Result<()> { + self.with_store(move |mut store| -> Result<()> { + let v = val.into_val(&mut store); + global + .set(&mut store, v) + .map_err(|_| wit::Error::MismatchedType)?; + Ok(()) + }) + .await? + } + + async fn table_len(&mut self, table: Table) -> Result { + self.with_store(move |store| table.size(&store)).await + } + + async fn table_get_element(&mut self, table: Table, index: u64) -> Result { + self.with_store(move |mut store| -> Result { + let val = table + .get(&mut store, index) + .ok_or(wit::Error::OutOfBounds)?; + WasmValue::new(&mut store, val.into()) + }) + .await? + } + + async fn table_set_element(&mut self, table: Table, index: u64, val: WasmValue) -> Result<()> { + self.with_store(move |mut store| -> Result<()> { + let v = val.into_val(&mut store); + let r = v.ref_().ok_or(wit::Error::MismatchedType)?; + table + .set(&mut store, index, r) + .map_err(|_| wit::Error::MismatchedType)?; + Ok(()) + }) + .await? + } + + async fn func_params(&mut self, func: Func) -> Result> { + self.with_store(move |store| { + let ty = func.ty(&store); + ty.params() + .map(|ty| val_type_to_wasm_type(&ty)) + .collect::>>() + }) + .await? + } + + async fn func_results(&mut self, func: Func) -> Result> { + self.with_store(move |store| { + let ty = func.ty(&store); + ty.results() + .map(|ty| val_type_to_wasm_type(&ty)) + .collect::>>() + }) + .await? + } + + async fn tag_params(&mut self, tag: Tag) -> Result> { + self.with_store(move |store| { + let ty = tag.ty(&store); + ty.ty() + .params() + .map(|ty| val_type_to_wasm_type(&ty)) + .collect::>>() + }) + .await? + } + + async fn tag_new(&mut self, engine: Engine, params: Vec) -> Result { + self.with_store(move |mut store| { + let func_ty = FuncType::new(&engine, params, []); + let tag_ty = TagType::new(func_ty); + Tag::new(&mut store, &tag_ty) + }) + .await? + } + + async fn exnref_get_tag(&mut self, exn: OwnedRooted) -> Result { + self.with_store(move |mut store| exn.tag(&mut store).expect("reference must be rooted")) + .await + } + + async fn exnref_get_fields(&mut self, exn: OwnedRooted) -> Result> { + self.with_store(move |mut store| { + let fields = exn + .fields(&mut store) + .expect("reference must be rooted") + .collect::>(); + fields + .into_iter() + .map(|v| WasmValue::new(&mut store, v)) + .collect::>>() + }) + .await? + } + + async fn exnref_new( + &mut self, + tag: Tag, + fields: Vec, + ) -> Result> { + self.with_store(move |mut store| -> Result> { + let exn_ty = + ExnType::from_tag_type(&tag.ty(&store)).expect("tag type is already validated"); + let allocator = ExnRefPre::new(&mut store, exn_ty); + let field_vals = fields + .into_iter() + .map(|v| v.into_val(&mut store)) + .collect::>(); + let exn = ExnRef::new(&mut store, &allocator, &tag, &field_vals) + .map_err(|_| wit::Error::AllocFailure)?; + Ok(exn.to_owned_rooted(&mut store).unwrap()) + }) + .await? + } + + async fn frame_instance(&mut self, frame: FrameHandle) -> Result { + self.with_store(move |mut store| -> Result { + Ok(frame + .instance(&mut store) + .map_err(|_| wit::Error::InvalidFrame)?) + }) + .await? + } + + async fn frame_func_and_pc(&mut self, frame: FrameHandle) -> Result<(u32, u32)> { + self.with_store(move |mut store| -> Result<(u32, u32)> { + let (func, pc) = frame + .wasm_function_index_and_pc(&mut store) + .map_err(|_| wit::Error::InvalidFrame)? + .ok_or(wit::Error::NonWasmFrame)?; + Ok((func.as_u32(), pc.raw())) + }) + .await? + } + + async fn frame_locals(&mut self, frame: FrameHandle) -> Result> { + self.with_store(move |mut store| -> Result> { + let n_locals = frame + .num_locals(&mut store) + .map_err(|_| wit::Error::InvalidFrame)?; + let mut result = vec![]; + for i in 0..n_locals { + let val = frame + .local(&mut store, i) + .expect("checked for validity above"); + result.push(WasmValue::new(&mut store, val)?); + } + Ok(result) + }) + .await? + } + + async fn frame_stack(&mut self, frame: FrameHandle) -> Result> { + self.with_store(move |mut store| -> Result> { + let n_stacks = frame + .num_stacks(&mut store) + .map_err(|_| wit::Error::InvalidFrame)?; + let mut result = vec![]; + for i in 0..n_stacks { + let val = frame + .stack(&mut store, i) + .expect("checked for validity above"); + result.push(WasmValue::new(&mut store, val)?); + } + Ok(result) + }) + .await? + } + + async fn frame_parent(&mut self, frame: FrameHandle) -> Result> { + self.with_store(move |mut store| -> Result> { + Ok(frame + .parent(&mut store) + .map_err(|_| wit::Error::InvalidFrame)?) + }) + .await? + } + + async fn module_add_breakpoint(&mut self, module: Module, pc: u32) -> Result<()> { + self.with_store(move |store| -> Result<()> { + store + .edit_breakpoints() + .expect("guest debugging is enabled") + .add_breakpoint(&module, wasmtime::ModulePC::new(pc)) + .map_err(|_| wit::Error::InvalidPc)?; + Ok(()) + }) + .await? + } + + async fn module_remove_breakpoint(&mut self, module: Module, pc: u32) -> Result<()> { + self.with_store(move |store| -> Result<()> { + store + .edit_breakpoints() + .expect("guest debugging is enabled") + .remove_breakpoint(&module, wasmtime::ModulePC::new(pc)) + .map_err(|_| wit::Error::InvalidPc)?; + Ok(()) + }) + .await? + } + + async fn finish(&mut self) -> Result<()> { + self.finish().await?; + Ok(()) + } +} diff --git a/crates/debugger/src/lib.rs b/crates/debugger/src/lib.rs index 92ec2ce7f223..5aad12d0fed3 100644 --- a/crates/debugger/src/lib.rs +++ b/crates/debugger/src/lib.rs @@ -2,21 +2,35 @@ //! //! This crate builds on top of the core Wasmtime crate's //! guest-debugger APIs to present an environment where a debugger -//! runs as a "co-running process" and sees the debugee as a a +//! runs as a "co-running process" and sees the debuggee as a a //! provider of a stream of events, on which actions can be taken //! between each event. //! //! In the future, this crate will also provide a WIT-level API and //! world in which to run debugger components. -use std::{any::Any, future::Future, pin::Pin, sync::Arc}; -use tokio::sync::{Mutex, mpsc}; +use std::{ + any::Any, + future::Future, + pin::Pin, + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + }, +}; +use tokio::{ + sync::{Mutex, mpsc}, + task::JoinHandle, +}; use wasmtime::{ AsContextMut, DebugEvent, DebugHandler, Engine, ExnRef, OwnedRooted, Result, Store, StoreContextMut, Trap, }; -/// A `Debugger` wraps up state associated with debugging the code +mod host; +pub use host::{DebuggerComponent, add_debuggee, add_to_linker, wit}; + +/// A `Debuggee` wraps up state associated with debugging the code /// running in a single `Store`. /// /// It acts as a Future combinator, wrapping an inner async body that @@ -26,24 +40,32 @@ use wasmtime::{ /// states: running or paused. When paused, it acts as a /// `StoreContextMut` and can allow examining the paused execution's /// state. One runs until the next event suspends execution by -/// invoking `Debugger::run`. -pub struct Debugger { +/// invoking `Debuggee::run`. +pub struct Debuggee { /// A handle to the Engine that the debuggee store lives within. engine: Engine, /// State: either a task handle or the store when passed out of /// the complete task. - state: DebuggerState, + state: DebuggeeState, /// The store, once complete. store: Option>, in_tx: mpsc::Sender>, out_rx: mpsc::Receiver>, + handle: Option>>, + /// Flag shared with the inner handler: set to `true` by + /// `interrupt()` so the next epoch yield is surfaced as an + /// `Interrupted` event rather than eaten by the handler. Epoch + /// yields serve two purposes, namely ensuring regular yields to + /// the event loop and enacting an explicit interrupt, and this + /// flag distinguishes those cases. + interrupt_pending: Arc, } /// State machine from the perspective of the outer logic. /// /// The intermediate states here, and the separation of these states /// from the `JoinHandle` above, are what allow us to implement a -/// cancel-safe version of `Debugger::run` below. +/// cancel-safe version of `Debuggee::run` below. /// /// The state diagram for the outer logic is: /// @@ -73,7 +95,7 @@ pub struct Debugger { /// `---<-' /// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum DebuggerState { +enum DebuggeeState { /// Inner body has just been started. Initial, /// Inner body is running in an async task and not in a debugger @@ -138,6 +160,7 @@ enum Response { struct HandlerInner { in_rx: Mutex>>, out_tx: mpsc::Sender>, + interrupt_pending: Arc, } struct Handler(Arc>); @@ -161,23 +184,39 @@ impl DebugHandler for Handler { } DebugEvent::Trap(trap) => DebugRunResult::Trap(trap), DebugEvent::Breakpoint => DebugRunResult::Breakpoint, - DebugEvent::EpochYield => DebugRunResult::EpochYield, + DebugEvent::EpochYield => { + // Only pause on epoch yields that were requested via + // interrupt(). Other epoch ticks simply yield to the + // event loop (functionality already implemented in + // core Wasmtime; no need to do that yield here in the + // debug handler). + if !self.0.interrupt_pending.swap(false, Ordering::SeqCst) { + return; + } + DebugRunResult::EpochYield + } }; - self.0 - .out_tx - .send(Response::Paused(result)) - .await - .expect("outbound channel closed prematurely"); + if self.0.out_tx.send(Response::Paused(result)).await.is_err() { + // Outer Debuggee has been dropped: just continue + // executing. + return; + } while let Some(cmd) = in_rx.recv().await { match cmd { Command::Query(closure) => { let result = closure(store.as_context_mut()); - self.0 + if self + .0 .out_tx .send(Response::QueryResponse(result)) .await - .expect("outbound channel closed prematurely"); + .is_err() + { + // Outer Debuggee has been dropped: just + // continue executing. + return; + } } Command::Continue => { break; @@ -187,7 +226,7 @@ impl DebugHandler for Handler { } } -impl Debugger { +impl Debuggee { /// Create a new Debugger that attaches to the given Store and /// runs the given inner body. /// @@ -195,14 +234,14 @@ impl Debugger { /// paused. /// /// When paused, the holder of this object can invoke - /// `Debugger::run` to enter the running state. The inner body + /// `Debuggee::run` to enter the running state. The inner body /// will run until paused by a debug event. While running, the - /// future returned by either of these methods owns the `Debugger` + /// future returned by either of these methods owns the `Debuggee` /// and hence no other methods can be invoked. /// /// When paused, the holder of this object can access the `Store` /// indirectly by providing a closure - pub fn new(mut store: Store, inner: F) -> Debugger + pub fn new(mut store: Store, inner: F) -> Debuggee where F: for<'a> FnOnce( &'a mut Store, @@ -213,45 +252,52 @@ impl Debugger { let engine = store.engine().clone(); let (in_tx, in_rx) = mpsc::channel(1); let (out_tx, out_rx) = mpsc::channel(1); - - tokio::spawn(async move { - // Create the handler that's invoked from within the async - // debug-event callback. - let out_tx_clone = out_tx.clone(); - let handler = Handler(Arc::new(HandlerInner { - in_rx: Mutex::new(in_rx), - out_tx, - })); - - // Emulate a breakpoint at startup. - log::trace!("inner debuggee task: first breakpoint"); - handler - .handle(store.as_context_mut(), DebugEvent::Breakpoint) - .await; - log::trace!("inner debuggee task: first breakpoint resumed"); - - // Now invoke the actual inner body. - store.set_debug_handler(handler); - log::trace!("inner debuggee task: running `inner`"); - let result = inner(&mut store).await; - log::trace!("inner debuggee task: done with `inner`"); - let _ = out_tx_clone.send(Response::Finished(store)).await; - result + let interrupt_pending = Arc::new(AtomicBool::new(false)); + + let handle = tokio::spawn({ + let interrupt_pending = interrupt_pending.clone(); + async move { + // Create the handler that's invoked from within the async + // debug-event callback. + let out_tx_clone = out_tx.clone(); + let handler = Handler(Arc::new(HandlerInner { + in_rx: Mutex::new(in_rx), + out_tx, + interrupt_pending, + })); + + // Emulate a breakpoint at startup. + log::trace!("inner debuggee task: first breakpoint"); + handler + .handle(store.as_context_mut(), DebugEvent::Breakpoint) + .await; + log::trace!("inner debuggee task: first breakpoint resumed"); + + // Now invoke the actual inner body. + store.set_debug_handler(handler); + log::trace!("inner debuggee task: running `inner`"); + let result = inner(&mut store).await; + log::trace!("inner debuggee task: done with `inner`"); + let _ = out_tx_clone.send(Response::Finished(store)).await; + result + } }); - Debugger { + Debuggee { engine, - state: DebuggerState::Initial, + state: DebuggeeState::Initial, store: None, in_tx, out_rx, + interrupt_pending, + handle: Some(handle), } } /// Is the inner body done running? pub fn is_complete(&self) -> bool { match self.state { - DebuggerState::Complete => true, + DebuggeeState::Complete => true, _ => false, } } @@ -261,8 +307,14 @@ impl Debugger { &self.engine } + /// Get the interrupt-pending flag. Setting this to `true` causes + /// the next epoch yield to surface as an `Interrupted` event. + pub fn interrupt_pending(&self) -> &Arc { + &self.interrupt_pending + } + async fn wait_for_initial(&mut self) -> Result<()> { - if let DebuggerState::Initial = &self.state { + if let DebuggeeState::Initial = &self.state { // Need to receive and discard first `Paused`. let response = self .out_rx @@ -270,7 +322,7 @@ impl Debugger { .await .ok_or_else(|| wasmtime::format_err!("Premature close of debugger channel"))?; assert!(matches!(response, Response::Paused(_))); - self.state = DebuggerState::Paused; + self.state = DebuggeeState::Paused; } Ok(()) } @@ -284,8 +336,8 @@ impl Debugger { self.wait_for_initial().await?; match self.state { - DebuggerState::Initial => unreachable!(), - DebuggerState::Paused => { + DebuggeeState::Initial => unreachable!(), + DebuggeeState::Paused => { log::trace!("sending Continue"); self.in_tx .send(Command::Continue) @@ -297,13 +349,13 @@ impl Debugger { // sent, so it's fine to remain in `Paused`. If it // succeeded and we reached here, transition to // `Running` so we don't re-send. - self.state = DebuggerState::Running; + self.state = DebuggeeState::Running; } - DebuggerState::Running => { + DebuggeeState::Running => { // Previous `run()` must have been canceled; no action // to take here. } - DebuggerState::Queried => { + DebuggeeState::Queried => { // We expect to receive a `QueryResponse`; drop it if // the query was canceled, then transition back to // `Paused`. @@ -314,7 +366,7 @@ impl Debugger { })?; log::trace!("in Queried; received, dropping"); assert!(matches!(response, Response::QueryResponse(_))); - self.state = DebuggerState::Paused; + self.state = DebuggeeState::Paused; // Now send a `Continue`, as above. log::trace!("in Paused; sending Continue"); @@ -322,10 +374,10 @@ impl Debugger { .send(Command::Continue) .await .map_err(|_| wasmtime::format_err!("Failed to send over debug channel"))?; - self.state = DebuggerState::Running; + self.state = DebuggeeState::Running; } - DebuggerState::Complete => { - panic!("Cannot `run()` an already-complete Debugger"); + DebuggeeState::Complete => { + panic!("Cannot `run()` an already-complete Debuggee"); } } @@ -344,13 +396,13 @@ impl Debugger { match response { Response::Finished(store) => { log::trace!("got Finished"); - self.state = DebuggerState::Complete; + self.state = DebuggeeState::Complete; self.store = Some(store); Ok(DebugRunResult::Finished) } Response::Paused(result) => { log::trace!("got Paused"); - self.state = DebuggerState::Paused; + self.state = DebuggeeState::Paused; Ok(result) } Response::QueryResponse(_) => { @@ -372,6 +424,9 @@ impl Debugger { } } } + if let Some(handle) = self.handle.take() { + handle.await??; + } assert!(self.is_complete()); Ok(()) } @@ -379,7 +434,7 @@ impl Debugger { /// Perform some action on the contained `Store` while not running. /// /// This may only be invoked before the inner body finishes and - /// when it is paused; that is, when the `Debugger` is initially + /// when it is paused; that is, when the `Debuggee` is initially /// created and after any call to `run()` returns a result other /// than `DebugRunResult::Finished`. If an earlier `run()` /// invocation was canceled, it must be re-invoked and return @@ -401,25 +456,25 @@ impl Debugger { self.wait_for_initial().await?; match self.state { - DebuggerState::Initial => unreachable!(), - DebuggerState::Queried => { + DebuggeeState::Initial => unreachable!(), + DebuggeeState::Queried => { // Earlier query canceled; drop its response first. let response = self.out_rx.recv().await.ok_or_else(|| { wasmtime::format_err!("Premature close of debugger channel") })?; assert!(matches!(response, Response::QueryResponse(_))); - self.state = DebuggerState::Paused; + self.state = DebuggeeState::Paused; } - DebuggerState::Running => { + DebuggeeState::Running => { // Results from a canceled `run()`; `run()` must // complete before this can be invoked. panic!("Cannot query in Running state"); } - DebuggerState::Complete => { + DebuggeeState::Complete => { panic!("Cannot query when complete"); } - DebuggerState::Paused => { + DebuggeeState::Paused => { // OK -- this is the state we want. } } @@ -429,7 +484,7 @@ impl Debugger { .send(Command::Query(Box::new(|store| Box::new(f(store))))) .await .map_err(|_| wasmtime::format_err!("Premature close of debugger channel"))?; - self.state = DebuggerState::Queried; + self.state = DebuggeeState::Queried; let response = self .out_rx @@ -439,13 +494,13 @@ impl Debugger { let Response::QueryResponse(resp) = response else { wasmtime::bail!("Incorrect response from debugger task"); }; - self.state = DebuggerState::Paused; + self.state = DebuggeeState::Paused; Ok(*resp.downcast::().expect("type mismatch")) } } -/// The result of one call to `Debugger::run()`. +/// The result of one call to `Debuggee::run()`. /// /// This is similar to `DebugEvent` but without the lifetime, so it /// can be sent across async tasks, and incorporates the possibility @@ -497,7 +552,7 @@ mod test { let instance = Instance::new_async(&mut store, &module, &[]).await?; let main = instance.get_func(&mut store, "main").unwrap(); - let mut debugger = Debugger::new(store, move |store| { + let mut debuggee = Debuggee::new(store, move |store| { Box::pin(async move { let mut results = [Val::I32(0)]; store.edit_breakpoints().unwrap().single_step(true).unwrap(); @@ -511,10 +566,10 @@ mod test { }) }); - let event = debugger.run().await?; + let event = debuggee.run().await?; assert!(matches!(event, DebugRunResult::Breakpoint)); // At (before executing) first `local.get`. - debugger + debuggee .with_store(|mut store| { let frame = store.debug_exit_frames().next().unwrap(); assert_eq!( @@ -531,7 +586,8 @@ mod test { .wasm_function_index_and_pc(&mut store) .unwrap() .unwrap() - .1, + .1 + .raw(), 36 ); assert_eq!(frame.num_locals(&mut store).unwrap(), 2); @@ -543,10 +599,10 @@ mod test { }) .await?; - let event = debugger.run().await?; + let event = debuggee.run().await?; // At second `local.get`. assert!(matches!(event, DebugRunResult::Breakpoint)); - debugger + debuggee .with_store(|mut store| { let frame = store.debug_exit_frames().next().unwrap(); assert_eq!( @@ -563,7 +619,8 @@ mod test { .wasm_function_index_and_pc(&mut store) .unwrap() .unwrap() - .1, + .1 + .raw(), 38 ); assert_eq!(frame.num_locals(&mut store).unwrap(), 2); @@ -576,10 +633,10 @@ mod test { }) .await?; - let event = debugger.run().await?; + let event = debuggee.run().await?; // At `i32.add`. assert!(matches!(event, DebugRunResult::Breakpoint)); - debugger + debuggee .with_store(|mut store| { let frame = store.debug_exit_frames().next().unwrap(); assert_eq!( @@ -596,7 +653,8 @@ mod test { .wasm_function_index_and_pc(&mut store) .unwrap() .unwrap() - .1, + .1 + .raw(), 40 ); assert_eq!(frame.num_locals(&mut store).unwrap(), 2); @@ -610,10 +668,10 @@ mod test { }) .await?; - let event = debugger.run().await?; + let event = debuggee.run().await?; // At return point. assert!(matches!(event, DebugRunResult::Breakpoint)); - debugger + debuggee .with_store(|mut store| { let frame = store.debug_exit_frames().next().unwrap(); assert_eq!( @@ -630,7 +688,8 @@ mod test { .wasm_function_index_and_pc(&mut store) .unwrap() .unwrap() - .1, + .1 + .raw(), 41 ); assert_eq!(frame.num_locals(&mut store).unwrap(), 2); @@ -644,7 +703,7 @@ mod test { .await?; // Now disable breakpoints before continuing. Second call should proceed with no more events. - debugger + debuggee .with_store(|store| { store .edit_breakpoints() @@ -654,10 +713,10 @@ mod test { }) .await?; - let event = debugger.run().await?; + let event = debuggee.run().await?; assert!(matches!(event, DebugRunResult::Finished)); - assert!(debugger.is_complete()); + assert!(debuggee.is_complete()); Ok(()) } @@ -685,7 +744,7 @@ mod test { let instance = Instance::new_async(&mut store, &module, &[]).await?; let main = instance.get_func(&mut store, "main").unwrap(); - let mut debugger = Debugger::new(store, move |store| { + let mut debuggee = Debuggee::new(store, move |store| { Box::pin(async move { let mut results = [Val::I32(0)]; store.edit_breakpoints().unwrap().single_step(true).unwrap(); @@ -696,15 +755,15 @@ mod test { }) }); - debugger.finish().await?; - assert!(debugger.is_complete()); + debuggee.finish().await?; + assert!(debuggee.is_complete()); Ok(()) } #[tokio::test] #[cfg_attr(miri, ignore)] - async fn drop_debugger_and_store() -> Result<()> { + async fn drop_debuggee_and_store() -> Result<()> { let _ = env_logger::try_init(); let mut config = Config::new(); @@ -725,7 +784,7 @@ mod test { let instance = Instance::new_async(&mut store, &module, &[]).await?; let main = instance.get_func(&mut store, "main").unwrap(); - let mut debugger = Debugger::new(store, move |store| { + let mut debuggee = Debuggee::new(store, move |store| { Box::pin(async move { let mut results = [Val::I32(0)]; store.edit_breakpoints().unwrap().single_step(true).unwrap(); @@ -740,7 +799,7 @@ mod test { // function. Wasmtime's fiber cleanup should safely happen // without attempting to raise debug async handler calls with // missing async context. - let _ = debugger.run().await?; + let _ = debuggee.run().await?; Ok(()) } diff --git a/crates/debugger/wit/deps/cli.wit b/crates/debugger/wit/deps/cli.wit new file mode 100644 index 000000000000..d7a3ca4d2ceb --- /dev/null +++ b/crates/debugger/wit/deps/cli.wit @@ -0,0 +1,261 @@ +package wasi:cli@0.2.6; + +@since(version = 0.2.0) +interface environment { + /// Get the POSIX-style environment variables. + /// + /// Each environment variable is provided as a pair of string variable names + /// and string value. + /// + /// Morally, these are a value import, but until value imports are available + /// in the component model, this import function should return the same + /// values each time it is called. + @since(version = 0.2.0) + get-environment: func() -> list>; + + /// Get the POSIX-style arguments to the program. + @since(version = 0.2.0) + get-arguments: func() -> list; + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + @since(version = 0.2.0) + initial-cwd: func() -> option; +} + +@since(version = 0.2.0) +interface exit { + /// Exit the current instance and any linked instances. + @since(version = 0.2.0) + exit: func(status: result); + + /// Exit the current instance and any linked instances, reporting the + /// specified status code to the host. + /// + /// The meaning of the code depends on the context, with 0 usually meaning + /// "success", and other values indicating various types of failure. + /// + /// This function does not return; the effect is analogous to a trap, but + /// without the connotation that something bad has happened. + @unstable(feature = cli-exit-with-code) + exit-with-code: func(status-code: u8); +} + +@since(version = 0.2.0) +interface run { + /// Run the program. + @since(version = 0.2.0) + run: func() -> result; +} + +@since(version = 0.2.0) +interface stdin { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream}; + + @since(version = 0.2.0) + get-stdin: func() -> input-stream; +} + +@since(version = 0.2.0) +interface stdout { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{output-stream}; + + @since(version = 0.2.0) + get-stdout: func() -> output-stream; +} + +@since(version = 0.2.0) +interface stderr { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{output-stream}; + + @since(version = 0.2.0) + get-stderr: func() -> output-stream; +} + +/// Terminal input. +/// +/// In the future, this may include functions for disabling echoing, +/// disabling input buffering so that keyboard events are sent through +/// immediately, querying supported features, and so on. +@since(version = 0.2.0) +interface terminal-input { + /// The input side of a terminal. + @since(version = 0.2.0) + resource terminal-input; +} + +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. +@since(version = 0.2.0) +interface terminal-output { + /// The output side of a terminal. + @since(version = 0.2.0) + resource terminal-output; +} + +/// An interface providing an optional `terminal-input` for stdin as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stdin { + @since(version = 0.2.0) + use terminal-input.{terminal-input}; + + /// If stdin is connected to a terminal, return a `terminal-input` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stdin: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stdout as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stdout { + @since(version = 0.2.0) + use terminal-output.{terminal-output}; + + /// If stdout is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stdout: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stderr as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stderr { + @since(version = 0.2.0) + use terminal-output.{terminal-output}; + + /// If stderr is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stderr: func() -> option; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import environment; + @since(version = 0.2.0) + import exit; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import stdin; + @since(version = 0.2.0) + import stdout; + @since(version = 0.2.0) + import stderr; + @since(version = 0.2.0) + import terminal-input; + @since(version = 0.2.0) + import terminal-output; + @since(version = 0.2.0) + import terminal-stdin; + @since(version = 0.2.0) + import terminal-stdout; + @since(version = 0.2.0) + import terminal-stderr; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/types@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/preopens@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/instance-network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/ip-name-lookup@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure-seed@0.2.6; +} +@since(version = 0.2.0) +world command { + @since(version = 0.2.0) + import environment; + @since(version = 0.2.0) + import exit; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import stdin; + @since(version = 0.2.0) + import stdout; + @since(version = 0.2.0) + import stderr; + @since(version = 0.2.0) + import terminal-input; + @since(version = 0.2.0) + import terminal-output; + @since(version = 0.2.0) + import terminal-stdin; + @since(version = 0.2.0) + import terminal-stdout; + @since(version = 0.2.0) + import terminal-stderr; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/types@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/preopens@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/instance-network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/ip-name-lookup@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure-seed@0.2.6; + + @since(version = 0.2.0) + export run; +} diff --git a/crates/debugger/wit/deps/clocks.wit b/crates/debugger/wit/deps/clocks.wit new file mode 100644 index 000000000000..d638f1a40fa1 --- /dev/null +++ b/crates/debugger/wit/deps/clocks.wit @@ -0,0 +1,157 @@ +package wasi:clocks@0.2.6; + +/// WASI Monotonic Clock is a clock API intended to let users measure elapsed +/// time. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A monotonic clock is a clock which has an unspecified initial value, and +/// successive reads of the clock will produce non-decreasing values. +@since(version = 0.2.0) +interface monotonic-clock { + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + + /// An instant in time, in nanoseconds. An instant is relative to an + /// unspecified initial value, and can only be compared to instances from + /// the same monotonic-clock. + @since(version = 0.2.0) + type instant = u64; + + /// A duration of time, in nanoseconds. + @since(version = 0.2.0) + type duration = u64; + + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + @since(version = 0.2.0) + now: func() -> instant; + + /// Query the resolution of the clock. Returns the duration of time + /// corresponding to a clock tick. + @since(version = 0.2.0) + resolution: func() -> duration; + + /// Create a `pollable` which will resolve once the specified instant + /// has occurred. + @since(version = 0.2.0) + subscribe-instant: func(when: instant) -> pollable; + + /// Create a `pollable` that will resolve after the specified duration has + /// elapsed from the time this function is invoked. + @since(version = 0.2.0) + subscribe-duration: func(when: duration) -> pollable; +} + +/// WASI Wall Clock is a clock API intended to let users query the current +/// time. The name "wall" makes an analogy to a "clock on the wall", which +/// is not necessarily monotonic as it may be reset. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A wall clock is a clock which measures the date and time according to +/// some external reference. +/// +/// External references may be reset, so this clock is not necessarily +/// monotonic, making it unsuitable for measuring elapsed time. +/// +/// It is intended for reporting the current date and time for humans. +@since(version = 0.2.0) +interface wall-clock { + /// A time and date in seconds plus nanoseconds. + @since(version = 0.2.0) + record datetime { + seconds: u64, + nanoseconds: u32, + } + + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + @since(version = 0.2.0) + now: func() -> datetime; + + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + @since(version = 0.2.0) + resolution: func() -> datetime; +} + +@unstable(feature = clocks-timezone) +interface timezone { + @unstable(feature = clocks-timezone) + use wall-clock.{datetime}; + + /// Information useful for displaying the timezone of a specific `datetime`. + /// + /// This information may vary within a single `timezone` to reflect daylight + /// saving time adjustments. + @unstable(feature = clocks-timezone) + record timezone-display { + /// The number of seconds difference between UTC time and the local + /// time of the timezone. + /// + /// The returned value will always be less than 86400 which is the + /// number of seconds in a day (24*60*60). + /// + /// In implementations that do not expose an actual time zone, this + /// should return 0. + utc-offset: s32, + /// The abbreviated name of the timezone to display to a user. The name + /// `UTC` indicates Coordinated Universal Time. Otherwise, this should + /// reference local standards for the name of the time zone. + /// + /// In implementations that do not expose an actual time zone, this + /// should be the string `UTC`. + /// + /// In time zones that do not have an applicable name, a formatted + /// representation of the UTC offset may be returned, such as `-04:00`. + name: string, + /// Whether daylight saving time is active. + /// + /// In implementations that do not expose an actual time zone, this + /// should return false. + in-daylight-saving-time: bool, + } + + /// Return information needed to display the given `datetime`. This includes + /// the UTC offset, the time zone name, and a flag indicating whether + /// daylight saving time is active. + /// + /// If the timezone cannot be determined for the given `datetime`, return a + /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight + /// saving time. + @unstable(feature = clocks-timezone) + display: func(when: datetime) -> timezone-display; + + /// The same as `display`, but only return the UTC offset. + @unstable(feature = clocks-timezone) + utc-offset: func(when: datetime) -> s32; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import monotonic-clock; + @since(version = 0.2.0) + import wall-clock; + @unstable(feature = clocks-timezone) + import timezone; +} diff --git a/crates/debugger/wit/deps/filesystem.wit b/crates/debugger/wit/deps/filesystem.wit new file mode 100644 index 000000000000..9f4a8288b48d --- /dev/null +++ b/crates/debugger/wit/deps/filesystem.wit @@ -0,0 +1,587 @@ +package wasi:filesystem@0.2.6; + +/// WASI filesystem is a filesystem API primarily intended to let users run WASI +/// programs that access their files on their existing filesystems, without +/// significant overhead. +/// +/// It is intended to be roughly portable between Unix-family platforms and +/// Windows, though it does not hide many of the major differences. +/// +/// Paths are passed as interface-type `string`s, meaning they must consist of +/// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain +/// paths which are not accessible by this API. +/// +/// The directory separator in WASI is always the forward-slash (`/`). +/// +/// All paths in WASI are relative paths, and are interpreted relative to a +/// `descriptor` referring to a base directory. If a `path` argument to any WASI +/// function starts with `/`, or if any step of resolving a `path`, including +/// `..` and symbolic link steps, reaches a directory outside of the base +/// directory, or reaches a symlink to an absolute or rooted path in the +/// underlying filesystem, the function fails with `error-code::not-permitted`. +/// +/// For more information about WASI path resolution and sandboxing, see +/// [WASI filesystem path resolution]. +/// +/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md +@since(version = 0.2.0) +interface types { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream, output-stream, error}; + @since(version = 0.2.0) + use wasi:clocks/wall-clock@0.2.6.{datetime}; + + /// File size or length of a region within a file. + @since(version = 0.2.0) + type filesize = u64; + + /// The type of a filesystem object referenced by a descriptor. + /// + /// Note: This was called `filetype` in earlier versions of WASI. + @since(version = 0.2.0) + enum descriptor-type { + /// The type of the descriptor or file is unknown or is different from + /// any of the other types specified. + unknown, + /// The descriptor refers to a block device inode. + block-device, + /// The descriptor refers to a character device inode. + character-device, + /// The descriptor refers to a directory inode. + directory, + /// The descriptor refers to a named pipe. + fifo, + /// The file refers to a symbolic link inode. + symbolic-link, + /// The descriptor refers to a regular file inode. + regular-file, + /// The descriptor refers to a socket. + socket, + } + + /// Descriptor flags. + /// + /// Note: This was called `fdflags` in earlier versions of WASI. + @since(version = 0.2.0) + flags descriptor-flags { + /// Read mode: Data can be read. + read, + /// Write mode: Data can be written to. + write, + /// Request that writes be performed according to synchronized I/O file + /// integrity completion. The data stored in the file and the file's + /// metadata are synchronized. This is similar to `O_SYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + file-integrity-sync, + /// Request that writes be performed according to synchronized I/O data + /// integrity completion. Only the data stored in the file is + /// synchronized. This is similar to `O_DSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + data-integrity-sync, + /// Requests that reads be performed at the same level of integrity + /// requested for writes. This is similar to `O_RSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + requested-write-sync, + /// Mutating directories mode: Directory contents may be mutated. + /// + /// When this flag is unset on a descriptor, operations using the + /// descriptor which would create, rename, delete, modify the data or + /// metadata of filesystem objects, or obtain another handle which + /// would permit any of those, shall fail with `error-code::read-only` if + /// they would otherwise succeed. + /// + /// This may only be set on directories. + mutate-directory, + } + + /// Flags determining the method of how paths are resolved. + @since(version = 0.2.0) + flags path-flags { + /// As long as the resolved path corresponds to a symbolic link, it is + /// expanded. + symlink-follow, + } + + /// Open flags used by `open-at`. + @since(version = 0.2.0) + flags open-flags { + /// Create file if it does not exist, similar to `O_CREAT` in POSIX. + create, + /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. + directory, + /// Fail if file already exists, similar to `O_EXCL` in POSIX. + exclusive, + /// Truncate file to size 0, similar to `O_TRUNC` in POSIX. + truncate, + } + + /// Number of hard links to an inode. + @since(version = 0.2.0) + type link-count = u64; + + /// File attributes. + /// + /// Note: This was called `filestat` in earlier versions of WASI. + @since(version = 0.2.0) + record descriptor-stat { + /// File type. + %type: descriptor-type, + /// Number of hard links to the file. + link-count: link-count, + /// For regular files, the file size in bytes. For symbolic links, the + /// length in bytes of the pathname contained in the symbolic link. + size: filesize, + /// Last data access timestamp. + /// + /// If the `option` is none, the platform doesn't maintain an access + /// timestamp for this file. + data-access-timestamp: option, + /// Last data modification timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + data-modification-timestamp: option, + /// Last file status-change timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// status-change timestamp for this file. + status-change-timestamp: option, + } + + /// When setting a timestamp, this gives the value to set it to. + @since(version = 0.2.0) + variant new-timestamp { + /// Leave the timestamp set to its previous value. + no-change, + /// Set the timestamp to the current time of the system clock associated + /// with the filesystem. + now, + /// Set the timestamp to the given value. + timestamp(datetime), + } + + /// A directory entry. + record directory-entry { + /// The type of the file referred to by this directory entry. + %type: descriptor-type, + /// The name of the object. + name: string, + } + + /// Error codes returned by functions, similar to `errno` in POSIX. + /// Not all of these error codes are returned by the functions provided by this + /// API; some are used in higher-level library layers, and others are provided + /// merely for alignment with POSIX. + enum error-code { + /// Permission denied, similar to `EACCES` in POSIX. + access, + /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. + would-block, + /// Connection already in progress, similar to `EALREADY` in POSIX. + already, + /// Bad descriptor, similar to `EBADF` in POSIX. + bad-descriptor, + /// Device or resource busy, similar to `EBUSY` in POSIX. + busy, + /// Resource deadlock would occur, similar to `EDEADLK` in POSIX. + deadlock, + /// Storage quota exceeded, similar to `EDQUOT` in POSIX. + quota, + /// File exists, similar to `EEXIST` in POSIX. + exist, + /// File too large, similar to `EFBIG` in POSIX. + file-too-large, + /// Illegal byte sequence, similar to `EILSEQ` in POSIX. + illegal-byte-sequence, + /// Operation in progress, similar to `EINPROGRESS` in POSIX. + in-progress, + /// Interrupted function, similar to `EINTR` in POSIX. + interrupted, + /// Invalid argument, similar to `EINVAL` in POSIX. + invalid, + /// I/O error, similar to `EIO` in POSIX. + io, + /// Is a directory, similar to `EISDIR` in POSIX. + is-directory, + /// Too many levels of symbolic links, similar to `ELOOP` in POSIX. + loop, + /// Too many links, similar to `EMLINK` in POSIX. + too-many-links, + /// Message too large, similar to `EMSGSIZE` in POSIX. + message-size, + /// Filename too long, similar to `ENAMETOOLONG` in POSIX. + name-too-long, + /// No such device, similar to `ENODEV` in POSIX. + no-device, + /// No such file or directory, similar to `ENOENT` in POSIX. + no-entry, + /// No locks available, similar to `ENOLCK` in POSIX. + no-lock, + /// Not enough space, similar to `ENOMEM` in POSIX. + insufficient-memory, + /// No space left on device, similar to `ENOSPC` in POSIX. + insufficient-space, + /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. + not-directory, + /// Directory not empty, similar to `ENOTEMPTY` in POSIX. + not-empty, + /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. + not-recoverable, + /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. + unsupported, + /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. + no-tty, + /// No such device or address, similar to `ENXIO` in POSIX. + no-such-device, + /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. + overflow, + /// Operation not permitted, similar to `EPERM` in POSIX. + not-permitted, + /// Broken pipe, similar to `EPIPE` in POSIX. + pipe, + /// Read-only file system, similar to `EROFS` in POSIX. + read-only, + /// Invalid seek, similar to `ESPIPE` in POSIX. + invalid-seek, + /// Text file busy, similar to `ETXTBSY` in POSIX. + text-file-busy, + /// Cross-device link, similar to `EXDEV` in POSIX. + cross-device, + } + + /// File or memory access pattern advisory information. + @since(version = 0.2.0) + enum advice { + /// The application has no advice to give on its behavior with respect + /// to the specified data. + normal, + /// The application expects to access the specified data sequentially + /// from lower offsets to higher offsets. + sequential, + /// The application expects to access the specified data in a random + /// order. + random, + /// The application expects to access the specified data in the near + /// future. + will-need, + /// The application expects that it will not access the specified data + /// in the near future. + dont-need, + /// The application expects to access the specified data once and then + /// not reuse it thereafter. + no-reuse, + } + + /// A 128-bit hash value, split into parts because wasm doesn't have a + /// 128-bit integer type. + @since(version = 0.2.0) + record metadata-hash-value { + /// 64 bits of a 128-bit hash value. + lower: u64, + /// Another 64 bits of a 128-bit hash value. + upper: u64, + } + + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + @since(version = 0.2.0) + resource descriptor { + /// Return a stream for reading from a file, if available. + /// + /// May fail with an error-code describing why the file cannot be read. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. + @since(version = 0.2.0) + read-via-stream: func(offset: filesize) -> result; + /// Return a stream for writing to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be written. + /// + /// Note: This allows using `write-stream`, which is similar to `write` in + /// POSIX. + @since(version = 0.2.0) + write-via-stream: func(offset: filesize) -> result; + /// Return a stream for appending to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be appended. + /// + /// Note: This allows using `write-stream`, which is similar to `write` with + /// `O_APPEND` in POSIX. + @since(version = 0.2.0) + append-via-stream: func() -> result; + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + @since(version = 0.2.0) + advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + @since(version = 0.2.0) + sync-data: func() -> result<_, error-code>; + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.2.0) + get-flags: func() -> result; + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.2.0) + get-type: func() -> result; + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + @since(version = 0.2.0) + set-size: func(size: filesize) -> result<_, error-code>; + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + @since(version = 0.2.0) + set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. The returned list will contain up to `length` bytes; it + /// may return fewer than requested, if the end of the file is reached or + /// if the I/O operation is interrupted. + /// + /// In the future, this may change to return a `stream`. + /// + /// Note: This is similar to `pread` in POSIX. + @since(version = 0.2.0) + read: func(length: filesize, offset: filesize) -> result, bool>, error-code>; + /// Write to a descriptor, without using and updating the descriptor's offset. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// In the future, this may change to take a `stream`. + /// + /// Note: This is similar to `pwrite` in POSIX. + @since(version = 0.2.0) + write: func(buffer: list, offset: filesize) -> result; + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + @since(version = 0.2.0) + read-directory: func() -> result; + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + @since(version = 0.2.0) + sync: func() -> result<_, error-code>; + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + @since(version = 0.2.0) + create-directory-at: func(path: string) -> result<_, error-code>; + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX, except that it does not return + /// device and inode information. For testing whether two descriptors refer to + /// the same underlying filesystem object, use `is-same-object`. To obtain + /// additional data that can be used do determine whether a file has been + /// modified, use `metadata-hash`. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + @since(version = 0.2.0) + stat: func() -> result; + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX, except that it does not + /// return device and inode information. See the `stat` description for a + /// discussion of alternatives. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + @since(version = 0.2.0) + stat-at: func(path-flags: path-flags, path: string) -> result; + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + @since(version = 0.2.0) + set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + /// Create a hard link. + /// + /// Fails with `error-code::no-entry` if the old path does not exist, + /// with `error-code::exist` if the new path already exists, and + /// `error-code::not-permitted` if the old path is not a file. + /// + /// Note: This is similar to `linkat` in POSIX. + @since(version = 0.2.0) + link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + /// Open a file or directory. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + @since(version = 0.2.0) + open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + @since(version = 0.2.0) + readlink-at: func(path: string) -> result; + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + @since(version = 0.2.0) + remove-directory-at: func(path: string) -> result<_, error-code>; + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + @since(version = 0.2.0) + rename-at: func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + @since(version = 0.2.0) + symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>; + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + @since(version = 0.2.0) + unlink-file-at: func(path: string) -> result<_, error-code>; + /// Test whether two descriptors refer to the same filesystem object. + /// + /// In POSIX, this corresponds to testing whether the two descriptors have the + /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. + /// wasi-filesystem does not expose device and inode numbers, so this function + /// may be used instead. + @since(version = 0.2.0) + is-same-object: func(other: borrow) -> bool; + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a descriptor. + /// + /// This returns a hash of the last-modification timestamp and file size, and + /// may also include the inode number, device number, birth timestamp, and + /// other metadata fields that may change when the file is modified or + /// replaced. It may also include a secret value chosen by the + /// implementation and not otherwise exposed. + /// + /// Implementations are encouraged to provide the following properties: + /// + /// - If the file is not modified or replaced, the computed hash value should + /// usually not change. + /// - If the object is modified or replaced, the computed hash value should + /// usually change. + /// - The inputs to the hash should not be easily computable from the + /// computed hash. + /// + /// However, none of these is required. + @since(version = 0.2.0) + metadata-hash: func() -> result; + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a directory descriptor and a relative path. + /// + /// This performs the same hash computation as `metadata-hash`. + @since(version = 0.2.0) + metadata-hash-at: func(path-flags: path-flags, path: string) -> result; + } + + /// A stream of directory entries. + @since(version = 0.2.0) + resource directory-entry-stream { + /// Read a single directory entry from a `directory-entry-stream`. + @since(version = 0.2.0) + read-directory-entry: func() -> result, error-code>; + } + + /// Attempts to extract a filesystem-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// filesystem-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are filesystem-related errors. + @since(version = 0.2.0) + filesystem-error-code: func(err: borrow) -> option; +} + +@since(version = 0.2.0) +interface preopens { + @since(version = 0.2.0) + use types.{descriptor}; + + /// Return the set of preopened directories, and their paths. + @since(version = 0.2.0) + get-directories: func() -> list>; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import preopens; +} diff --git a/crates/debugger/wit/deps/io.wit b/crates/debugger/wit/deps/io.wit new file mode 100644 index 000000000000..08ad78e6b7c7 --- /dev/null +++ b/crates/debugger/wit/deps/io.wit @@ -0,0 +1,331 @@ +package wasi:io@0.2.6; + +@since(version = 0.2.0) +interface error { + /// A resource which represents some error information. + /// + /// The only method provided by this resource is `to-debug-string`, + /// which provides some human-readable information about the error. + /// + /// In the `wasi:io` package, this resource is returned through the + /// `wasi:io/streams/stream-error` type. + /// + /// To provide more specific error information, other interfaces may + /// offer functions to "downcast" this error into more specific types. For example, + /// errors returned from streams derived from filesystem types can be described using + /// the filesystem's own error-code type. This is done using the function + /// `wasi:filesystem/types/filesystem-error-code`, which takes a `borrow` + /// parameter and returns an `option`. + /// + /// The set of functions which can "downcast" an `error` into a more + /// concrete type is open. + @since(version = 0.2.0) + resource error { + /// Returns a string that is suitable to assist humans in debugging + /// this error. + /// + /// WARNING: The returned string should not be consumed mechanically! + /// It may change across platforms, hosts, or other implementation + /// details. Parsing this string is a major platform-compatibility + /// hazard. + @since(version = 0.2.0) + to-debug-string: func() -> string; + } +} + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +@since(version = 0.2.0) +interface poll { + /// `pollable` represents a single I/O event which may be ready, or not. + @since(version = 0.2.0) + resource pollable { + /// Return the readiness of a pollable. This function never blocks. + /// + /// Returns `true` when the pollable is ready, and `false` otherwise. + @since(version = 0.2.0) + ready: func() -> bool; + /// `block` returns immediately if the pollable is ready, and otherwise + /// blocks until ready. + /// + /// This function is equivalent to calling `poll.poll` on a list + /// containing only this pollable. + @since(version = 0.2.0) + block: func(); + } + + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// This function traps if either: + /// - the list is empty, or: + /// - the list contains more elements than can be indexed with a `u32` value. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being ready for I/O. + @since(version = 0.2.0) + poll: func(in: list>) -> list; +} + +/// WASI I/O is an I/O abstraction API which is currently focused on providing +/// stream types. +/// +/// In the future, the component model is expected to add built-in stream types; +/// when it does, they are expected to subsume this API. +@since(version = 0.2.0) +interface streams { + @since(version = 0.2.0) + use error.{error}; + @since(version = 0.2.0) + use poll.{pollable}; + + /// An error for input-stream and output-stream operations. + @since(version = 0.2.0) + variant stream-error { + /// The last operation (a write or flush) failed before completion. + /// + /// More information is available in the `error` payload. + /// + /// After this, the stream will be closed. All future operations return + /// `stream-error::closed`. + last-operation-failed(error), + /// The stream is closed: no more input will be accepted by the + /// stream. A closed output-stream will return this error on all + /// future operations. + closed, + } + + /// An input bytestream. + /// + /// `input-stream`s are *non-blocking* to the extent practical on underlying + /// platforms. I/O operations always return promptly; if fewer bytes are + /// promptly available than requested, they return the number of bytes promptly + /// available, which could even be zero. To wait for data to be available, + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + @since(version = 0.2.0) + resource input-stream { + /// Perform a non-blocking read from the stream. + /// + /// When the source of a `read` is binary data, the bytes from the source + /// are returned verbatim. When the source of a `read` is known to the + /// implementation to be text, bytes containing the UTF-8 encoding of the + /// text are returned. + /// + /// This function returns a list of bytes containing the read data, + /// when successful. The returned list will contain up to `len` bytes; + /// it may return fewer than requested, but not more. The list is + /// empty when no bytes are available for reading at this time. The + /// pollable given by `subscribe` will be ready when more bytes are + /// available. + /// + /// This function fails with a `stream-error` when the operation + /// encounters an error, giving `last-operation-failed`, or when the + /// stream is closed, giving `closed`. + /// + /// When the caller gives a `len` of 0, it represents a request to + /// read 0 bytes. If the stream is still open, this call should + /// succeed and return an empty list, or otherwise fail with `closed`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + @since(version = 0.2.0) + read: func(len: u64) -> result, stream-error>; + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, behavior is identical to `read`. + @since(version = 0.2.0) + blocking-read: func(len: u64) -> result, stream-error>; + /// Skip bytes from a stream. Returns number of bytes skipped. + /// + /// Behaves identical to `read`, except instead of returning a list + /// of bytes, returns the number of bytes consumed from the stream. + @since(version = 0.2.0) + skip: func(len: u64) -> result; + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + @since(version = 0.2.0) + blocking-skip: func(len: u64) -> result; + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + /// An output bytestream. + /// + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + /// + /// Dropping an `output-stream` while there's still an active write in + /// progress may result in the data being lost. Before dropping the stream, + /// be sure to fully flush your writes. + @since(version = 0.2.0) + resource output-stream { + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + @since(version = 0.2.0) + check-write: func() -> result; + /// Perform a write. This function never blocks. + /// + /// When the destination of a `write` is binary data, the bytes from + /// `contents` are written verbatim. When the destination of a `write` is + /// known to the implementation to be text, the bytes of `contents` are + /// transcoded from UTF-8 into the encoding of the destination and then + /// written. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + @since(version = 0.2.0) + write: func(contents: list) -> result<_, stream-error>; + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write`, and `flush`, and is implemented with the + /// following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while !contents.is_empty() { + /// // Wait for the stream to become writable + /// pollable.block(); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, contents.len()); + /// let (chunk, rest) = contents.split_at(len); + /// this.write(chunk ); // eliding error handling + /// contents = rest; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// pollable.block(); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + @since(version = 0.2.0) + blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + @since(version = 0.2.0) + flush: func() -> result<_, stream-error>; + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + @since(version = 0.2.0) + blocking-flush: func() -> result<_, stream-error>; + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occurred. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Write zeroes to a stream. + /// + /// This should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + @since(version = 0.2.0) + write-zeroes: func(len: u64) -> result<_, stream-error>; + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with + /// the following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while num_zeroes != 0 { + /// // Wait for the stream to become writable + /// pollable.block(); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, num_zeroes); + /// this.write-zeroes(len); // eliding error handling + /// num_zeroes -= len; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// pollable.block(); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + @since(version = 0.2.0) + blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; + /// Read from one stream and write to another. + /// + /// The behavior of splice is equivalent to: + /// 1. calling `check-write` on the `output-stream` + /// 2. calling `read` on the `input-stream` with the smaller of the + /// `check-write` permitted length and the `len` provided to `splice` + /// 3. calling `write` on the `output-stream` with that read data. + /// + /// Any error reported by the call to `check-write`, `read`, or + /// `write` ends the splice and reports that error. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + @since(version = 0.2.0) + splice: func(src: borrow, len: u64) -> result; + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until the + /// `output-stream` is ready for writing, and the `input-stream` + /// is ready for reading, before performing the `splice`. + @since(version = 0.2.0) + blocking-splice: func(src: borrow, len: u64) -> result; + } +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import error; + @since(version = 0.2.0) + import poll; + @since(version = 0.2.0) + import streams; +} diff --git a/crates/debugger/wit/deps/random.wit b/crates/debugger/wit/deps/random.wit new file mode 100644 index 000000000000..73edf5b60e06 --- /dev/null +++ b/crates/debugger/wit/deps/random.wit @@ -0,0 +1,92 @@ +package wasi:random@0.2.6; + +/// The insecure-seed interface for seeding hash-map DoS resistance. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface insecure-seed { + /// Return a 128-bit value that may contain a pseudo-random value. + /// + /// The returned value is not required to be computed from a CSPRNG, and may + /// even be entirely deterministic. Host implementations are encouraged to + /// provide pseudo-random values to any program exposed to + /// attacker-controlled content, to enable DoS protection built into many + /// languages' hash-map implementations. + /// + /// This function is intended to only be called once, by a source language + /// to initialize Denial Of Service (DoS) protection in its hash-map + /// implementation. + /// + /// # Expected future evolution + /// + /// This will likely be changed to a value import, to prevent it from being + /// called multiple times and potentially used for purposes other than DoS + /// protection. + @since(version = 0.2.0) + insecure-seed: func() -> tuple; +} + +/// The insecure interface for insecure pseudo-random numbers. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface insecure { + /// Return `len` insecure pseudo-random bytes. + /// + /// This function is not cryptographically secure. Do not use it for + /// anything related to security. + /// + /// There are no requirements on the values of the returned bytes, however + /// implementations are encouraged to return evenly distributed values with + /// a long period. + @since(version = 0.2.0) + get-insecure-random-bytes: func(len: u64) -> list; + + /// Return an insecure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-insecure-random-bytes`, represented as a `u64`. + @since(version = 0.2.0) + get-insecure-random-u64: func() -> u64; +} + +/// WASI Random is a random data API. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface random { + /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// + /// This function must produce data at least as cryptographically secure and + /// fast as an adequately seeded cryptographically-secure pseudo-random + /// number generator (CSPRNG). It must not block, from the perspective of + /// the calling program, under any circumstances, including on the first + /// request and on requests for numbers of bytes. The returned data must + /// always be unpredictable. + /// + /// This function must always return fresh data. Deterministic environments + /// must omit this function, rather than implementing it with deterministic + /// data. + @since(version = 0.2.0) + get-random-bytes: func(len: u64) -> list; + + /// Return a cryptographically-secure random or pseudo-random `u64` value. + /// + /// This function returns the same type of data as `get-random-bytes`, + /// represented as a `u64`. + @since(version = 0.2.0) + get-random-u64: func() -> u64; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import random; + @since(version = 0.2.0) + import insecure; + @since(version = 0.2.0) + import insecure-seed; +} diff --git a/crates/debugger/wit/deps/sockets.wit b/crates/debugger/wit/deps/sockets.wit new file mode 100644 index 000000000000..db6d1a23b40e --- /dev/null +++ b/crates/debugger/wit/deps/sockets.wit @@ -0,0 +1,949 @@ +package wasi:sockets@0.2.6; + +@since(version = 0.2.0) +interface network { + @unstable(feature = network-error-code) + use wasi:io/error@0.2.6.{error}; + + /// An opaque resource that represents access to (a subset of) the network. + /// This enables context-based security for networking. + /// There is no need for this to map 1:1 to a physical network interface. + @since(version = 0.2.0) + resource network; + + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// - `concurrency-conflict` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + @since(version = 0.2.0) + enum error-code { + /// Unknown error + unknown, + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + not-supported, + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + invalid-argument, + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, + /// The operation timed out before it could finish completely. + timeout, + /// This operation is incompatible with another asynchronous operation that is already in progress. + /// + /// POSIX equivalent: EALREADY + concurrency-conflict, + /// Trying to finish an asynchronous operation that: + /// - has not been started yet, or: + /// - was already finished by a previous `finish-*` call. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + not-in-progress, + /// The operation has been aborted because it could not be completed immediately. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + would-block, + /// The operation is not valid in the socket's current state. + invalid-state, + /// A new socket resource could not be created because of a system limit. + new-socket-limit, + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + address-not-bindable, + /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + address-in-use, + /// The remote address is not reachable + remote-unreachable, + /// The TCP connection was forcefully rejected + connection-refused, + /// The TCP connection was reset. + connection-reset, + /// A TCP connection was aborted. + connection-aborted, + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. + datagram-too-large, + /// Name does not exist or has no suitable associated IP addresses. + name-unresolvable, + /// A temporary failure in name resolution occurred. + temporary-resolver-failure, + /// A permanent failure in name resolution occurred. + permanent-resolver-failure, + } + + @since(version = 0.2.0) + enum ip-address-family { + /// Similar to `AF_INET` in POSIX. + ipv4, + /// Similar to `AF_INET6` in POSIX. + ipv6, + } + + @since(version = 0.2.0) + type ipv4-address = tuple; + + @since(version = 0.2.0) + type ipv6-address = tuple; + + @since(version = 0.2.0) + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + @since(version = 0.2.0) + record ipv4-socket-address { + /// sin_port + port: u16, + /// sin_addr + address: ipv4-address, + } + + @since(version = 0.2.0) + record ipv6-socket-address { + /// sin6_port + port: u16, + /// sin6_flowinfo + flow-info: u32, + /// sin6_addr + address: ipv6-address, + /// sin6_scope_id + scope-id: u32, + } + + @since(version = 0.2.0) + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } + + /// Attempts to extract a network-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// network-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are network-related errors. + @unstable(feature = network-error-code) + network-error-code: func(err: borrow) -> option; +} + +/// This interface provides a value-export of the default network handle.. +@since(version = 0.2.0) +interface instance-network { + @since(version = 0.2.0) + use network.{network}; + + /// Get a handle to the default network. + @since(version = 0.2.0) + instance-network: func() -> network; +} + +@since(version = 0.2.0) +interface ip-name-lookup { + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + @since(version = 0.2.0) + use network.{network, error-code, ip-address}; + + @since(version = 0.2.0) + resource resolve-address-stream { + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + @since(version = 0.2.0) + resolve-next-address: func() -> result, error-code>; + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + /// Resolve an internet host name to a list of IP addresses. + /// + /// Unicode domain names are automatically converted to ASCII using IDNA encoding. + /// If the input is an IP address string, the address is parsed and returned + /// as-is without making any external requests. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// This function never blocks. It either immediately fails or immediately + /// returns successfully with a `resolve-address-stream` that can be used + /// to (asynchronously) fetch the results. + /// + /// # Typical errors + /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + resolve-addresses: func(network: borrow, name: string) -> result; +} + +@since(version = 0.2.0) +interface tcp { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream, output-stream}; + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + @since(version = 0.2.0) + use wasi:clocks/monotonic-clock@0.2.6.{duration}; + @since(version = 0.2.0) + use network.{network, error-code, ip-socket-address, ip-address-family}; + + @since(version = 0.2.0) + enum shutdown-type { + /// Similar to `SHUT_RD` in POSIX. + receive, + /// Similar to `SHUT_WR` in POSIX. + send, + /// Similar to `SHUT_RDWR` in POSIX. + both, + } + + /// A TCP socket resource. + /// + /// The socket can be in one of the following states: + /// - `unbound` + /// - `bind-in-progress` + /// - `bound` (See note below) + /// - `listen-in-progress` + /// - `listening` + /// - `connect-in-progress` + /// - `connected` + /// - `closed` + /// See + /// for more information. + /// + /// Note: Except where explicitly mentioned, whenever this documentation uses + /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. + /// (i.e. `bound`, `listen-in-progress`, `listening`, `connect-in-progress` or `connected`) + /// + /// In addition to the general error codes documented on the + /// `network::error-code` type, TCP socket methods may always return + /// `error(invalid-state)` when in the `closed` state. + @since(version = 0.2.0) + resource tcp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// Bind can be attempted multiple times on the same socket, even with + /// different arguments on each iteration. But never concurrently and + /// only as long as the previous bind failed. Once a bind succeeds, the + /// binding can't be changed anymore. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT + /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR + /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior + /// and SO_REUSEADDR performs something different entirely. + /// + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-bind: func() -> result<_, error-code>; + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the `connected` state. + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// After a failed connection attempt, the socket will be in the `closed` + /// state and the only valid action left is to `drop` the socket. A single + /// socket can not be used to connect more than once. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN) + /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows) + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A connect operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// The POSIX equivalent of `start-connect` is the regular `connect` syscall. + /// Because all WASI sockets are non-blocking this is expected to return + /// EINPROGRESS, which should be translated to `ok()` in WASI. + /// + /// The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT` + /// with a timeout of 0 on the socket descriptor. Followed by a check for + /// the `SO_ERROR` socket option, in case the poll signaled readiness. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-connect: func() -> result, error-code>; + /// Start listening for new connections. + /// + /// Transitions the socket into the `listening` state. + /// + /// Unlike POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the `listening` state. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A listen operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the listen operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `listen` as part of either `start-listen` or `finish-listen`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-listen: func() -> result<_, error-code>; + @since(version = 0.2.0) + finish-listen: func() -> result<_, error-code>; + /// Accept a new client socket. + /// + /// The returned socket is bound and in the `connected` state. The following properties are inherited from the listener socket: + /// - `address-family` + /// - `keep-alive-enabled` + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// - `hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `invalid-state`: Socket is not in the `listening` state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + accept: func() -> result, error-code>; + /// Get the bound local address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + local-address: func() -> result; + /// Get the remote address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + remote-address: func() -> result; + /// Whether the socket is in the `listening` state. + /// + /// Equivalent to the SO_ACCEPTCONN socket option. + @since(version = 0.2.0) + is-listening: func() -> bool; + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.2.0) + address-family: func() -> ip-address-family; + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// + /// # Typical errors + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-argument`: (set) The provided value was 0. + /// - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected` state. + @since(version = 0.2.0) + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + /// Enables or disables keepalive. + /// + /// The keepalive behavior can be adjusted using: + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// + /// Equivalent to the SO_KEEPALIVE socket option. + @since(version = 0.2.0) + keep-alive-enabled: func() -> result; + @since(version = 0.2.0) + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; + /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-idle-time: func() -> result; + @since(version = 0.2.0) + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; + /// The time between keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPINTVL socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-interval: func() -> result; + @since(version = 0.2.0) + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; + /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPCNT socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-count: func() -> result; + @since(version = 0.2.0) + set-keep-alive-count: func(value: u32) -> result<_, error-code>; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.2.0) + hop-limit: func() -> result; + @since(version = 0.2.0) + set-hop-limit: func(value: u8) -> result<_, error-code>; + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + receive-buffer-size: func() -> result; + @since(version = 0.2.0) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.2.0) + send-buffer-size: func() -> result; + @since(version = 0.2.0) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + /// Create a `pollable` which can be used to poll for, or block on, + /// completion of any of the asynchronous operations of this socket. + /// + /// When `finish-bind`, `finish-listen`, `finish-connect` or `accept` + /// return `error(would-block)`, this pollable can be used to wait for + /// their success or failure, after which the method can be retried. + /// + /// The pollable is not limited to the async operation that happens to be + /// in progress at the time of calling `subscribe` (if any). Theoretically, + /// `subscribe` only has to be called once per socket and can then be + /// (re)used for the remainder of the socket's lifetime. + /// + /// See + /// for more information. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Initiate a graceful shutdown. + /// + /// - `receive`: The socket is not expecting to receive any data from + /// the peer. The `input-stream` associated with this socket will be + /// closed. Any data still in the receive queue at time of calling + /// this method will be discarded. + /// - `send`: The socket has no more data to send to the peer. The `output-stream` + /// associated with this socket will be closed and a FIN packet will be sent. + /// - `both`: Same effect as `receive` & `send` combined. + /// + /// This function is idempotent; shutting down a direction more than once + /// has no effect and returns `ok`. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; + } +} + +@since(version = 0.2.0) +interface tcp-create-socket { + @since(version = 0.2.0) + use network.{network, error-code, ip-address-family}; + @since(version = 0.2.0) + use tcp.{tcp-socket}; + + /// Create a new TCP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` + /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + create-tcp-socket: func(address-family: ip-address-family) -> result; +} + +@since(version = 0.2.0) +interface udp { + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + @since(version = 0.2.0) + use network.{network, error-code, ip-socket-address, ip-address-family}; + + /// A received datagram. + @since(version = 0.2.0) + record incoming-datagram { + /// The payload. + /// + /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + data: list, + /// The source address. + /// + /// This field is guaranteed to match the remote address the stream was initialized with, if any. + /// + /// Equivalent to the `src_addr` out parameter of `recvfrom`. + remote-address: ip-socket-address, + } + + /// A datagram to be sent out. + @since(version = 0.2.0) + record outgoing-datagram { + /// The payload. + data: list, + /// The destination address. + /// + /// The requirements on this field depend on how the stream was initialized: + /// - with a remote address: this field must be None or match the stream's remote address exactly. + /// - without a remote address: this field is required. + /// + /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`. + remote-address: option, + } + + /// A UDP socket handle. + @since(version = 0.2.0) + resource udp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the port is zero, the socket will be bound to a random free port. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-bind: func() -> result<_, error-code>; + /// Set up inbound & outbound communication channels, optionally to a specific peer. + /// + /// This function only changes the local socket configuration and does not generate any network traffic. + /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well, + /// based on the best network path to `remote-address`. + /// + /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer: + /// - `send` can only be used to send to this destination. + /// - `receive` will only return datagrams sent from the provided `remote-address`. + /// + /// This method may be called multiple times on the same socket to change its association, but + /// only the most recently returned pair of streams will be operational. Implementations may trap if + /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again. + /// + /// The POSIX equivalent in pseudo-code is: + /// ```text + /// if (was previously connected) { + /// connect(s, AF_UNSPEC) + /// } + /// if (remote_address is Some) { + /// connect(s, remote_address) + /// } + /// ``` + /// + /// Unlike in POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-state`: The socket is not bound. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + %stream: func(remote-address: option) -> result, error-code>; + /// Get the current bound address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + local-address: func() -> result; + /// Get the address the socket is currently streaming to. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + remote-address: func() -> result; + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.2.0) + address-family: func() -> ip-address-family; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.2.0) + unicast-hop-limit: func() -> result; + @since(version = 0.2.0) + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + receive-buffer-size: func() -> result; + @since(version = 0.2.0) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.2.0) + send-buffer-size: func() -> result; + @since(version = 0.2.0) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + @since(version = 0.2.0) + resource incoming-datagram-stream { + /// Receive messages on the socket. + /// + /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. + /// The returned list may contain fewer elements than requested, but never more. + /// + /// This function returns successfully with an empty list when either: + /// - `max-results` is 0, or: + /// - `max-results` is greater than 0, but no results are immediately available. + /// This function never returns `error(would-block)`. + /// + /// # Typical errors + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + receive: func(max-results: u64) -> result, error-code>; + /// Create a `pollable` which will resolve once the stream is ready to receive again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + @since(version = 0.2.0) + resource outgoing-datagram-stream { + /// Check readiness for sending. This function never blocks. + /// + /// Returns the number of datagrams permitted for the next call to `send`, + /// or an error. Calling `send` with more datagrams than this function has + /// permitted will trap. + /// + /// When this function returns ok(0), the `subscribe` pollable will + /// become ready when this function will report at least ok(1), or an + /// error. + /// + /// Never returns `would-block`. + check-send: func() -> result; + /// Send messages on the socket. + /// + /// This function attempts to send all provided `datagrams` on the socket without blocking and + /// returns how many messages were actually sent (or queued for sending). This function never + /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned. + /// + /// This function semantically behaves the same as iterating the `datagrams` list and sequentially + /// sending each individual datagram until either the end of the list has been reached or the first error occurred. + /// If at least one datagram has been sent successfully, this function never returns an error. + /// + /// If the input list is empty, the function returns `ok(0)`. + /// + /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if + /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) + /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + send: func(datagrams: list) -> result; + /// Create a `pollable` which will resolve once the stream is ready to send again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } +} + +@since(version = 0.2.0) +interface udp-create-socket { + @since(version = 0.2.0) + use network.{network, error-code, ip-address-family}; + @since(version = 0.2.0) + use udp.{udp-socket}; + + /// Create a new UDP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, + /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + create-udp-socket: func(address-family: ip-address-family) -> result; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import network; + @since(version = 0.2.0) + import instance-network; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import udp; + @since(version = 0.2.0) + import udp-create-socket; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import tcp; + @since(version = 0.2.0) + import tcp-create-socket; + @since(version = 0.2.0) + import ip-name-lookup; +} diff --git a/crates/debugger/wit/world.wit b/crates/debugger/wit/world.wit new file mode 100644 index 000000000000..e3c602ce2612 --- /dev/null +++ b/crates/debugger/wit/world.wit @@ -0,0 +1,400 @@ +package bytecodealliance:wasmtime@44.0.0; + +world debug-main { + import wasi:io/poll@0.2.6; + import debuggee; + + /// Print an informational message from the debugger (e.g. "listening + /// on port 1234") to the user. The host decides how to display this. + import print-debugger-info: func(message: string); + + export debugger; +} + +interface debugger { + use debuggee.{debuggee}; + + /// Launch the debugger top-half (e.g., protocol server or UI) + /// implemented by this component, given the provided interface + /// to a debuggee. The debuggee will be paused and awaiting + /// a resumption. + /// + /// `args` contains the command-line arguments of the debugger, + /// starting with the program name (args[0]). + debug: func(d: borrow, args: list); +} + +interface debuggee { + use wasi:io/poll@0.2.6.{pollable}; + + /// A debuggee consisting of one Store over which we have + /// debugging control. + /// + /// A debuggee is always either "paused" or "running", and certain + /// methods below are only available in one or the other state. + resource debuggee { + /// List all modules that exist in the Store. + /// + /// If invoked while already running, causes a trap. + all-modules: func() -> list; + + /// List of all instances that exist in the Store. + /// + /// If invoked while already running, causes a trap. + all-instances: func() -> list; + + /// Force an interruption event in any running code. + /// + /// Usable in paused or running states. If invoked in + /// the paused state, the next execution will immediately + /// yield an "interrupted" event. + interrupt: func(); + + /// Single-step, returning a future that will yield an event + /// when the single-step execution is complete. + /// + /// The `resumption` value indicates any mutations to + /// perform to execution state as part of resuming: + /// for example, injecting a call to another function, + /// or throwing an exception, or forcing an early return + /// from the current function. + /// + /// Usable in paused state; transitions to running state. + /// Debuggee remains in running state until the returned + /// future completes. + /// + /// If invoked while already running, causes a trap. + single-step: func(resumption: resumption-value) -> event-future; + + /// Continue execution, returning a future that will + /// yield an event when the resumed execution + /// is complete. + /// + /// The `resumption` value indicates any mutations to + /// perform to execution state as part of resuming: + /// for example, injecting a call to another function, + /// or throwing an exception, or forcing an early return + /// from the current function. + /// + /// Usable in paused state; transitions to running state. + /// Debuggee remains in running state until the returned + /// future completes. + /// + /// If invoked while already running, causes a trap. + continue: func(resumption: resumption-value) -> event-future; + + /// Get the current exit frames for each activation. + /// + /// If invoked while already running, causes a trap. + exit-frames: func() -> list; + } + + /// A future that represents asynchronous execution of the + /// debuggee until the next debug event pausing execution. + /// + /// When complete, yields the resulting debug event. + resource event-future { + /// Subscribe to this future, returning a wasi-io pollable. + subscribe: func() -> pollable; + + /// Consume this future, producing the resulting + /// event. Blocks if not ready. The debuggee + /// must be provided as well. + finish: static func(self: event-future, debuggee: borrow) -> result; + } + + /// A `resumption` value indicates how we want to continue + /// execution after a pause. + /// + /// By default, if we are a "read-only" debugger, there is only + /// one answer: with no mutation, according to the abstract + /// Wasm machine semantics and current machine state. + /// + /// However, this interface also supports various kinds of mutations. + /// For example, the debugger can ask to insert a call to an arbitrary + /// Wasm function, it can force an early return from the current function, + /// or it can throw an exception. + /// + /// This `resumption` value is orthogonal to the + /// `continue` vs. `single-step` resume axis: + /// the resumption value indicates whether we want to mutate + /// the executing abstract machine's *state*, while the single-step + /// vs. continue axis indicates how we want to step that machine + /// forward from whatever state is indicated (i.e., one step or + /// many until an event). + variant resumption-value { + /// Resume normally: do not alter the machine state. + normal, + /// Inject a call to a Wasm function at the current point, as-if + /// the program contained a `call` instruction with the given + /// values on the operand stack. + /// + /// Execution is subject to ordinary debugger events as with + /// any other; e.g., the function we call may experience breakpoint + /// pauses, may be single-stepped if we resume with `single-step`, + /// etc. + /// + /// If the injected function call completes normally, an + /// `injected-call-return` debug event is raised with the + /// return value(s). + inject-call(inject-call), + /// Resume as-if an exception were thrown at the current point. + throw-exception(wasm-exception), + /// Force a return from the current function frame, with the given + /// value(s) as return value(s). + early-return(list), + } + + /// A function call to be injected upon resumption. + record inject-call { + callee: wasm-func, + arguments: list, + } + + /// A debug event. + variant event { + /// Execution of the debuggee has completed. + complete, + /// A trap occurred in the debuggee. Execution + /// is paused at the trap-point, and will terminate + /// when resuming execution. + trap, + /// A breakpoint was hit in the debuggee, pausing + /// execution. + breakpoint, + /// An interruption due to `debuggee.interrupt` occurred. + interrupted, + /// An exception was thrown and caught by Wasm. + caught-exception-thrown(wasm-exception), + /// An exception was thrown and not caught by Wasm. + uncaught-exception-thrown(wasm-exception), + /// An injected call completed with return value(s). + injected-call-return(list), + } + + resource instance { + /// Get this instance's module. + get-module: func(d: borrow) -> module; + + /// Get a memory from this instance's memory index space. + get-memory: func(d: borrow, memory-index: u32) -> result; + + /// Get a global from this instance's global index space. + get-global: func(d: borrow, global-index: u32) -> result; + + /// Get a table from this instance's table index space. + get-table: func(d: borrow, table-index: u32) -> result; + + /// Get a function from this instance's function index space. + get-func: func(d: borrow, func-index: u32) -> result; + + /// Get a tag from this instance's tag index space. + get-tag: func(d: borrow, tag-index: u32) -> result; + + /// Clone this handle. + clone: func() -> instance; + + /// Get the unique ID of this instance (within the debuggee) + /// to allow equality and hashing. + unique-id: func() -> u64; + } + + resource module { + /// Get the original Wasm bytecode for this module, if available. + bytecode: func() -> option>; + + /// Add a breakpoint. + add-breakpoint: func(d: borrow, pc: u32) -> result<_, error>; + + /// Remove a breakpoint. + remove-breakpoint: func(d: borrow, pc: u32) -> result<_, error>; + + /// Clone this handle. + clone: func() -> module; + + /// Get the unique ID of this module to allow equality and hashing. + unique-id: func() -> u64; + } + + resource memory { + /// Get the current memory size, in bytes. + size-bytes: func(d: borrow) -> u64; + + /// Get the page size, in bytes. + page-size-bytes: func(d: borrow) -> u64; + + /// Increase size by the given `delta`. Returns the old size in bytes. + grow-to-bytes: func(d: borrow, delta: u64) -> result; + + /// Read `len` bytes starting at `addr`. Returns `out-of-bounds` if any + /// byte in the range is out-of-bounds. + get-bytes: func(d: borrow, addr: u64, len: u64) -> result, error>; + + /// Write `bytes` starting at `addr`. Returns `out-of-bounds` if any + /// byte in the range is out-of-bounds. + set-bytes: func(d: borrow, addr: u64, bytes: list) -> result<_, error>; + + /// Get a u8 (byte) at an address. Returns `none` if out-of-bounds. + get-u8: func(d: borrow, addr: u64) -> result; + /// Get a u16 (in little endian order) at an address. + get-u16: func(d: borrow, addr: u64) -> result; + /// Get a u32 (in little endian order) at an address. + get-u32: func(d: borrow, addr: u64) -> result; + /// Get a u64 (in little endian order) at an address. + get-u64: func(d: borrow, addr: u64) -> result; + + /// Set a u8 (byte) at an address. Returns `none` if out-of-bounds. + set-u8: func(d: borrow, addr: u64, value: u8) -> result<_, error>; + /// Set a u16 (in little endian order) at an address. + set-u16: func(d: borrow, addr: u64, value: u16) -> result<_, error>; + /// Set a u32 (in little endian order) at an address. + set-u32: func(d: borrow, addr: u64, value: u32) -> result<_, error>; + /// Set a u64 (in little endian order) at an address. + set-u64: func(d: borrow, addr: u64, value: u64) -> result<_, error>; + + /// Clone this handle. + clone: func() -> memory; + + /// Get the unique ID of this memory to allow equality and hashing. + unique-id: func() -> u64; + } + + resource global { + /// Get the value of this global. + get: func(d: borrow) -> result; + + /// Set the value of this global. + set: func(d: borrow, val: wasm-value) -> result<_, error>; + + /// Clone this handle. + clone: func() -> global; + + /// Get the unique ID of this memory to allow equality and hashing. + unique-id: func() -> u64; + } + + resource table { + /// Get the current length of this table, in elements. + len: func(d: borrow) -> u64; + + /// Get the value at the Nth slot. + get-element: func(d: borrow, index: u64) -> result; + + /// Set the value at the Nth slot. + set-element: func(d: borrow, index: u64, val: wasm-value) -> result<_, error>; + + /// Clone this handle. + clone: func() -> table; + + /// Get the unique ID of this memory to allow equality and hashing. + unique-id: func() -> u64; + } + + resource wasm-func { + /// Get the parameter types. + params: func(d: borrow) -> result, error>; + + /// Get the result types. + results: func(d: borrow) -> result, error>; + + /// Clone this handle. + clone: func() -> wasm-func; + } + + resource wasm-exception { + /// Get the tag of this exception. + get-tag: func(d: borrow) -> wasm-tag; + + /// Get the payload values of this exception. + get-values: func(d: borrow) -> result, error>; + + /// Clone this reference. + clone: func(d: borrow) -> wasm-exception; + + /// Allocate a new exception. + make: static func( + d: borrow, + tag: borrow, + values: list + ) -> result; + } + + resource wasm-tag { + /// Get the parameter types. + params: func(d: borrow) -> result, error>; + + /// Get the unique ID of this tag to allow equality and hashing. + unique-id: func() -> u64; + + /// Clone this handle. + clone: func() -> wasm-tag; + + /// Allocate a new tag. + make: static func(d: borrow, params: list) -> wasm-tag; + } + + resource frame { + /// Instance of this frame. + get-instance: func(d: borrow) -> result; + + /// Function index in this frame's instance. + get-func-index: func(d: borrow) -> result; + + /// Current PC in this frame's instance. + get-pc: func(d: borrow) -> result; + + /// Wasm locals. + get-locals: func(d: borrow) -> result, error>; + + /// Operand stack. + get-stack: func(d: borrow) -> result, error>; + + /// parent frame (the one that called this frame), if any. + parent-frame: func(d: borrow) -> result, error>; + } + + enum error { + invalid-entity, + invalid-pc, + invalid-frame, + unsupported-type, + mismatched-type, + non-wasm-frame, + alloc-failure, + breakpoint-update, + read-only, + out-of-bounds, + memory-grow-failure, + execution-trap, + } + + resource wasm-value { + get-type: func() -> wasm-type; + unwrap-i32: func() -> u32; + unwrap-i64: func() -> u64; + unwrap-f32: func() -> f32; + unwrap-f64: func() -> f64; + unwrap-v128: func() -> list; + unwrap-func: func() -> option; + unwrap-exception: func() -> option; + + make-i32: static func(value: u32) -> wasm-value; + make-i64: static func(value: u64) -> wasm-value; + make-f32: static func(value: f32) -> wasm-value; + make-f64: static func(value: f64) -> wasm-value; + make-v128: static func(value: list) -> wasm-value; + + clone: func() -> wasm-value; + } + + variant wasm-type { + wasm-i32, + wasm-i64, + wasm-f32, + wasm-f64, + wasm-v128, + wasm-funcref, + wasm-exnref, + // TODO: GC structs and arrays + } +} diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index 9a92d5fdffd6..d2267ff8bb5b 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -20,7 +20,7 @@ all-features = true [dependencies] anyhow = { workspace = true } postcard = { workspace = true } -cpp_demangle = { version = "0.4.3", optional = true } +cpp_demangle = { version = "0.4.3", default-features = false, features = ['alloc'], optional = true } cranelift-entity = { workspace = true, features = ['enable-serde'] } cranelift-bitset = { workspace = true, features = ['enable-serde'] } cranelift-bforest = { workspace = true } @@ -32,12 +32,12 @@ serde_derive = { workspace = true } log = { workspace = true } gimli = { workspace = true } object = { workspace = true } -rustc-demangle = { version = "0.1.16", optional = true } +rustc-demangle = { version = "0.1.16", default-features = false, optional = true } target-lexicon = { workspace = true } wasm-encoder = { workspace = true, optional = true } wasmprinter = { workspace = true, optional = true } wasmtime-component-util = { workspace = true, optional = true } -wasmtime-core = { workspace = true } +wasmtime-core = { workspace = true, features = ['serde'] } semver = { workspace = true, optional = true, features = ['serde'] } smallvec = { workspace = true, features = ['serde'] } sha2 = { workspace = true } @@ -70,7 +70,7 @@ component-model = [ "dep:semver", "wasmparser/component-model", ] -demangle = ['std', 'dep:rustc-demangle', 'dep:cpp_demangle'] +demangle = ['dep:rustc-demangle', 'dep:cpp_demangle'] gc = [] gc-drc = ["gc"] gc-null = ["gc"] @@ -90,6 +90,8 @@ std = [ 'wasmparser/std', 'indexmap/std', 'wasmtime-core/std', + 'cpp_demangle?/std', + 'rustc-demangle?/std', ] rr = [ "component-model" diff --git a/crates/environ/examples/btrees.rs b/crates/environ/examples/btrees.rs index e93fb1812d65..90e9969cb5a9 100644 --- a/crates/environ/examples/btrees.rs +++ b/crates/environ/examples/btrees.rs @@ -18,9 +18,9 @@ impl Map for BTreeMap { } } -impl Map for collections::BTreeMap { +impl Map for collections::TryBTreeMap { fn insert(&mut self, key: Key, value: Value) -> Result<()> { - collections::BTreeMap::insert(self, key, value)?; + collections::TryBTreeMap::insert(self, key, value)?; Ok(()) } } @@ -32,7 +32,7 @@ fn main() -> Result<()> { let mut map: Box = match kind.as_str() { "std" => Box::new(BTreeMap::new()), - "bforest" => Box::new(collections::BTreeMap::new()), + "bforest" => Box::new(collections::TryBTreeMap::new()), _ => bail!("first argument must be either 'std' or 'bforest', got: '{kind}'"), }; diff --git a/crates/environ/src/address_map.rs b/crates/environ/src/address_map.rs index c9cafcaa8d60..8d1fd2d48bfd 100644 --- a/crates/environ/src/address_map.rs +++ b/crates/environ/src/address_map.rs @@ -1,6 +1,7 @@ //! Data structures to provide transformation of the source -use object::{Bytes, LittleEndian, U32Bytes}; +use core::fmt; +use object::{Bytes, LittleEndian, U32}; use serde_derive::{Deserialize, Serialize}; /// Single source location to generated address mapping. @@ -61,20 +62,97 @@ impl Default for FilePos { } } +/// A Wasm bytecode offset relative to the start of a component (or +/// top-level module) binary. +/// +/// When compiling a component, the Wasm parser returns source +/// positions relative to the entire component binary. This type +/// captures that convention. Use +/// [`ComponentPC::to_module_pc`] to convert to a +/// [`ModulePC`] given the byte offset of the module within the +/// component. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ComponentPC(u32); + +impl ComponentPC { + /// Create a new component-relative PC from a raw offset. + pub fn new(offset: u32) -> Self { + Self(offset) + } + + /// Get the raw u32 offset. + pub fn raw(self) -> u32 { + self.0 + } + + /// Convert to a module-relative PC by subtracting the byte offset + /// of the module within the component binary. + pub fn to_module_pc(self, wasm_module_offset: u64) -> ModulePC { + let offset = u32::try_from(wasm_module_offset).unwrap(); + ModulePC(self.0 - offset) + } +} + +impl fmt::Debug for ComponentPC { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ComponentPC({:#x})", self.0) + } +} + +impl fmt::Display for ComponentPC { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#x}", self.0) + } +} + +/// A Wasm bytecode offset relative to the start of a core Wasm +/// module binary. +/// +/// In the guest-debug system, PCs are always module-relative because +/// the debugger presents a core-Wasm view of the world where +/// components are deconstructed into individual core Wasm modules. +/// +/// For standalone (non-component) modules, `ModulePC` and +/// [`ComponentPC`] values are numerically identical. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ModulePC(u32); + +impl ModulePC { + /// Create a new module-relative PC from a raw offset. + pub fn new(offset: u32) -> Self { + Self(offset) + } + + /// Get the raw u32 offset. + pub fn raw(self) -> u32 { + self.0 + } +} + +impl fmt::Debug for ModulePC { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ModulePC({:#x})", self.0) + } +} + +impl fmt::Display for ModulePC { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#x}", self.0) + } +} + /// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets /// and the slice of associated file positions for each offset. -fn parse_address_map( - section: &[u8], -) -> Option<(&[U32Bytes], &[U32Bytes])> { +fn parse_address_map(section: &[u8]) -> Option<(&[U32], &[U32])> { let mut section = Bytes(section); // NB: this matches the encoding written by `append_to` in the // `compile::address_map` module. - let count = section.read::>().ok()?; + let count = section.read::>().ok()?; let count = usize::try_from(count.get(LittleEndian)).ok()?; let (offsets, section) = - object::slice_from_bytes::>(section.0, count).ok()?; + object::slice_from_bytes::>(section.0, count).ok()?; let (positions, section) = - object::slice_from_bytes::>(section, count).ok()?; + object::slice_from_bytes::>(section, count).ok()?; debug_assert!(section.is_empty()); Some((offsets, positions)) } diff --git a/crates/environ/src/collections.rs b/crates/environ/src/collections.rs index 0c9f3ef07636..be7e4cc36997 100644 --- a/crates/environ/src/collections.rs +++ b/crates/environ/src/collections.rs @@ -8,19 +8,19 @@ mod index_map; mod primary_map; mod secondary_map; -pub use btree_map::BTreeMap; -pub use entity_set::EntitySet; -pub use hash_map::HashMap; -pub use hash_set::HashSet; -pub use index_map::IndexMap; -pub use primary_map::PrimaryMap; -pub use secondary_map::SecondaryMap; +pub use btree_map::TryBTreeMap; +pub use entity_set::TryEntitySet; +pub use hash_map::TryHashMap; +pub use hash_set::TryHashSet; +pub use index_map::TryIndexMap; +pub use primary_map::TryPrimaryMap; +pub use secondary_map::TrySecondaryMap; pub use wasmtime_core::{ alloc::{ TryClone, TryCollect, TryCow, TryExtend, TryFromIterator, TryNew, TryString, TryToOwned, - Vec, try_new, + TryVec, try_new, }, - vec, + try_vec, }; /// Collections which abort on OOM. diff --git a/crates/environ/src/collections/btree_map.rs b/crates/environ/src/collections/btree_map.rs index 29b795ff4121..16c9c246b97d 100644 --- a/crates/environ/src/collections/btree_map.rs +++ b/crates/environ/src/collections/btree_map.rs @@ -1,4 +1,4 @@ -//! OOM-handling `BTreeMap` implementation. +//! OOM-handling `TryBTreeMap` implementation. use crate::error::OutOfMemory; use core::{mem, ops::RangeBounds, ptr::NonNull}; @@ -6,7 +6,7 @@ use wasmtime_core::slab::{Id, Slab}; /// Like `std::collections::BTreeMap` but its methods return errors on /// allocation failure. -pub struct BTreeMap +pub struct TryBTreeMap where K: Copy, { @@ -15,7 +15,7 @@ where map: cranelift_bforest::Map, } -impl Default for BTreeMap +impl Default for TryBTreeMap where K: Copy, { @@ -28,7 +28,7 @@ where } } -impl BTreeMap +impl TryBTreeMap where K: Copy, { @@ -162,7 +162,7 @@ where } /// Same as [`std::collections::BTreeMap::range`]. - pub fn range(&self, range: R) -> BTreeMapRange<'_, K, V, R> + pub fn range(&self, range: R) -> BTreeMapRange<'_, K, V> where K: Ord, R: RangeBounds, @@ -174,7 +174,7 @@ where } /// Same as [`std::collections::BTreeMap::range_mut`]. - pub fn range_mut(&mut self, range: R) -> BTreeMapRangeMut<'_, K, V, R> + pub fn range_mut(&mut self, range: R) -> BTreeMapRangeMut<'_, K, V> where K: Ord, R: RangeBounds, @@ -205,7 +205,7 @@ where } } -impl<'a, K, V> IntoIterator for &'a BTreeMap +impl<'a, K, V> IntoIterator for &'a TryBTreeMap where K: Copy, { @@ -217,7 +217,7 @@ where } } -/// An iterator over `(K, &V)` pairs returned by [`BTreeMap::iter`]. +/// An iterator over `(K, &V)` pairs returned by [`TryBTreeMap::iter`]. pub struct BTreeMapIter<'a, K, V> where K: Copy, @@ -242,7 +242,7 @@ where } } -impl<'a, K, V> IntoIterator for &'a mut BTreeMap +impl<'a, K, V> IntoIterator for &'a mut TryBTreeMap where K: Copy, { @@ -254,7 +254,7 @@ where } } -/// An iterator over `(K, &mut V)` pairs returned by [`BTreeMap::iter_mut`]. +/// An iterator over `(K, &mut V)` pairs returned by [`TryBTreeMap::iter_mut`]. pub struct BTreeMapIterMut<'a, K, V> where K: Copy, @@ -286,7 +286,7 @@ where } } -impl IntoIterator for BTreeMap +impl IntoIterator for TryBTreeMap where K: Copy + Ord, { @@ -301,7 +301,7 @@ where } } -/// An iterator over `(K, V)` pairs returned by [`BTreeMap::into_iter`]. +/// An iterator over `(K, V)` pairs returned by [`TryBTreeMap::into_iter`]. pub struct BTreeMapIntoIter where K: Copy, @@ -328,7 +328,7 @@ where } } -/// An iterator over keys returned by [`BTreeMap::keys`]. +/// An iterator over keys returned by [`TryBTreeMap::keys`]. pub struct BTreeMapKeys<'a, K, V> where K: Copy, @@ -348,7 +348,7 @@ where } } -/// An iterator over shared values returned by [`BTreeMap::values`]. +/// An iterator over shared values returned by [`TryBTreeMap::values`]. pub struct BTreeMapValues<'a, K, V> where K: Copy, @@ -368,7 +368,7 @@ where } } -/// An iterator over mutable values returned by [`BTreeMap::values_mut`]. +/// An iterator over mutable values returned by [`TryBTreeMap::values_mut`]. pub struct BTreeMapValuesMut<'a, K, V> where K: Copy, @@ -388,20 +388,18 @@ where } } -/// A range iterator of `(K, &'a V)` items returned by [`BTreeMap::range`]. -pub struct BTreeMapRange<'a, K, V, R> +/// A range iterator of `(K, &'a V)` items returned by [`TryBTreeMap::range`]. +pub struct BTreeMapRange<'a, K, V> where K: Copy + Ord, - R: RangeBounds, { - inner: cranelift_bforest::MapRange<'a, K, Id, R, ()>, + inner: cranelift_bforest::MapRange<'a, K, Id, ()>, values: &'a Slab, } -impl<'a, K, V, R> Iterator for BTreeMapRange<'a, K, V, R> +impl<'a, K, V> Iterator for BTreeMapRange<'a, K, V> where K: Copy + Ord, - R: RangeBounds, { type Item = (K, &'a V); @@ -411,20 +409,28 @@ where } } -/// A range iterator of `(K, &'a V)` items returned by [`BTreeMap::range`]. -pub struct BTreeMapRangeMut<'a, K, V, R> +impl<'a, K, V> DoubleEndedIterator for BTreeMapRange<'a, K, V> where K: Copy + Ord, - R: RangeBounds, { - inner: cranelift_bforest::MapRange<'a, K, Id, R, ()>, + fn next_back(&mut self) -> Option { + let (key, id) = self.inner.next_back()?; + Some((key, &self.values[id])) + } +} + +/// A range iterator of `(K, &'a V)` items returned by [`TryBTreeMap::range`]. +pub struct BTreeMapRangeMut<'a, K, V> +where + K: Copy + Ord, +{ + inner: cranelift_bforest::MapRange<'a, K, Id, ()>, values: &'a mut Slab, } -impl<'a, K, V, R> Iterator for BTreeMapRangeMut<'a, K, V, R> +impl<'a, K, V> Iterator for BTreeMapRangeMut<'a, K, V> where K: Copy + Ord, - R: RangeBounds, { type Item = (K, &'a mut V); @@ -651,7 +657,7 @@ mod tests { use crate::error::Result; use alloc::{vec, vec::Vec}; - type M = BTreeMap; + type M = TryBTreeMap; #[test] fn new() -> Result<()> { diff --git a/crates/environ/src/collections/entity_set.rs b/crates/environ/src/collections/entity_set.rs index d4bc95647c0e..af4a2d03efe5 100644 --- a/crates/environ/src/collections/entity_set.rs +++ b/crates/environ/src/collections/entity_set.rs @@ -4,20 +4,20 @@ use wasmtime_core::error::OutOfMemory; /// Like `cranelift_entity::EntitySet` but enforces fallible allocation for all /// methods that allocate. #[derive(Debug, Default)] -pub struct EntitySet +pub struct TryEntitySet where K: EntityRef, { inner: cranelift_entity::EntitySet, } -impl EntitySet +impl TryEntitySet where K: EntityRef, { /// Create a new empty set. pub fn new() -> Self { - EntitySet { + TryEntitySet { inner: Default::default(), } } diff --git a/crates/environ/src/collections/hash_map.rs b/crates/environ/src/collections/hash_map.rs index 53fe10efdec4..d83d67d966c8 100644 --- a/crates/environ/src/collections/hash_map.rs +++ b/crates/environ/src/collections/hash_map.rs @@ -15,11 +15,11 @@ use wasmtime_core::alloc::TryClone; /// A wrapper type around [`hashbrown::hash_map::HashMap`] that only exposes /// fallible allocation. -pub struct HashMap { +pub struct TryHashMap { inner: inner::HashMap, } -impl TryClone for HashMap +impl TryClone for TryHashMap where K: Eq + Hash + TryClone, V: TryClone, @@ -35,7 +35,7 @@ where } } -impl Default for HashMap +impl Default for TryHashMap where S: Default, { @@ -46,7 +46,7 @@ where } } -impl PartialEq for HashMap +impl PartialEq for TryHashMap where K: Eq + Hash, V: PartialEq, @@ -57,7 +57,7 @@ where } } -impl Eq for HashMap +impl Eq for TryHashMap where K: Eq + Hash, V: Eq, @@ -65,7 +65,7 @@ where { } -impl fmt::Debug for HashMap +impl fmt::Debug for TryHashMap where K: fmt::Debug, V: fmt::Debug, @@ -75,7 +75,7 @@ where } } -impl HashMap { +impl TryHashMap { /// Same as [`hashbrown::hash_map::HashMap::new`]. pub fn new() -> Self { Self { @@ -84,7 +84,7 @@ impl HashMap { } } -impl HashMap +impl TryHashMap where K: Eq + Hash, { @@ -97,7 +97,7 @@ where } } -impl HashMap { +impl TryHashMap { /// Same as [`hashbrown::hash_map::HashMap::with_hasher`]. pub const fn with_hasher(hasher: S) -> Self { Self { @@ -162,7 +162,7 @@ impl HashMap { } } -impl HashMap +impl TryHashMap where K: Eq + Hash, S: BuildHasher, @@ -223,7 +223,7 @@ where } } -impl<'a, K, V, S> IntoIterator for &'a HashMap { +impl<'a, K, V, S> IntoIterator for &'a TryHashMap { type Item = (&'a K, &'a V); type IntoIter = inner::Iter<'a, K, V>; @@ -233,7 +233,7 @@ impl<'a, K, V, S> IntoIterator for &'a HashMap { } } -impl<'a, K, V, S> IntoIterator for &'a mut HashMap { +impl<'a, K, V, S> IntoIterator for &'a mut TryHashMap { type Item = (&'a K, &'a mut V); type IntoIter = inner::IterMut<'a, K, V>; @@ -243,7 +243,7 @@ impl<'a, K, V, S> IntoIterator for &'a mut HashMap { } } -impl IntoIterator for HashMap { +impl IntoIterator for TryHashMap { type Item = (K, V); type IntoIter = inner::IntoIter; @@ -252,3 +252,9 @@ impl IntoIterator for HashMap { self.inner.into_iter() } } + +impl From> for inner::HashMap { + fn from(map: TryHashMap) -> Self { + map.inner + } +} diff --git a/crates/environ/src/collections/hash_set.rs b/crates/environ/src/collections/hash_set.rs index 9dc28065352b..4593ffecff5d 100644 --- a/crates/environ/src/collections/hash_set.rs +++ b/crates/environ/src/collections/hash_set.rs @@ -13,11 +13,11 @@ use hashbrown::{DefaultHashBuilder, hash_set as inner}; /// A wrapper type around [`hashbrown::hash_set::HashSet`] that only exposes /// fallible allocation. -pub struct HashSet { +pub struct TryHashSet { inner: inner::HashSet, } -impl Default for HashSet +impl Default for TryHashSet where S: Default, { @@ -28,7 +28,7 @@ where } } -impl PartialEq for HashSet +impl PartialEq for TryHashSet where T: Eq + Hash, S: BuildHasher, @@ -38,14 +38,14 @@ where } } -impl Eq for HashSet +impl Eq for TryHashSet where T: Eq + Hash, S: BuildHasher, { } -impl fmt::Debug for HashSet +impl fmt::Debug for TryHashSet where T: fmt::Debug, { @@ -54,7 +54,7 @@ where } } -impl HashSet { +impl TryHashSet { /// Same as [`hashbrown::hash_set::HashSet::new`]. pub fn new() -> Self { Self { @@ -63,7 +63,7 @@ impl HashSet { } } -impl HashSet +impl TryHashSet where T: Eq + Hash, { @@ -76,7 +76,7 @@ where } } -impl HashSet { +impl TryHashSet { /// Same as [`hashbrown::hash_set::HashSet::with_hasher`]. pub const fn with_hasher(hasher: S) -> Self { Self { @@ -131,7 +131,7 @@ impl HashSet { } } -impl HashSet +impl TryHashSet where T: Eq + Hash, S: BuildHasher, diff --git a/crates/environ/src/collections/index_map.rs b/crates/environ/src/collections/index_map.rs index efb9654bfc84..55629f871d2a 100644 --- a/crates/environ/src/collections/index_map.rs +++ b/crates/environ/src/collections/index_map.rs @@ -18,11 +18,11 @@ use hashbrown::DefaultHashBuilder; /// A wrapper around [`indexmap::IndexMap`] that only provides fallible /// allocation. -pub struct IndexMap { +pub struct TryIndexMap { inner: indexmap::IndexMap, } -impl TryClone for IndexMap +impl TryClone for TryIndexMap where K: Eq + Hash + TryClone, V: TryClone, @@ -37,7 +37,7 @@ where } } -impl fmt::Debug for IndexMap +impl fmt::Debug for TryIndexMap where K: fmt::Debug, V: fmt::Debug, @@ -48,19 +48,19 @@ where } } -impl From> for indexmap::IndexMap { - fn from(map: IndexMap) -> Self { +impl From> for indexmap::IndexMap { + fn from(map: TryIndexMap) -> Self { map.inner } } -impl From> for IndexMap { +impl From> for TryIndexMap { fn from(inner: indexmap::IndexMap) -> Self { Self { inner } } } -impl serde::ser::Serialize for IndexMap +impl serde::ser::Serialize for TryIndexMap where K: serde::ser::Serialize, V: serde::ser::Serialize, @@ -78,7 +78,7 @@ where } } -impl<'de, K, V> serde::de::Deserialize<'de> for IndexMap +impl<'de, K, V> serde::de::Deserialize<'de> for TryIndexMap where K: serde::de::Deserialize<'de> + Hash + Eq, V: serde::de::Deserialize<'de>, @@ -87,14 +87,14 @@ where where D: serde::Deserializer<'de>, { - struct Visitor(PhantomData IndexMap>); + struct Visitor(PhantomData TryIndexMap>); impl<'de, K, V> serde::de::Visitor<'de> for Visitor where K: serde::de::Deserialize<'de> + Hash + Eq, V: serde::de::Deserialize<'de>, { - type Value = IndexMap; + type Value = TryIndexMap; fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("an `IndexMap`") @@ -106,7 +106,7 @@ where { use serde::de::Error as _; - let mut result = IndexMap::::new(); + let mut result = TryIndexMap::::new(); if let Some(len) = map.size_hint() { result.reserve(len).map_err(|oom| A::Error::custom(oom))?; @@ -124,7 +124,7 @@ where } } -impl IndexMap { +impl TryIndexMap { /// Same as [`indexmap::IndexMap::new`]. pub fn new() -> Self { Self { @@ -139,7 +139,7 @@ impl IndexMap { } } -impl IndexMap { +impl TryIndexMap { /// Same as [`indexmap::IndexMap::with_capacity_and_hasher`] but returns an /// error on allocation failure. pub fn with_capacity_and_hasher(n: usize, hash_builder: S) -> Result { @@ -150,7 +150,7 @@ impl IndexMap { /// Same as [`indexmap::IndexMap::with_hasher`]. pub const fn with_hasher(hash_builder: S) -> Self { - IndexMap { + TryIndexMap { inner: indexmap::IndexMap::with_hasher(hash_builder), } } @@ -279,7 +279,7 @@ impl IndexMap { } } -impl IndexMap +impl TryIndexMap where K: Hash + Eq, S: BuildHasher, @@ -369,7 +369,7 @@ where } } -impl IndexMap +impl TryIndexMap where S: BuildHasher, { @@ -509,7 +509,7 @@ where } } -impl IndexMap { +impl TryIndexMap { /// Same as [`indexmap::IndexMap::pop`]. pub fn pop(&mut self) -> Option<(K, V)> { self.inner.pop() @@ -712,7 +712,7 @@ impl IndexMap { } } -impl Index<&Q> for IndexMap +impl Index<&Q> for TryIndexMap where Q: ?Sized + Hash + Eq, K: Borrow, @@ -725,7 +725,7 @@ where } } -impl IndexMut<&Q> for IndexMap +impl IndexMut<&Q> for TryIndexMap where Q: ?Sized + Hash + Eq, K: Borrow, @@ -736,7 +736,7 @@ where } } -impl Index for IndexMap { +impl Index for TryIndexMap { type Output = V; fn index(&self, index: usize) -> &V { @@ -744,13 +744,13 @@ impl Index for IndexMap { } } -impl IndexMut for IndexMap { +impl IndexMut for TryIndexMap { fn index_mut(&mut self, index: usize) -> &mut V { &mut self.inner[index] } } -impl Default for IndexMap +impl Default for TryIndexMap where S: Default, { @@ -759,19 +759,19 @@ where } } -impl PartialEq> for IndexMap +impl PartialEq> for TryIndexMap where K: Hash + Eq, V1: PartialEq, S1: BuildHasher, S2: BuildHasher, { - fn eq(&self, other: &IndexMap) -> bool { + fn eq(&self, other: &TryIndexMap) -> bool { &self.inner == &other.inner } } -impl Eq for IndexMap +impl Eq for TryIndexMap where K: Eq + Hash, V: Eq, @@ -779,7 +779,7 @@ where { } -impl<'a, K, V, S> IntoIterator for &'a IndexMap { +impl<'a, K, V, S> IntoIterator for &'a TryIndexMap { type Item = (&'a K, &'a V); type IntoIter = inner::Iter<'a, K, V>; @@ -788,7 +788,7 @@ impl<'a, K, V, S> IntoIterator for &'a IndexMap { } } -impl<'a, K, V, S> IntoIterator for &'a mut IndexMap { +impl<'a, K, V, S> IntoIterator for &'a mut TryIndexMap { type Item = (&'a K, &'a mut V); type IntoIter = inner::IterMut<'a, K, V>; @@ -797,7 +797,7 @@ impl<'a, K, V, S> IntoIterator for &'a mut IndexMap { } } -impl IntoIterator for IndexMap { +impl IntoIterator for TryIndexMap { type Item = (K, V); type IntoIter = inner::IntoIter; @@ -813,7 +813,7 @@ mod tests { #[test] fn smoke() -> Result<()> { - let mut map = IndexMap::new(); + let mut map = TryIndexMap::new(); map.insert("a", 10)?; map.insert("b", 20)?; diff --git a/crates/environ/src/collections/primary_map.rs b/crates/environ/src/collections/primary_map.rs index e011c7d07275..a44dd4b8b718 100644 --- a/crates/environ/src/collections/primary_map.rs +++ b/crates/environ/src/collections/primary_map.rs @@ -1,4 +1,5 @@ -use crate::{collections::TryExtend, error::OutOfMemory}; +use crate::error::OutOfMemory; +use crate::prelude::*; use core::{ fmt, ops::{Index, IndexMut}, @@ -9,14 +10,14 @@ use serde::{Serialize, ser::SerializeSeq}; /// Like [`cranelift_entity::PrimaryMap`] but enforces fallible allocation for /// all methods that allocate. #[derive(Hash, PartialEq, Eq)] -pub struct PrimaryMap +pub struct TryPrimaryMap where K: EntityRef, { inner: cranelift_entity::PrimaryMap, } -impl Default for PrimaryMap +impl Default for TryPrimaryMap where K: EntityRef, { @@ -27,7 +28,7 @@ where } } -impl fmt::Debug for PrimaryMap +impl fmt::Debug for TryPrimaryMap where K: EntityRef + fmt::Debug, V: fmt::Debug, @@ -37,18 +38,18 @@ where } } -impl From> for PrimaryMap +impl From> for TryPrimaryMap where K: EntityRef, { - fn from(values: crate::collections::Vec) -> Self { + fn from(values: TryVec) -> Self { let values: ::alloc::vec::Vec = values.into(); let inner = cranelift_entity::PrimaryMap::from(values); Self { inner } } } -impl serde::ser::Serialize for PrimaryMap +impl serde::ser::Serialize for TryPrimaryMap where K: EntityRef, V: Serialize, @@ -65,7 +66,7 @@ where } } -impl<'de, K, V> serde::de::Deserialize<'de> for PrimaryMap +impl<'de, K, V> serde::de::Deserialize<'de> for TryPrimaryMap where K: EntityRef, V: serde::de::Deserialize<'de>, @@ -74,12 +75,12 @@ where where D: serde::Deserializer<'de>, { - let v: crate::collections::Vec = serde::de::Deserialize::deserialize(deserializer)?; + let v: TryVec = serde::de::Deserialize::deserialize(deserializer)?; Ok(Self::from(v)) } } -impl PrimaryMap +impl TryPrimaryMap where K: EntityRef, { @@ -217,7 +218,7 @@ where } } -impl TryExtend for PrimaryMap +impl TryExtend for TryPrimaryMap where K: EntityRef, { @@ -236,7 +237,7 @@ where } } -impl Index for PrimaryMap +impl Index for TryPrimaryMap where K: EntityRef, { @@ -247,7 +248,7 @@ where } } -impl IndexMut for PrimaryMap +impl IndexMut for TryPrimaryMap where K: EntityRef, { @@ -256,7 +257,7 @@ where } } -impl IntoIterator for PrimaryMap +impl IntoIterator for TryPrimaryMap where K: EntityRef, { @@ -267,7 +268,7 @@ where } } -impl<'a, K, V> IntoIterator for &'a PrimaryMap +impl<'a, K, V> IntoIterator for &'a TryPrimaryMap where K: EntityRef, { @@ -278,7 +279,7 @@ where } } -impl<'a, K, V> IntoIterator for &'a mut PrimaryMap +impl<'a, K, V> IntoIterator for &'a mut TryPrimaryMap where K: EntityRef, { diff --git a/crates/environ/src/collections/secondary_map.rs b/crates/environ/src/collections/secondary_map.rs index d9e91de07d0a..a4d909a7f900 100644 --- a/crates/environ/src/collections/secondary_map.rs +++ b/crates/environ/src/collections/secondary_map.rs @@ -1,9 +1,9 @@ -use crate::{collections::Vec, error::OutOfMemory}; +use crate::{error::OutOfMemory, prelude::*}; use core::{fmt, ops::Index}; use cranelift_entity::{EntityRef, SecondaryMap as Inner}; /// Like [`cranelift_entity::SecondaryMap`] but all allocation is fallible. -pub struct SecondaryMap +pub struct TrySecondaryMap where K: EntityRef, V: Clone, @@ -11,7 +11,7 @@ where inner: Inner, } -impl fmt::Debug for SecondaryMap +impl fmt::Debug for TrySecondaryMap where K: EntityRef + fmt::Debug, V: fmt::Debug + Clone, @@ -21,7 +21,7 @@ where } } -impl SecondaryMap +impl TrySecondaryMap where K: EntityRef, V: Clone, @@ -125,19 +125,19 @@ where } } -impl Default for SecondaryMap +impl Default for TrySecondaryMap where K: EntityRef, V: Clone + Default, { - fn default() -> SecondaryMap { - SecondaryMap::new() + fn default() -> TrySecondaryMap { + TrySecondaryMap::new() } } // NB: no `IndexMut` implementation because it requires allocation but the trait // doesn't allow for fallibility. -impl Index for SecondaryMap +impl Index for TrySecondaryMap where K: EntityRef, V: Clone, @@ -149,19 +149,19 @@ where } } -impl From> for SecondaryMap +impl From> for TrySecondaryMap where K: EntityRef, V: Clone + Default, { - fn from(values: Vec) -> Self { + fn from(values: TryVec) -> Self { let values: alloc::vec::Vec = values.into(); let inner = Inner::from(values); Self { inner } } } -impl serde::ser::Serialize for SecondaryMap +impl serde::ser::Serialize for TrySecondaryMap where K: EntityRef, V: Clone + PartialEq + serde::ser::Serialize, @@ -197,7 +197,7 @@ where } } -impl<'de, K, V> serde::de::Deserialize<'de> for SecondaryMap +impl<'de, K, V> serde::de::Deserialize<'de> for TrySecondaryMap where K: EntityRef, V: Clone + serde::de::Deserialize<'de>, @@ -206,7 +206,7 @@ where where D: serde::Deserializer<'de>, { - struct Visitor(core::marker::PhantomData SecondaryMap>) + struct Visitor(core::marker::PhantomData TrySecondaryMap>) where K: EntityRef, V: Clone; @@ -216,7 +216,7 @@ where K: EntityRef, V: Clone + serde::de::Deserialize<'de>, { - type Value = SecondaryMap; + type Value = TrySecondaryMap; fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("struct SecondaryMap") @@ -234,7 +234,7 @@ where return Err(serde::de::Error::custom("Default value required")); }; - let mut map = SecondaryMap::::with_default(default.clone()); + let mut map = TrySecondaryMap::::with_default(default.clone()); if let Some(n) = size_hint { map.resize(n).map_err(|oom| serde::de::Error::custom(oom))?; @@ -279,7 +279,7 @@ mod tests { #[test] fn serialize_deserialize() -> Result<()> { - let mut map = SecondaryMap::::with_default(99); + let mut map = TrySecondaryMap::::with_default(99); map.insert(k(0), 33)?; map.insert(k(1), 44)?; map.insert(k(2), 55)?; @@ -287,7 +287,7 @@ mod tests { map.insert(k(4), 99)?; let bytes = postcard::to_allocvec(&map)?; - let map2: SecondaryMap = postcard::from_bytes(&bytes)?; + let map2: TrySecondaryMap = postcard::from_bytes(&bytes)?; for i in 0..10 { assert_eq!(map[k(i)], map2[k(i)]); diff --git a/crates/environ/src/compile/address_map.rs b/crates/environ/src/compile/address_map.rs index 4420e942be28..8620a4840ebd 100644 --- a/crates/environ/src/compile/address_map.rs +++ b/crates/environ/src/compile/address_map.rs @@ -4,7 +4,7 @@ use crate::InstructionAddressMap; use crate::obj::ELF_WASMTIME_ADDRMAP; use crate::prelude::*; use object::write::{Object, StandardSegment}; -use object::{LittleEndian, SectionKind, U32Bytes}; +use object::{LittleEndian, SectionKind, U32}; use std::ops::Range; /// Builder for the address map section of a wasmtime compilation image. @@ -14,8 +14,8 @@ use std::ops::Range; /// into an `Object`. #[derive(Default)] pub struct AddressMapSection { - offsets: Vec>, - positions: Vec>, + offsets: Vec>, + positions: Vec>, last_offset: u32, } @@ -58,8 +58,8 @@ impl AddressMapSection { } last_srcloc = Some(srcloc); - self.offsets.push(U32Bytes::new(LittleEndian, pos)); - self.positions.push(U32Bytes::new(LittleEndian, srcloc)); + self.offsets.push(U32::new(LittleEndian, pos)); + self.positions.push(U32::new(LittleEndian, srcloc)); } self.last_offset = func_end; } diff --git a/crates/environ/src/compile/frame_table.rs b/crates/environ/src/compile/frame_table.rs index 489e990c9ab0..f3288f08f121 100644 --- a/crates/environ/src/compile/frame_table.rs +++ b/crates/environ/src/compile/frame_table.rs @@ -10,9 +10,9 @@ use crate::{ FrameInstPos, FrameStackShape, FrameStateSlotOffset, FrameTableDescriptorIndex, FrameValType, - FuncKey, WasmHeapTopType, WasmValType, prelude::*, + FuncKey, ModulePC, WasmHeapTopType, WasmValType, prelude::*, }; -use object::{LittleEndian, U32Bytes}; +use object::{LittleEndian, U32}; use std::collections::{HashMap, hash_map::Entry}; /// Builder for a stackslot descriptor. @@ -230,19 +230,19 @@ impl FrameStateSlotBuilder { pub struct FrameTableBuilder { /// (offset, length) pairs into `frame_descriptor_data`, indexed /// by frame descriptor number. - frame_descriptor_ranges: Vec>, + frame_descriptor_ranges: Vec>, frame_descriptor_data: Vec, /// Offset from frame slot up to FP for each frame descriptor. - frame_descriptor_fp_offsets: Vec>, + frame_descriptor_fp_offsets: Vec>, - progpoint_pcs: Vec>, - progpoint_descriptor_offsets: Vec>, - progpoint_descriptor_data: Vec>, + progpoint_pcs: Vec>, + progpoint_descriptor_offsets: Vec>, + progpoint_descriptor_data: Vec>, - breakpoint_pcs: Vec>, - breakpoint_patch_offsets: Vec>, - breakpoint_patch_data_ends: Vec>, + breakpoint_pcs: Vec>, + breakpoint_patch_offsets: Vec>, + breakpoint_patch_data_ends: Vec>, breakpoint_patch_data: Vec, } @@ -264,11 +264,11 @@ impl FrameTableBuilder { u32::try_from(self.frame_descriptor_fp_offsets.len()).unwrap(), ); self.frame_descriptor_fp_offsets - .push(U32Bytes::new(LittleEndian, slot_to_fp_offset)); + .push(U32::new(LittleEndian, slot_to_fp_offset)); self.frame_descriptor_ranges - .push(U32Bytes::new(LittleEndian, start)); + .push(U32::new(LittleEndian, start)); self.frame_descriptor_ranges - .push(U32Bytes::new(LittleEndian, end)); + .push(U32::new(LittleEndian, end)); index } @@ -278,9 +278,9 @@ impl FrameTableBuilder { &mut self, native_pc: u32, pos: FrameInstPos, - // For each frame: Wasm PC, frame descriptor, stack shape - // within the frame descriptor. - frames: &[(u32, FrameTableDescriptorIndex, FrameStackShape)], + // For each frame: module-relative Wasm PC, frame descriptor, + // stack shape within the frame descriptor. + frames: &[(ModulePC, FrameTableDescriptorIndex, FrameStackShape)], ) { let pc_and_pos = FrameInstPos::encode(native_pc, pos); // If we already have a program point record at this PC, @@ -295,34 +295,39 @@ impl FrameTableBuilder { } let start = u32::try_from(self.progpoint_descriptor_data.len()).unwrap(); - self.progpoint_pcs - .push(U32Bytes::new(LittleEndian, pc_and_pos)); + self.progpoint_pcs.push(U32::new(LittleEndian, pc_and_pos)); self.progpoint_descriptor_offsets - .push(U32Bytes::new(LittleEndian, start)); + .push(U32::new(LittleEndian, start)); for (i, &(wasm_pc, frame_descriptor, stack_shape)) in frames.iter().enumerate() { - debug_assert!(wasm_pc < 0x8000_0000); + let wasm_pc_raw = wasm_pc.raw(); + debug_assert!(wasm_pc_raw < 0x8000_0000); let not_last = i < (frames.len() - 1); - let wasm_pc = wasm_pc | if not_last { 0x8000_0000 } else { 0 }; + let wasm_pc_raw = wasm_pc_raw | if not_last { 0x8000_0000 } else { 0 }; self.progpoint_descriptor_data - .push(U32Bytes::new(LittleEndian, wasm_pc)); + .push(U32::new(LittleEndian, wasm_pc_raw)); self.progpoint_descriptor_data - .push(U32Bytes::new(LittleEndian, frame_descriptor.0)); + .push(U32::new(LittleEndian, frame_descriptor.0)); self.progpoint_descriptor_data - .push(U32Bytes::new(LittleEndian, stack_shape.0)); + .push(U32::new(LittleEndian, stack_shape.0)); } } /// Add one breakpoint patch. - pub fn add_breakpoint_patch(&mut self, wasm_pc: u32, patch_start_native_pc: u32, patch: &[u8]) { + pub fn add_breakpoint_patch( + &mut self, + wasm_pc: ModulePC, + patch_start_native_pc: u32, + patch: &[u8], + ) { self.breakpoint_pcs - .push(U32Bytes::new(LittleEndian, wasm_pc)); + .push(U32::new(LittleEndian, wasm_pc.raw())); self.breakpoint_patch_offsets - .push(U32Bytes::new(LittleEndian, patch_start_native_pc)); + .push(U32::new(LittleEndian, patch_start_native_pc)); self.breakpoint_patch_data.extend(patch.iter().cloned()); let end = u32::try_from(self.breakpoint_patch_data.len()).unwrap(); self.breakpoint_patch_data_ends - .push(U32Bytes::new(LittleEndian, end)); + .push(U32::new(LittleEndian, end)); } /// Serialize the framd-table data section, taking a closure to diff --git a/crates/environ/src/compile/module_environ.rs b/crates/environ/src/compile/module_environ.rs index 61938af88442..59ddb758cf83 100644 --- a/crates/environ/src/compile/module_environ.rs +++ b/crates/environ/src/compile/module_environ.rs @@ -10,7 +10,7 @@ use crate::{ ModuleInternedTypeIndex, ModuleTypesBuilder, PanicOnOom as _, PrimaryMap, SizeOverflow, StaticMemoryInitializer, StaticModuleIndex, TableIndex, TableInitialValue, Tag, TagIndex, Tunables, TypeConvert, TypeIndex, WasmError, WasmHeapTopType, WasmHeapType, WasmResult, - WasmValType, WasmparserTypeConverter, collections, + WasmValType, WasmparserTypeConverter, }; use cranelift_entity::SecondaryMap; use cranelift_entity::packed_option::ReservedValue; @@ -53,6 +53,12 @@ pub struct ModuleTranslation<'data> { /// themselves. pub wasm: &'data [u8], + /// The byte offset of this module's Wasm binary within the outer + /// binary (e.g. a component). For standalone modules this is 0. + /// This is used to convert component-relative source locations to + /// module-relative source locations. + pub wasm_module_offset: u64, + /// References to the function bodies. pub function_body_inputs: PrimaryMap>, @@ -118,6 +124,7 @@ impl<'data> ModuleTranslation<'data> { Self { module: Module::new(module_index), wasm: &[], + wasm_module_offset: 0, function_body_inputs: PrimaryMap::default(), known_imported_functions: SecondaryMap::default(), exported_signatures: Vec::default(), @@ -403,7 +410,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { self.result.module.tables.push(table)?; let init = match init { wasmparser::TableInit::RefNull => TableInitialValue::Null { - precomputed: collections::Vec::new(), + precomputed: TryVec::new(), }, wasmparser::TableInit::Expr(expr) => { let (init, escaped) = ConstExpr::from_wasmparser(self, expr)?; @@ -1107,7 +1114,7 @@ impl ModuleTranslation<'_> { // memory initialization image is built here from the page data and then // it's converted to a single initializer. let data = mem::replace(&mut self.data, Vec::new()); - let mut map = collections::PrimaryMap::with_capacity(info.len()).panic_on_oom(); + let mut map = TryPrimaryMap::with_capacity(info.len()).panic_on_oom(); let mut module_data_size = 0u32; for (memory, info) in info.iter() { // Create the in-memory `image` which is the initialized contents of @@ -1230,7 +1237,7 @@ impl ModuleTranslation<'_> { if let TableInitialValue::Expr(expr) = init { if let [ConstOp::RefFunc(f)] = expr.ops() { *init = TableInitialValue::Null { - precomputed: collections::vec![*f; table_size as usize].panic_on_oom(), + precomputed: try_vec![*f; table_size as usize].panic_on_oom(), }; } } diff --git a/crates/environ/src/compile/stack_maps.rs b/crates/environ/src/compile/stack_maps.rs index a02b5e7162a9..57ce411874bd 100644 --- a/crates/environ/src/compile/stack_maps.rs +++ b/crates/environ/src/compile/stack_maps.rs @@ -2,7 +2,7 @@ use crate::obj::ELF_WASMTIME_STACK_MAP; use crate::prelude::*; use cranelift_bitset::CompoundBitSet; use object::write::{Object, StandardSegment}; -use object::{LittleEndian, SectionKind, U32Bytes}; +use object::{LittleEndian, SectionKind, U32}; /// Builder for the `ELF_WASMTIME_STACK_MAP` section in compiled executables. /// @@ -71,9 +71,9 @@ use object::{LittleEndian, SectionKind, U32Bytes}; /// more. #[derive(Default)] pub struct StackMapSection { - pcs: Vec>, - pointers_to_stack_map: Vec>, - stack_map_data: Vec>, + pcs: Vec>, + pointers_to_stack_map: Vec>, + stack_map_data: Vec>, last_offset: u32, } @@ -103,16 +103,15 @@ impl StackMapSection { } // Record parallel entries in `pcs`/`pointers_to_stack_map`. - self.pcs.push(U32Bytes::new(LittleEndian, code_offset)); - self.pointers_to_stack_map.push(U32Bytes::new( + self.pcs.push(U32::new(LittleEndian, code_offset)); + self.pointers_to_stack_map.push(U32::new( LittleEndian, u32::try_from(self.stack_map_data.len()).unwrap(), )); // The frame data starts with the frame size and is then followed by // `offsets` represented as a bit set. - self.stack_map_data - .push(U32Bytes::new(LittleEndian, frame_size)); + self.stack_map_data.push(U32::new(LittleEndian, frame_size)); let mut bits = CompoundBitSet::::default(); for offset in frame_offsets { @@ -121,10 +120,9 @@ impl StackMapSection { } let count = bits.iter_scalars().count(); self.stack_map_data - .push(U32Bytes::new(LittleEndian, count as u32)); + .push(U32::new(LittleEndian, count as u32)); for scalar in bits.iter_scalars() { - self.stack_map_data - .push(U32Bytes::new(LittleEndian, scalar.0)); + self.stack_map_data.push(U32::new(LittleEndian, scalar.0)); } } diff --git a/crates/environ/src/compile/trap_encoding.rs b/crates/environ/src/compile/trap_encoding.rs index 3cd548aa380b..c5d5b63a54ea 100644 --- a/crates/environ/src/compile/trap_encoding.rs +++ b/crates/environ/src/compile/trap_encoding.rs @@ -2,7 +2,7 @@ use crate::TrapInformation; use crate::obj::ELF_WASMTIME_TRAPS; use crate::prelude::*; use object::write::{Object, StandardSegment}; -use object::{LittleEndian, SectionKind, U32Bytes}; +use object::{LittleEndian, SectionKind, U32}; use std::ops::Range; /// A helper structure to build the custom-encoded section of a wasmtime @@ -13,7 +13,7 @@ use std::ops::Range; /// `lookup_trap_code` below with the resulting section. #[derive(Default)] pub struct TrapEncodingBuilder { - offsets: Vec>, + offsets: Vec>, traps: Vec, last_offset: u32, } @@ -45,7 +45,7 @@ impl TrapEncodingBuilder { for info in traps { let pos = func_start + info.code_offset; assert!(pos >= self.last_offset); - self.offsets.push(U32Bytes::new(LittleEndian, pos)); + self.offsets.push(U32::new(LittleEndian, pos)); self.traps.push(info.trap_code as u8); self.last_offset = pos; } diff --git a/crates/environ/src/component/info.rs b/crates/environ/src/component/info.rs index 40feaa1c8869..4222dd75687c 100644 --- a/crates/environ/src/component/info.rs +++ b/crates/environ/src/component/info.rs @@ -660,6 +660,15 @@ impl FixedEncoding { FixedEncoding::Latin1 => 1, } } + + /// Returns the alignment of strings using this encoding. + pub fn align(&self) -> u8 { + match self { + FixedEncoding::Utf8 => 1, + FixedEncoding::Utf16 => 2, + FixedEncoding::Latin1 => 2, + } + } } /// Description of a new resource declared in a `GlobalInitializer::Resource` diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs index 8855a848a660..9fef9cce7bf8 100644 --- a/crates/environ/src/component/translate.rs +++ b/crates/environ/src/component/translate.rs @@ -1199,7 +1199,7 @@ impl<'a, 'data> Translator<'a, 'data> { let index = self.validator.types(0).unwrap().module_count(); self.validator.module_section(&unchecked_range)?; let static_module_index = self.static_modules.next_key(); - let translation = ModuleEnvironment::new( + let mut translation = ModuleEnvironment::new( self.tunables, self.validator, self.types.module_types_builder(), @@ -1219,6 +1219,8 @@ impl<'a, 'data> Translator<'a, 'data> { .context("wasm component contains an invalid module section") })?, )?; + + translation.wasm_module_offset = u64::try_from(unchecked_range.start).unwrap(); let static_module_index2 = self.static_modules.push(translation); assert_eq!(static_module_index, static_module_index2); let types = self.validator.types(0).unwrap(); diff --git a/crates/environ/src/component/translate/inline.rs b/crates/environ/src/component/translate/inline.rs index 7af372fea828..e1e87643c973 100644 --- a/crates/environ/src/component/translate/inline.rs +++ b/crates/environ/src/component/translate/inline.rs @@ -535,7 +535,7 @@ impl<'a> Inliner<'a> { dfg::CoreDef::Trampoline(index) } - // Lowering a lifted functio means that a "fused adapter" + // Lowering a lifted function means that a "fused adapter" // was just identified. // // Metadata about this fused adapter is recorded in the diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs index 0f1a811a0d0f..5cbfde24c693 100644 --- a/crates/environ/src/component/types.rs +++ b/crates/environ/src/component/types.rs @@ -89,6 +89,8 @@ indices! { pub struct TypeResultIndex(u32); /// Index pointing to a list type in the component model. pub struct TypeListIndex(u32); + /// Index pointing to a map type in the component model. + pub struct TypeMapIndex(u32); /// Index pointing to a fixed size list type in the component model. pub struct TypeFixedLengthListIndex(u32); /// Index pointing to a future type in the component model. @@ -283,6 +285,7 @@ pub struct ComponentTypes { pub(super) component_instances: PrimaryMap, pub(super) functions: PrimaryMap, pub(super) lists: PrimaryMap, + pub(super) maps: PrimaryMap, pub(super) records: PrimaryMap, pub(super) variants: PrimaryMap, pub(super) tuples: PrimaryMap, @@ -363,7 +366,9 @@ impl ComponentTypes { &CanonicalAbiInfo::SCALAR8 } - InterfaceType::String | InterfaceType::List(_) => &CanonicalAbiInfo::POINTER_PAIR, + InterfaceType::String | InterfaceType::List(_) | InterfaceType::Map(_) => { + &CanonicalAbiInfo::POINTER_PAIR + } InterfaceType::Record(i) => &self[*i].abi, InterfaceType::Variant(i) => &self[*i].abi, @@ -416,6 +421,7 @@ impl_index! { impl Index for ComponentTypes { TypeOption => options } impl Index for ComponentTypes { TypeResult => results } impl Index for ComponentTypes { TypeList => lists } + impl Index for ComponentTypes { TypeMap => maps } impl Index for ComponentTypes { TypeResourceTable => resource_tables } impl Index for ComponentTypes { TypeFuture => futures } impl Index for ComponentTypes { TypeStream => streams } @@ -591,6 +597,7 @@ pub enum InterfaceType { Variant(TypeVariantIndex), List(TypeListIndex), Tuple(TypeTupleIndex), + Map(TypeMapIndex), Flags(TypeFlagsIndex), Enum(TypeEnumIndex), Option(TypeOptionIndex), @@ -1181,6 +1188,23 @@ pub struct TypeList { pub element: InterfaceType, } +/// Shape of a "map" interface type. +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] +pub struct TypeMap { + /// The key type of the map. + pub key: InterfaceType, + /// The value type of the map. + pub value: InterfaceType, + /// Byte information for each map entry represented as `tuple`. + pub entry_abi: CanonicalAbiInfo, + /// Offset in bytes from the start of the entry tuple to the value field in + /// memory32. + pub value_offset32: u32, + /// Offset in bytes from the start of the entry tuple to the value field in + /// memory64. + pub value_offset64: u32, +} + /// Shape of a "fixed size list" interface type. #[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] pub struct TypeFixedLengthList { diff --git a/crates/environ/src/component/types_builder.rs b/crates/environ/src/component/types_builder.rs index 97df81cb3a33..10422028a3dd 100644 --- a/crates/environ/src/component/types_builder.rs +++ b/crates/environ/src/component/types_builder.rs @@ -38,6 +38,7 @@ const MAX_TYPE_DEPTH: u32 = 100; pub struct ComponentTypesBuilder { functions: HashMap, lists: HashMap, + maps: HashMap, records: HashMap, variants: HashMap, tuples: HashMap, @@ -103,6 +104,7 @@ impl ComponentTypesBuilder { functions: HashMap::default(), lists: HashMap::default(), + maps: HashMap::default(), records: HashMap::default(), variants: HashMap::default(), tuples: HashMap::default(), @@ -441,6 +443,9 @@ impl ComponentTypesBuilder { InterfaceType::Variant(self.variant_type(types, e)?) } ComponentDefinedType::List(e) => InterfaceType::List(self.list_type(types, e)?), + ComponentDefinedType::Map(key, value) => { + InterfaceType::Map(self.map_type(types, key, value)?) + } ComponentDefinedType::Tuple(e) => InterfaceType::Tuple(self.tuple_type(types, e)?), ComponentDefinedType::Flags(e) => InterfaceType::Flags(self.flags_type(e)), ComponentDefinedType::Enum(e) => InterfaceType::Enum(self.enum_type(e)), @@ -461,9 +466,6 @@ impl ComponentTypesBuilder { ComponentDefinedType::FixedLengthList(ty, size) => { InterfaceType::FixedLengthList(self.fixed_length_list_type(types, ty, *size)?) } - ComponentDefinedType::Map(..) => { - bail!("support not implemented for map type"); - } }; let info = self.type_information(&ret); if info.depth > MAX_TYPE_DEPTH { @@ -690,6 +692,36 @@ impl ComponentTypesBuilder { Ok(self.add_list_type(TypeList { element })) } + fn map_type( + &mut self, + types: TypesRef<'_>, + key: &ComponentValType, + value: &ComponentValType, + ) -> Result { + assert_eq!(types.id(), self.module_types.validator_id()); + let key_ty = self.valtype(types, key)?; + let value_ty = self.valtype(types, value)?; + let key_abi = self.component_types.canonical_abi(&key_ty); + let value_abi = self.component_types.canonical_abi(&value_ty); + let entry_abi = CanonicalAbiInfo::record([key_abi, value_abi].into_iter()); + + let mut offset32 = 0; + key_abi.next_field32(&mut offset32); + let value_offset32 = value_abi.next_field32(&mut offset32); + + let mut offset64 = 0; + key_abi.next_field64(&mut offset64); + let value_offset64 = value_abi.next_field64(&mut offset64); + + Ok(self.add_map_type(TypeMap { + key: key_ty, + value: value_ty, + entry_abi, + value_offset32, + value_offset64, + })) + } + /// Converts a wasmparser `id`, which must point to a resource, to its /// corresponding `TypeResourceTableIndex`. pub fn resource_id(&mut self, id: ResourceId) -> TypeResourceTableIndex { @@ -749,6 +781,11 @@ impl ComponentTypesBuilder { intern_and_fill_flat_types!(self, lists, ty) } + /// Interns a new map type within this type information. + pub fn add_map_type(&mut self, ty: TypeMap) -> TypeMapIndex { + intern_and_fill_flat_types!(self, maps, ty) + } + /// Interns a new future type within this type information. pub fn add_future_type(&mut self, ty: TypeFuture) -> TypeFutureIndex { intern(&mut self.futures, &mut self.component_types.futures, ty) @@ -852,6 +889,7 @@ impl ComponentTypesBuilder { } InterfaceType::List(i) => &self.type_info.lists[*i], + InterfaceType::Map(i) => &self.type_info.maps[*i], InterfaceType::Record(i) => &self.type_info.records[*i], InterfaceType::Variant(i) => &self.type_info.variants[*i], InterfaceType::Tuple(i) => &self.type_info.tuples[*i], @@ -969,6 +1007,7 @@ struct TypeInformationCache { options: PrimaryMap, results: PrimaryMap, lists: PrimaryMap, + maps: PrimaryMap, fixed_length_lists: PrimaryMap, } @@ -1182,4 +1221,15 @@ impl TypeInformation { self.depth += info.depth; self.has_borrow = info.has_borrow; } + + fn maps(&mut self, types: &ComponentTypesBuilder, ty: &TypeMap) { + // Maps are represented as list> in canonical ABI + // So we use POINTER_PAIR like lists, and calculate depth/borrow from key and value + *self = TypeInformation::string(); + let key_info = types.type_information(&ty.key); + let value_info = types.type_information(&ty.value); + // Depth is max of key/value depths, plus 1 for the extra map layer. + self.depth = key_info.depth.max(value_info.depth) + 1; + self.has_borrow = key_info.has_borrow || value_info.has_borrow; + } } diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index 8757bcb26aef..fd1ea3e8929f 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -20,8 +20,8 @@ use crate::component::{ InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT, START_FLAG_ASYNC_CALLEE, StringEncoding, Transcode, TypeComponentLocalErrorContextTableIndex, TypeEnumIndex, TypeFixedLengthListIndex, - TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeOptionIndex, TypeRecordIndex, - TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex, + TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeMapIndex, TypeOptionIndex, + TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, VariantInfo, }; use crate::fact::signature::Signature; @@ -40,7 +40,7 @@ use wasmtime_component_util::{DiscriminantSize, FlagsSize}; use super::DataModel; -const MAX_STRING_BYTE_LENGTH: u32 = 1 << 31; +const MAX_STRING_BYTE_LENGTH: u32 = (1 << 31) - 1; const UTF16_TAG: u32 = 1 << 31; /// This value is arbitrarily chosen and should be fine to change at any time, @@ -1146,6 +1146,8 @@ impl<'a, 'b> Compiler<'a, 'b> { // Iteration of a loop is along the lines of the cost of a string // so give it the same cost InterfaceType::List(_) => 40, + // Maps are similar to lists in terms of iteration cost + InterfaceType::Map(_) => 40, InterfaceType::Flags(i) => { let count = self.module.types[*i].names.len(); @@ -1196,6 +1198,7 @@ impl<'a, 'b> Compiler<'a, 'b> { InterfaceType::Char => self.translate_char(src, dst_ty, dst), InterfaceType::String => self.translate_string(src, dst_ty, dst), InterfaceType::List(t) => self.translate_list(*t, src, dst_ty, dst), + InterfaceType::Map(t) => self.translate_map(*t, src, dst_ty, dst), InterfaceType::Record(t) => self.translate_record(*t, src, dst_ty, dst), InterfaceType::Flags(f) => self.translate_flags(*f, src, dst_ty, dst), InterfaceType::Tuple(t) => self.translate_tuple(*t, src, dst_ty, dst), @@ -1761,7 +1764,6 @@ impl<'a, 'b> Compiler<'a, 'b> { dst_enc: FE, ) -> WasmString<'c> { assert!(dst_enc.width() >= src_enc.width()); - self.validate_string_length(src, dst_enc); let src_mem_opts = { match &src.opts.data_model { @@ -1776,22 +1778,8 @@ impl<'a, 'b> Compiler<'a, 'b> { } }; - // Calculate the source byte length given the size of each code - // unit. Note that this shouldn't overflow given - // `validate_string_length` above. - let mut src_byte_len_tmp = None; - let src_byte_len = if src_enc.width() == 1 { - src.len.idx - } else { - assert_eq!(src_enc.width(), 2); - self.instruction(LocalGet(src.len.idx)); - self.ptr_uconst(src_mem_opts, 1); - self.ptr_shl(src_mem_opts); - let tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr()); - let ret = tmp.idx; - src_byte_len_tmp = Some(tmp); - ret - }; + let (src_byte_len_tmp, src_byte_len) = + self.source_string_byte_len(src, src_enc, src_mem_opts); // Convert the source code units length to the destination byte // length type. @@ -1814,7 +1802,7 @@ impl<'a, 'b> Compiler<'a, 'b> { let dst_mem = self.malloc( dst_opts, MallocSize::Local(dst_byte_len.idx), - dst_enc.width().into(), + dst_enc.align().into(), ); WasmString { ptr: dst_mem.addr, @@ -1853,6 +1841,39 @@ impl<'a, 'b> Compiler<'a, 'b> { dst } + + /// Calculate the source byte length given the size of each code + /// unit. + /// + /// Returns an optional temporary local if it was needed, which the caller + /// needs to deallocate with `free_temp_local`. Additionally returns the + /// index of the local which contains the byte length of the string, which + /// may point to the temporary local passed in. + fn source_string_byte_len( + &mut self, + src: &WasmString<'_>, + src_enc: FE, + src_mem_opts: &LinearMemoryOptions, + ) -> (Option, u32) { + self.validate_string_length(src, src_enc); + + if src_enc.width() == 1 { + (None, src.len.idx) + } else { + assert_eq!(src_enc.width(), 2); + + // Note that this shouldn't overflow given `validate_string_length` + // above. + self.instruction(LocalGet(src.len.idx)); + self.ptr_uconst(src_mem_opts, 1); + self.ptr_shl(src_mem_opts); + let tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr()); + + let idx = tmp.idx; + (Some(tmp), idx) + } + } + // Corresponding function for `store_string_to_utf8` in the spec. // // This translation works by possibly performing a number of @@ -2130,6 +2151,7 @@ impl<'a, 'b> Compiler<'a, 'b> { } })); self.instruction(LocalSet(dst.ptr.idx)); + self.verify_aligned(dst_opts.data_model.unwrap_memory(), dst.ptr.idx, 2); self.instruction(End); // end of shrink-to-fit self.free_temp_local(dst_byte_len); @@ -2224,6 +2246,7 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(LocalGet(dst.len.idx)); // new_size self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32())); self.instruction(LocalSet(dst.ptr.idx)); + self.verify_aligned(dst_opts.data_model.unwrap_memory(), dst.ptr.idx, 2); self.free_temp_local(dst_byte_len); self.free_temp_local(src_byte_len); @@ -2252,7 +2275,9 @@ impl<'a, 'b> Compiler<'a, 'b> { DataModel::LinearMemory(opts) => opts, }; - self.validate_string_length(src, src_enc); + let (src_byte_len_tmp, src_byte_len) = + self.source_string_byte_len(src, src_enc, src_mem_opts); + self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr()); let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr()); let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr()); @@ -2265,7 +2290,7 @@ impl<'a, 'b> Compiler<'a, 'b> { } }; - self.validate_string_inbounds(src, src.len.idx); + self.validate_string_inbounds(src, src_byte_len); self.validate_string_inbounds(&dst, dst_byte_len.idx); // Perform the initial latin1 transcode. This returns the number of @@ -2305,6 +2330,7 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(LocalGet(dst.len.idx)); // new_size self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32())); self.instruction(LocalSet(dst.ptr.idx)); + self.verify_aligned(dst_opts.data_model.unwrap_memory(), dst.ptr.idx, 2); self.instruction(End); // In this block the latin1 encoding failed. The host transcode @@ -2330,6 +2356,8 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(LocalTee(dst_byte_len.idx)); self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32())); self.instruction(LocalSet(dst.ptr.idx)); + self.verify_aligned(dst_opts.data_model.unwrap_memory(), dst.ptr.idx, 2); + self.validate_string_inbounds(&dst, dst_byte_len.idx); // Call the host utf16 transcoding function. This will inflate the // prior latin1 bytes and then encode the rest of the source string @@ -2369,6 +2397,7 @@ impl<'a, 'b> Compiler<'a, 'b> { self.ptr_shl(dst_mem_opts); self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32())); self.instruction(LocalSet(dst.ptr.idx)); + self.verify_aligned(dst_opts.data_model.unwrap_memory(), dst.ptr.idx, 2); self.instruction(End); // Tag the returned pointer as utf16 @@ -2381,6 +2410,9 @@ impl<'a, 'b> Compiler<'a, 'b> { self.free_temp_local(src_len_tmp); self.free_temp_local(dst_byte_len); + if let Some(tmp) = src_byte_len_tmp { + self.free_temp_local(tmp); + } dst } @@ -2400,7 +2432,7 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(LocalGet(s.len.idx)); let max = MAX_STRING_BYTE_LENGTH / u32::from(dst); self.ptr_uconst(mem_opts, max); - self.ptr_ge_u(mem_opts); + self.ptr_gt_u(mem_opts); self.instruction(If(BlockType::Empty)); self.trap(Trap::StringOutOfBounds); self.instruction(End); @@ -2500,13 +2532,23 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(End); } - fn translate_list( + /// Shared preamble for translating list-like sequences (lists and maps). + /// + /// Emits: load ptr/len from source, compute byte lengths, malloc + /// destination, validate bounds, and if element sizes are non-zero opens + /// Block + Loop and initializes iteration locals. + /// + /// Returns a `SequenceTranslation` that the caller uses to emit the + /// loop body before calling `end_translate_sequence`. + fn begin_translate_sequence<'c>( &mut self, - src_ty: TypeListIndex, - src: &Source<'_>, - dst_ty: &InterfaceType, - dst: &Destination, - ) { + src: &Source<'c>, + dst: &Destination<'c>, + src_element_size: u32, + src_element_align: u32, + dst_element_size: u32, + dst_element_align: u32, + ) -> SequenceTranslation<'c> { let src_mem_opts = match &src.opts().data_model { DataModel::Gc {} => todo!("CM+GC"), DataModel::LinearMemory(opts) => opts, @@ -2516,20 +2558,13 @@ impl<'a, 'b> Compiler<'a, 'b> { DataModel::LinearMemory(opts) => opts, }; - let src_element_ty = &self.types[src_ty].element; - let dst_element_ty = match dst_ty { - InterfaceType::List(r) => &self.types[*r].element, - _ => panic!("expected a list"), - }; let src_opts = src.opts(); let dst_opts = dst.opts(); - let (src_size, src_align) = self.types.size_align(src_mem_opts, src_element_ty); - let (dst_size, dst_align) = self.types.size_align(dst_mem_opts, dst_element_ty); - // Load the pointer/length of this list into temporary locals. These - // will be referenced a good deal so this just makes it easier to deal - // with them consistently below rather than trying to reload from memory - // for example. + // Load the pointer/length of this sequence into temporary locals. + // These will be referenced a good deal so this just makes it easier + // to deal with them consistently below rather than trying to reload + // from memory for example. match src { Source::Stack(s) => { assert_eq!(s.locals.len(), 2); @@ -2547,30 +2582,35 @@ impl<'a, 'b> Compiler<'a, 'b> { // Create a `Memory` operand which will internally assert that the // `src_ptr` value is properly aligned. - let src_mem = self.memory_operand(src_opts, src_ptr, src_align); + let src_mem = self.memory_operand(src_opts, src_ptr, src_element_align); // Calculate the source/destination byte lengths into unique locals. - let src_byte_len = self.calculate_list_byte_len(src_mem_opts, src_len.idx, src_size); - let dst_byte_len = if src_size == dst_size { + let src_byte_len = + self.calculate_list_byte_len(src_mem_opts, src_len.idx, src_element_size); + let dst_byte_len = if src_element_size == dst_element_size { self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr()); self.local_set_new_tmp(dst_mem_opts.ptr()) } else if src_mem_opts.ptr() == dst_mem_opts.ptr() { - self.calculate_list_byte_len(dst_mem_opts, src_len.idx, dst_size) + self.calculate_list_byte_len(dst_mem_opts, src_len.idx, dst_element_size) } else { - self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr()); + self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr()); let tmp = self.local_set_new_tmp(dst_mem_opts.ptr()); - let ret = self.calculate_list_byte_len(dst_mem_opts, tmp.idx, dst_size); + let ret = self.calculate_list_byte_len(dst_mem_opts, tmp.idx, dst_element_size); self.free_temp_local(tmp); ret }; // Here `realloc` is invoked (in a `malloc`-like fashion) to allocate - // space for the list in the destination memory. This will also + // space for the sequence in the destination memory. This will also // internally insert checks that the returned pointer is aligned // correctly for the destination. - let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), dst_align); + let dst_mem = self.malloc( + dst_opts, + MallocSize::Local(dst_byte_len.idx), + dst_element_align, + ); - // With all the pointers and byte lengths verity that both the source + // With all the pointers and byte lengths verify that both the source // and the destination buffers are in-bounds. self.validate_memory_inbounds( src_mem_opts, @@ -2588,21 +2628,19 @@ impl<'a, 'b> Compiler<'a, 'b> { self.free_temp_local(src_byte_len); self.free_temp_local(dst_byte_len); - // This is the main body of the loop to actually translate list types. - // Note that if both element sizes are 0 then this won't actually do - // anything so the loop is removed entirely. - if src_size > 0 || dst_size > 0 { - // This block encompasses the entire loop and is use to exit before even - // entering the loop if the list size is zero. + // If both element sizes are 0 then there's nothing to copy so the + // loop is skipped entirely. Otherwise open a Block (for early exit + // on zero-length) and a Loop for the per-element iteration. + let loop_state = if src_element_size > 0 || dst_element_size > 0 { self.instruction(Block(BlockType::Empty)); - // Set the `remaining` local and only continue if it's > 0 + // Set the `remaining` local and only continue if it's > 0. self.instruction(LocalGet(src_len.idx)); let remaining = self.local_tee_new_tmp(src_mem_opts.ptr()); self.ptr_eqz(src_mem_opts); self.instruction(BrIf(0)); - // Initialize the two destination pointers to their initial values + // Initialize the two iteration pointers to their starting values. self.instruction(LocalGet(src_mem.addr.idx)); let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr()); self.instruction(LocalGet(dst_mem.addr.idx)); @@ -2610,70 +2648,288 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(Loop(BlockType::Empty)); - // Translate the next element in the list + Some(SequenceLoopState { + remaining, + cur_src_ptr, + cur_dst_ptr, + }) + } else { + None + }; + + SequenceTranslation { + src_len, + src_mem, + dst_mem, + src_opts, + dst_opts, + src_mem_opts, + dst_mem_opts, + loop_state, + } + } + + /// Shared epilogue for translating list-like sequences. + /// + /// If a loop was opened, emits: decrement remaining, BrIf to loop + /// head, End loop, End block, and frees iteration locals. Then stores + /// the ptr/len pair into the destination and frees all temporaries. + fn end_translate_sequence(&mut self, seq: SequenceTranslation<'_>, dst: &Destination) { + if let Some(loop_state) = seq.loop_state { + // Update the remaining count, falling through to break out if + // it's zero now. + self.instruction(LocalGet(loop_state.remaining.idx)); + self.ptr_iconst(seq.src_mem_opts, -1); + self.ptr_add(seq.src_mem_opts); + self.instruction(LocalTee(loop_state.remaining.idx)); + self.ptr_br_if(seq.src_mem_opts, 0); + self.instruction(End); // end of loop + self.instruction(End); // end of block + + self.free_temp_local(loop_state.cur_dst_ptr); + self.free_temp_local(loop_state.cur_src_ptr); + self.free_temp_local(loop_state.remaining); + } + + // Store the ptr/length in the desired destination. + match dst { + Destination::Stack(s, _) => { + self.instruction(LocalGet(seq.dst_mem.addr.idx)); + self.stack_set(&s[..1], seq.dst_mem_opts.ptr()); + self.convert_src_len_to_dst( + seq.src_len.idx, + seq.src_mem_opts.ptr(), + seq.dst_mem_opts.ptr(), + ); + self.stack_set(&s[1..], seq.dst_mem_opts.ptr()); + } + Destination::Memory(mem) => { + self.instruction(LocalGet(mem.addr.idx)); + self.instruction(LocalGet(seq.dst_mem.addr.idx)); + self.ptr_store(mem); + self.instruction(LocalGet(mem.addr.idx)); + self.convert_src_len_to_dst( + seq.src_len.idx, + seq.src_mem_opts.ptr(), + seq.dst_mem_opts.ptr(), + ); + self.ptr_store(&mem.bump(seq.dst_mem_opts.ptr_size().into())); + } + Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"), + } + + self.free_temp_local(seq.src_len); + self.free_temp_local(seq.src_mem.addr); + self.free_temp_local(seq.dst_mem.addr); + } + + fn translate_list( + &mut self, + src_ty: TypeListIndex, + src: &Source<'_>, + dst_ty: &InterfaceType, + dst: &Destination, + ) { + let src_mem_opts = match &src.opts().data_model { + DataModel::Gc {} => todo!("CM+GC"), + DataModel::LinearMemory(opts) => opts, + }; + let dst_mem_opts = match &dst.opts().data_model { + DataModel::Gc {} => todo!("CM+GC"), + DataModel::LinearMemory(opts) => opts, + }; + + let src_element_ty = &self.types[src_ty].element; + let dst_element_ty = match dst_ty { + InterfaceType::List(r) => &self.types[*r].element, + _ => panic!("expected a list"), + }; + let (src_size, src_align) = self.types.size_align(src_mem_opts, src_element_ty); + let (dst_size, dst_align) = self.types.size_align(dst_mem_opts, dst_element_ty); + + let seq = self.begin_translate_sequence(src, dst, src_size, src_align, dst_size, dst_align); + + if let Some(ref loop_state) = seq.loop_state { let element_src = Source::Memory(Memory { - opts: src_opts, + opts: seq.src_opts, offset: 0, - addr: TempLocal::new(cur_src_ptr.idx, cur_src_ptr.ty), + addr: TempLocal::new(loop_state.cur_src_ptr.idx, loop_state.cur_src_ptr.ty), }); let element_dst = Destination::Memory(Memory { - opts: dst_opts, + opts: seq.dst_opts, offset: 0, - addr: TempLocal::new(cur_dst_ptr.idx, cur_dst_ptr.ty), + addr: TempLocal::new(loop_state.cur_dst_ptr.idx, loop_state.cur_dst_ptr.ty), }); self.translate(src_element_ty, &element_src, dst_element_ty, &element_dst); - // Update the two loop pointers if src_size > 0 { - self.instruction(LocalGet(cur_src_ptr.idx)); + self.instruction(LocalGet(loop_state.cur_src_ptr.idx)); self.ptr_uconst(src_mem_opts, src_size); self.ptr_add(src_mem_opts); - self.instruction(LocalSet(cur_src_ptr.idx)); + self.instruction(LocalSet(loop_state.cur_src_ptr.idx)); } if dst_size > 0 { - self.instruction(LocalGet(cur_dst_ptr.idx)); + self.instruction(LocalGet(loop_state.cur_dst_ptr.idx)); self.ptr_uconst(dst_mem_opts, dst_size); self.ptr_add(dst_mem_opts); - self.instruction(LocalSet(cur_dst_ptr.idx)); + self.instruction(LocalSet(loop_state.cur_dst_ptr.idx)); } + } - // Update the remaining count, falling through to break out if it's zero - // now. - self.instruction(LocalGet(remaining.idx)); - self.ptr_iconst(src_mem_opts, -1); - self.ptr_add(src_mem_opts); - self.instruction(LocalTee(remaining.idx)); - self.ptr_br_if(src_mem_opts, 0); - self.instruction(End); // end of loop - self.instruction(End); // end of block + self.end_translate_sequence(seq, dst); + } - self.free_temp_local(cur_dst_ptr); - self.free_temp_local(cur_src_ptr); - self.free_temp_local(remaining); - } + /// Translates a map from one component's memory to another. + /// + /// In the Component Model, a `map` is stored in memory as + /// `list>`, so the translation reuses the same sequence + /// scaffolding as lists but with a two-field (key, value) loop body. + fn translate_map( + &mut self, + src_ty: TypeMapIndex, + src: &Source<'_>, + dst_ty: &InterfaceType, + dst: &Destination, + ) { + let src_mem_opts = match &src.opts().data_model { + DataModel::Gc {} => todo!("CM+GC"), + DataModel::LinearMemory(opts) => opts, + }; + let dst_mem_opts = match &dst.opts().data_model { + DataModel::Gc {} => todo!("CM+GC"), + DataModel::LinearMemory(opts) => opts, + }; - // Store the ptr/length in the desired destination - match dst { - Destination::Stack(s, _) => { - self.instruction(LocalGet(dst_mem.addr.idx)); - self.stack_set(&s[..1], dst_mem_opts.ptr()); - self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr()); - self.stack_set(&s[1..], dst_mem_opts.ptr()); + let src_map_ty = &self.types[src_ty]; + let dst_map_ty = match dst_ty { + InterfaceType::Map(r) => &self.types[*r], + _ => panic!("expected a map"), + }; + + // Each map entry is a tuple following record layout rules. + let src_key_abi = self.types.canonical_abi(&src_map_ty.key); + let src_value_abi = self.types.canonical_abi(&src_map_ty.value); + let src_entry_abi = CanonicalAbiInfo::record([src_key_abi, src_value_abi].into_iter()); + let (_, src_key_align) = self.types.size_align(src_mem_opts, &src_map_ty.key); + let (_, src_value_align) = self.types.size_align(src_mem_opts, &src_map_ty.value); + let (src_tuple_size, src_entry_align) = if src_mem_opts.memory64 { + (src_entry_abi.size64, src_entry_abi.align64) + } else { + (src_entry_abi.size32, src_entry_abi.align32) + }; + let src_value_offset = { + let mut offset = 0u32; + if src_mem_opts.memory64 { + src_key_abi.next_field64(&mut offset); + src_value_abi.next_field64(&mut offset) + } else { + src_key_abi.next_field32(&mut offset); + src_value_abi.next_field32(&mut offset) } - Destination::Memory(mem) => { - self.instruction(LocalGet(mem.addr.idx)); - self.instruction(LocalGet(dst_mem.addr.idx)); - self.ptr_store(mem); - self.instruction(LocalGet(mem.addr.idx)); - self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr()); - self.ptr_store(&mem.bump(dst_mem_opts.ptr_size().into())); + }; + + let dst_key_abi = self.types.canonical_abi(&dst_map_ty.key); + let dst_value_abi = self.types.canonical_abi(&dst_map_ty.value); + let dst_entry_abi = CanonicalAbiInfo::record([dst_key_abi, dst_value_abi].into_iter()); + let (_, dst_key_align) = self.types.size_align(dst_mem_opts, &dst_map_ty.key); + let (_, dst_value_align) = self.types.size_align(dst_mem_opts, &dst_map_ty.value); + let (dst_tuple_size, dst_entry_align) = if dst_mem_opts.memory64 { + (dst_entry_abi.size64, dst_entry_abi.align64) + } else { + (dst_entry_abi.size32, dst_entry_abi.align32) + }; + let dst_value_offset = { + let mut offset = 0u32; + if dst_mem_opts.memory64 { + dst_key_abi.next_field64(&mut offset); + dst_value_abi.next_field64(&mut offset) + } else { + dst_key_abi.next_field32(&mut offset); + dst_value_abi.next_field32(&mut offset) + } + }; + + let seq = self.begin_translate_sequence( + src, + dst, + src_tuple_size, + src_entry_align, + dst_tuple_size, + dst_entry_align, + ); + + if let Some(ref loop_state) = seq.loop_state { + let key_src = Source::Memory(self.memory_operand( + seq.src_opts, + TempLocal { + idx: loop_state.cur_src_ptr.idx, + ty: src_mem_opts.ptr(), + needs_free: false, + }, + src_key_align, + )); + let key_dst = Destination::Memory(self.memory_operand( + seq.dst_opts, + TempLocal { + idx: loop_state.cur_dst_ptr.idx, + ty: dst_mem_opts.ptr(), + needs_free: false, + }, + dst_key_align, + )); + self.translate(&src_map_ty.key, &key_src, &dst_map_ty.key, &key_dst); + + if src_value_offset > 0 { + self.instruction(LocalGet(loop_state.cur_src_ptr.idx)); + self.ptr_uconst(src_mem_opts, src_value_offset); + self.ptr_add(src_mem_opts); + self.instruction(LocalSet(loop_state.cur_src_ptr.idx)); + } + if dst_value_offset > 0 { + self.instruction(LocalGet(loop_state.cur_dst_ptr.idx)); + self.ptr_uconst(dst_mem_opts, dst_value_offset); + self.ptr_add(dst_mem_opts); + self.instruction(LocalSet(loop_state.cur_dst_ptr.idx)); + } + + let value_src = Source::Memory(self.memory_operand( + seq.src_opts, + TempLocal { + idx: loop_state.cur_src_ptr.idx, + ty: src_mem_opts.ptr(), + needs_free: false, + }, + src_value_align, + )); + let value_dst = Destination::Memory(self.memory_operand( + seq.dst_opts, + TempLocal { + idx: loop_state.cur_dst_ptr.idx, + ty: dst_mem_opts.ptr(), + needs_free: false, + }, + dst_value_align, + )); + self.translate(&src_map_ty.value, &value_src, &dst_map_ty.value, &value_dst); + + // Advance past value + trailing padding to the next entry + let src_advance_to_next = src_tuple_size - src_value_offset; + if src_advance_to_next > 0 { + self.instruction(LocalGet(loop_state.cur_src_ptr.idx)); + self.ptr_uconst(src_mem_opts, src_advance_to_next); + self.ptr_add(src_mem_opts); + self.instruction(LocalSet(loop_state.cur_src_ptr.idx)); + } + let dst_advance_to_next = dst_tuple_size - dst_value_offset; + if dst_advance_to_next > 0 { + self.instruction(LocalGet(loop_state.cur_dst_ptr.idx)); + self.ptr_uconst(dst_mem_opts, dst_advance_to_next); + self.ptr_add(dst_mem_opts); + self.instruction(LocalSet(loop_state.cur_dst_ptr.idx)); } - Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"), } - self.free_temp_local(src_len); - self.free_temp_local(src_mem.addr); - self.free_temp_local(dst_mem.addr); + self.end_translate_sequence(seq, dst); } fn calculate_list_byte_len( @@ -3777,11 +4033,11 @@ impl<'a, 'b> Compiler<'a, 'b> { } } - fn ptr_ge_u(&mut self, opts: &LinearMemoryOptions) { + fn ptr_gt_u(&mut self, opts: &LinearMemoryOptions) { if opts.memory64 { - self.instruction(I64GeU); + self.instruction(I64GtU); } else { - self.instruction(I32GeU); + self.instruction(I32GtU); } } @@ -3813,7 +4069,7 @@ impl<'a, 'b> Compiler<'a, 'b> { if opts.memory64 { self.instruction(I64Const(val.into())); } else { - self.instruction(I32Const(val as i32)); + self.instruction(I32Const(val.cast_signed())); } } @@ -4124,6 +4380,27 @@ where .0 } +/// State for the iteration loop inside a sequence translation. +struct SequenceLoopState { + remaining: TempLocal, + cur_src_ptr: TempLocal, + cur_dst_ptr: TempLocal, +} + +/// Holds all temporaries created by `begin_translate_sequence` so the +/// caller can emit a custom loop body before calling +/// `end_translate_sequence`. +struct SequenceTranslation<'a> { + src_len: TempLocal, + src_mem: Memory<'a>, + dst_mem: Memory<'a>, + src_opts: &'a Options, + dst_opts: &'a Options, + src_mem_opts: &'a LinearMemoryOptions, + dst_mem_opts: &'a LinearMemoryOptions, + loop_state: Option, +} + enum MallocSize { Const(u32), Local(u32), diff --git a/crates/environ/src/frame_table.rs b/crates/environ/src/frame_table.rs index e1ae0d72bbfb..c23a746b2653 100644 --- a/crates/environ/src/frame_table.rs +++ b/crates/environ/src/frame_table.rs @@ -4,9 +4,9 @@ //! section in a compiled artifact as produced by //! [`crate::compile::FrameTableBuilder`]. -use crate::FuncKey; +use crate::{FuncKey, ModulePC}; use alloc::vec::Vec; -use object::{Bytes, LittleEndian, U32Bytes}; +use object::{Bytes, LittleEndian, U32}; /// An index into the table of stack shapes. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -44,18 +44,18 @@ impl FrameTableDescriptorIndex { /// cheap to construct: it reads some header fields but does not /// interpret or validate content data until queried. pub struct FrameTable<'a> { - frame_descriptor_ranges: &'a [U32Bytes], + frame_descriptor_ranges: &'a [U32], frame_descriptor_data: &'a [u8], - frame_descriptor_fp_offsets: &'a [U32Bytes], + frame_descriptor_fp_offsets: &'a [U32], - progpoint_pcs: &'a [U32Bytes], - progpoint_descriptor_offsets: &'a [U32Bytes], - progpoint_descriptor_data: &'a [U32Bytes], + progpoint_pcs: &'a [U32], + progpoint_descriptor_offsets: &'a [U32], + progpoint_descriptor_data: &'a [U32], - breakpoint_pcs: &'a [U32Bytes], - breakpoint_patch_offsets: &'a [U32Bytes], - breakpoint_patch_data_ends: &'a [U32Bytes], + breakpoint_pcs: &'a [U32], + breakpoint_patch_offsets: &'a [U32], + breakpoint_patch_data_ends: &'a [U32], breakpoint_patch_data: &'a [u8], original_text: &'a [u8], @@ -67,67 +67,65 @@ impl<'a> FrameTable<'a> { pub fn parse(data: &'a [u8], original_text: &'a [u8]) -> anyhow::Result> { let mut data = Bytes(data); let num_frame_descriptors = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor count prefix"))?; let num_frame_descriptors = usize::try_from(num_frame_descriptors.get(LittleEndian))?; let num_progpoint_descriptors = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor count prefix"))?; let num_progpoint_descriptors = usize::try_from(num_progpoint_descriptors.get(LittleEndian))?; let num_breakpoints = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read breakpoint count prefix"))?; let num_breakpoints = usize::try_from(num_breakpoints.get(LittleEndian))?; let frame_descriptor_pool_length = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor pool length"))?; let frame_descriptor_pool_length = usize::try_from(frame_descriptor_pool_length.get(LittleEndian))?; let progpoint_descriptor_pool_length = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool length"))?; let progpoint_descriptor_pool_length = usize::try_from(progpoint_descriptor_pool_length.get(LittleEndian))?; let breakpoint_patch_pool_length = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch pool length"))?; let breakpoint_patch_pool_length = usize::try_from(breakpoint_patch_pool_length.get(LittleEndian))?; let (frame_descriptor_ranges, data) = - object::slice_from_bytes::>(data.0, 2 * num_frame_descriptors) + object::slice_from_bytes::>(data.0, 2 * num_frame_descriptors) .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor ranges slice"))?; let (frame_descriptor_fp_offsets, data) = - object::slice_from_bytes::>(data, num_frame_descriptors) + object::slice_from_bytes::>(data, num_frame_descriptors) .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor FP offset slice"))?; let (progpoint_pcs, data) = - object::slice_from_bytes::>(data, num_progpoint_descriptors) + object::slice_from_bytes::>(data, num_progpoint_descriptors) .map_err(|_| anyhow::anyhow!("Unable to read progpoint PC slice"))?; let (progpoint_descriptor_offsets, data) = - object::slice_from_bytes::>(data, num_progpoint_descriptors) + object::slice_from_bytes::>(data, num_progpoint_descriptors) .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor offset slice"))?; let (breakpoint_pcs, data) = - object::slice_from_bytes::>(data, num_breakpoints) + object::slice_from_bytes::>(data, num_breakpoints) .map_err(|_| anyhow::anyhow!("Unable to read breakpoint PC slice"))?; let (breakpoint_patch_offsets, data) = - object::slice_from_bytes::>(data, num_breakpoints) + object::slice_from_bytes::>(data, num_breakpoints) .map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch offsets slice"))?; let (breakpoint_patch_data_ends, data) = - object::slice_from_bytes::>(data, num_breakpoints) + object::slice_from_bytes::>(data, num_breakpoints) .map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch data ends slice"))?; let (frame_descriptor_data, data) = data .split_at_checked(frame_descriptor_pool_length) .ok_or_else(|| anyhow::anyhow!("Unable to read frame descriptor pool"))?; - let (progpoint_descriptor_data, data) = object::slice_from_bytes::>( - data, - progpoint_descriptor_pool_length, - ) - .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool"))?; + let (progpoint_descriptor_data, data) = + object::slice_from_bytes::>(data, progpoint_descriptor_pool_length) + .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool"))?; let (breakpoint_patch_data, _) = data .split_at_checked(breakpoint_patch_pool_length) @@ -181,7 +179,7 @@ impl<'a> FrameTable<'a> { &self, search_pc: u32, search_pos: FrameInstPos, - ) -> Option> { + ) -> Option> { let key = FrameInstPos::encode(search_pc, search_pos); let index = match self .progpoint_pcs @@ -203,7 +201,7 @@ impl<'a> FrameTable<'a> { Item = ( u32, FrameInstPos, - Vec<(u32, FrameTableDescriptorIndex, FrameStackShape)>, + Vec<(ModulePC, FrameTableDescriptorIndex, FrameStackShape)>, ), > + 'a { self.progpoint_pcs.iter().enumerate().map(move |(i, pc)| { @@ -220,7 +218,7 @@ impl<'a> FrameTable<'a> { fn program_point_frame_iter( &self, index: usize, - ) -> impl Iterator { + ) -> impl Iterator { let offset = usize::try_from(self.progpoint_descriptor_offsets[index].get(LittleEndian)).unwrap(); let mut data = &self.progpoint_descriptor_data[offset..]; @@ -229,12 +227,12 @@ impl<'a> FrameTable<'a> { if data.len() < 3 { return None; } - let wasm_pc = data[0].get(LittleEndian); + let wasm_pc_raw = data[0].get(LittleEndian); let frame_descriptor = FrameTableDescriptorIndex(data[1].get(LittleEndian)); let stack_shape = FrameStackShape(data[2].get(LittleEndian)); data = &data[3..]; - let not_last = wasm_pc & 0x8000_0000 != 0; - let wasm_pc = wasm_pc & 0x7fff_ffff; + let not_last = wasm_pc_raw & 0x8000_0000 != 0; + let wasm_pc = ModulePC::new(wasm_pc_raw & 0x7fff_ffff); if !not_last { data = &[]; } @@ -267,24 +265,25 @@ impl<'a> FrameTable<'a> { /// Find a list of breakpoint patches for a given Wasm PC. pub fn lookup_breakpoint_patches_by_pc( &self, - pc: u32, + pc: ModulePC, ) -> impl Iterator> + '_ { // Find *some* entry with a matching Wasm PC. Note that there // may be multiple entries for one PC. + let pc_raw = pc.raw(); let range = match self .breakpoint_pcs - .binary_search_by_key(&pc, |p| p.get(LittleEndian)) + .binary_search_by_key(&pc_raw, |p| p.get(LittleEndian)) { Ok(mut i) => { // Scan backward to first index with this PC. - while i > 0 && self.breakpoint_pcs[i - 1].get(LittleEndian) == pc { + while i > 0 && self.breakpoint_pcs[i - 1].get(LittleEndian) == pc_raw { i -= 1; } // Scan forward to find the end of the range. let mut end = i; while end < self.breakpoint_pcs.len() - && self.breakpoint_pcs[end].get(LittleEndian) == pc + && self.breakpoint_pcs[end].get(LittleEndian) == pc_raw { end += 1; } @@ -297,14 +296,31 @@ impl<'a> FrameTable<'a> { range.map(|i| self.breakpoint_patch(i)) } + /// Find the nearest breakpoint PC at or after the given PC. + pub fn nearest_breakpoint(&self, pc: ModulePC) -> Option { + match self + .breakpoint_pcs + .binary_search_by_key(&pc.raw(), |p| p.get(LittleEndian)) + { + Ok(_) => Some(pc), + Err(i) => { + if i < self.breakpoint_pcs.len() { + Some(ModulePC::new(self.breakpoint_pcs[i].get(LittleEndian))) + } else { + None + } + } + } + } + /// Return an iterator over all breakpoint patches. /// - /// Returned tuples are (Wasm PC, breakpoint data). + /// Returned tuples are (module-relative Wasm PC, breakpoint data). pub fn breakpoint_patches( &self, - ) -> impl Iterator)> + '_ { + ) -> impl Iterator)> + '_ { self.breakpoint_pcs.iter().enumerate().map(|(i, wasm_pc)| { - let wasm_pc = wasm_pc.get(LittleEndian); + let wasm_pc = ModulePC::new(wasm_pc.get(LittleEndian)); let data = self.breakpoint_patch(i); (wasm_pc, data) }) @@ -480,9 +496,9 @@ impl TryFrom for FrameValType { /// and for the stack given a stack shape. pub struct FrameStateSlot<'a> { func_key: FuncKey, - local_offsets: &'a [U32Bytes], - stack_shape_parents: &'a [U32Bytes], - stack_shape_offsets: &'a [U32Bytes], + local_offsets: &'a [U32], + stack_shape_parents: &'a [U32], + stack_shape_offsets: &'a [U32], local_types: &'a [u8], stack_shape_types: &'a [u8], } @@ -495,34 +511,34 @@ impl<'a> FrameStateSlot<'a> { pub fn parse(descriptor: &'a [u8]) -> anyhow::Result> { let mut data = Bytes(descriptor); let func_key_namespace = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read func key namespace"))? .get(LittleEndian); let func_key_index = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read func key index"))? .get(LittleEndian); let func_key = FuncKey::from_raw_parts(func_key_namespace, func_key_index); let num_locals = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read num_locals"))? .get(LittleEndian); let num_locals = usize::try_from(num_locals)?; let num_stack_shapes = data - .read::>() + .read::>() .map_err(|_| anyhow::anyhow!("Unable to read num_stack_shapes"))? .get(LittleEndian); let num_stack_shapes = usize::try_from(num_stack_shapes)?; let (local_offsets, data) = - object::slice_from_bytes::>(data.0, num_locals) + object::slice_from_bytes::>(data.0, num_locals) .map_err(|_| anyhow::anyhow!("Unable to read local_offsets slice"))?; let (stack_shape_parents, data) = - object::slice_from_bytes::>(data, num_stack_shapes) + object::slice_from_bytes::>(data, num_stack_shapes) .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_parents slice"))?; let (stack_shape_offsets, data) = - object::slice_from_bytes::>(data, num_stack_shapes) + object::slice_from_bytes::>(data, num_stack_shapes) .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_offsets slice"))?; let (local_types, data) = data .split_at_checked(num_locals) diff --git a/crates/environ/src/gc.rs b/crates/environ/src/gc.rs index 20a02cc81418..5de8ba4a2470 100644 --- a/crates/environ/src/gc.rs +++ b/crates/environ/src/gc.rs @@ -17,11 +17,25 @@ pub mod null; use crate::{ WasmArrayType, WasmCompositeInnerType, WasmCompositeType, WasmExnType, WasmStorageType, - WasmStructType, WasmValType, collections, error::OutOfMemory, prelude::*, + WasmStructType, WasmValType, error::OutOfMemory, prelude::*, }; use alloc::sync::Arc; use core::alloc::Layout; +/// Poison byte written over unallocated GC heap memory when `cfg(gc_zeal)` is +/// enabled. +pub const POISON: u8 = 0b00001111; + +/// Assert a condition, but only when `gc_zeal` is enabled. +#[macro_export] +macro_rules! gc_assert { + ($($arg:tt)*) => { + if cfg!(gc_zeal) { + assert!($($arg)*); + } + }; +} + /// Discriminant to check whether GC reference is an `i31ref` or not. pub const I31_DISCRIMINANT: u32 = 1; @@ -122,7 +136,7 @@ fn common_struct_or_exn_layout( fields: &[crate::WasmFieldType], header_size: u32, header_align: u32, -) -> (u32, u32, collections::Vec) { +) -> (u32, u32, TryVec) { use crate::PanicOnOom as _; // Process each field, aligning it to its natural alignment. @@ -145,7 +159,7 @@ fn common_struct_or_exn_layout( let is_gc_ref = f.element_type.is_vmgcref_type_and_not_i31(); GcStructLayoutField { offset, is_gc_ref } }) - .try_collect::, _>() + .try_collect::, _>() .panic_on_oom(); // Ensure that the final size is a multiple of the alignment, for @@ -377,7 +391,7 @@ pub struct GcStructLayout { /// The fields of this struct. The `i`th entry contains information about /// the `i`th struct field's layout. - pub fields: collections::Vec, + pub fields: TryVec, /// Whether this is an exception object layout. pub is_exception: bool, @@ -485,15 +499,16 @@ impl VMGcKind { #[inline] pub fn from_high_bits_of_u32(val: u32) -> VMGcKind { let masked = val & Self::MASK; - match masked { - x if x == Self::ExternRef.as_u32() => Self::ExternRef, - x if x == Self::AnyRef.as_u32() => Self::AnyRef, - x if x == Self::EqRef.as_u32() => Self::EqRef, - x if x == Self::ArrayRef.as_u32() => Self::ArrayRef, - x if x == Self::StructRef.as_u32() => Self::StructRef, - x if x == Self::ExnRef.as_u32() => Self::ExnRef, - _ => panic!("invalid `VMGcKind`: {masked:#032b}"), - } + let result = Self::try_from_u32(masked) + .unwrap_or_else(|| panic!("invalid `VMGcKind`: {masked:#032b}")); + + let poison_kind = u32::from_le_bytes([POISON, POISON, POISON, POISON]) & VMGcKind::MASK; + debug_assert_ne!( + masked, poison_kind, + "No valid `VMGcKind` should overlap with the poison pattern" + ); + + result } /// Does this kind match the other kind? @@ -509,6 +524,22 @@ impl VMGcKind { pub fn as_u32(self) -> u32 { self as u32 } + + /// Try to convert a `u32` into a `VMGcKind`. + /// + /// Returns `None` if the value doesn't match any known kind. + #[inline] + pub fn try_from_u32(x: u32) -> Option { + match x { + _ if x == Self::ExternRef.as_u32() => Some(Self::ExternRef), + _ if x == Self::AnyRef.as_u32() => Some(Self::AnyRef), + _ if x == Self::EqRef.as_u32() => Some(Self::EqRef), + _ if x == Self::ArrayRef.as_u32() => Some(Self::ArrayRef), + _ if x == Self::StructRef.as_u32() => Some(Self::StructRef), + _ if x == Self::ExnRef.as_u32() => Some(Self::ExnRef), + _ => None, + } + } } #[cfg(test)] diff --git a/crates/environ/src/graphs/scc.rs b/crates/environ/src/graphs/scc.rs index 37072db83b84..3a30f753b1f2 100644 --- a/crates/environ/src/graphs/scc.rs +++ b/crates/environ/src/graphs/scc.rs @@ -187,7 +187,7 @@ where &self.component_nodes[start..end] } - /// Iterate over each strongly-connnected component and the `Node`s that are + /// Iterate over each strongly-connected component and the `Node`s that are /// members of it. /// /// Iteration happens in reverse-topological order (successors are visited @@ -275,7 +275,7 @@ where /// ``` /// /// Note that a graph's condensation is always acyclic, because the - /// strongly-conneted components encapsulate and hide any cycles from the + /// strongly-connected components encapsulate and hide any cycles from the /// input graph. /// /// I am not aware of an existing name for the reverse (or transpose) diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index d667c9cceae2..c2f52c2f60a5 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -48,7 +48,7 @@ pub enum MemoryInitialization { /// data segments when the module is instantiated. /// /// This is the default memory initialization type. - Segmented(collections::Vec), + Segmented(TryVec), /// Memory initialization is statically known and involves a single `memcpy` /// or otherwise simply making the defined data visible. @@ -82,13 +82,13 @@ pub enum MemoryInitialization { /// /// The offset, range base, and range end are all guaranteed to be page /// aligned to the page size passed in to `try_static_init`. - map: collections::PrimaryMap>, + map: TryPrimaryMap>, }, } impl Default for MemoryInitialization { fn default() -> Self { - Self::Segmented(collections::Vec::new()) + Self::Segmented(TryVec::new()) } } @@ -226,14 +226,14 @@ pub struct TableInitialization { /// 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: collections::PrimaryMap, + pub initial_values: TryPrimaryMap, /// 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: collections::Vec, + pub segments: TryVec, } /// Initial value for all elements in a table. @@ -249,7 +249,7 @@ pub enum TableInitialValue { /// `FuncIndex::reserved_value()`. Note that this image is empty by /// default and may not encompass the entire span of the table in which /// case the elements are initialized to null. - precomputed: collections::Vec, + precomputed: TryVec, }, /// An arbitrary const expression. Expr(ConstExpr), @@ -301,10 +301,10 @@ pub struct Module { pub name: Option, /// All import records, in the order they are declared in the module. - pub initializers: collections::Vec, + pub initializers: TryVec, /// Exported entities. - pub exports: collections::IndexMap, + pub exports: TryIndexMap, /// The module "start" function, if present. pub start_func: Option, @@ -316,7 +316,7 @@ pub struct Module { pub memory_initialization: MemoryInitialization, /// WebAssembly passive elements. - pub passive_elements: collections::Vec, + pub passive_elements: TryVec, /// The map from passive element index (element segment index space) to index in `passive_elements`. pub passive_elements_map: BTreeMap, @@ -325,7 +325,7 @@ pub struct Module { pub passive_data_map: BTreeMap>, /// Types declared in the wasm module. - pub types: collections::PrimaryMap, + pub types: TryPrimaryMap, /// Number of imported or aliased functions in the module. pub num_imported_funcs: usize, @@ -353,22 +353,22 @@ pub struct Module { pub num_escaped_funcs: usize, /// Types of functions, imported and local. - pub functions: collections::PrimaryMap, + pub functions: TryPrimaryMap, /// WebAssembly tables. - pub tables: collections::PrimaryMap, + pub tables: TryPrimaryMap, /// WebAssembly linear memory plans. - pub memories: collections::PrimaryMap, + pub memories: TryPrimaryMap, /// WebAssembly global variables. - pub globals: collections::PrimaryMap, + pub globals: TryPrimaryMap, /// WebAssembly global initializers for locally-defined globals. - pub global_initializers: collections::PrimaryMap, + pub global_initializers: TryPrimaryMap, /// WebAssembly exception and control tags. - pub tags: collections::PrimaryMap, + pub tags: TryPrimaryMap, } /// Initialization routines for creating an instance, encompassing imports, diff --git a/crates/environ/src/module_artifacts.rs b/crates/environ/src/module_artifacts.rs index 1632defc3de2..6d6960b1ea60 100644 --- a/crates/environ/src/module_artifacts.rs +++ b/crates/environ/src/module_artifacts.rs @@ -4,7 +4,7 @@ use crate::prelude::*; use crate::{ EntityRef, FilePos, FuncIndex, FuncKey, FuncKeyIndex, FuncKeyKind, FuncKeyNamespace, Module, - PanicOnOom as _, collections::PrimaryMap, + PanicOnOom as _, }; use core::ops::Range; use core::{fmt, u32}; @@ -78,13 +78,13 @@ impl CompiledFunctionsTableBuilder { pub fn new() -> Self { Self { inner: CompiledFunctionsTable { - namespaces: PrimaryMap::new(), - func_loc_starts: PrimaryMap::new(), - sparse_starts: PrimaryMap::new(), - src_loc_starts: PrimaryMap::new(), - sparse_indices: PrimaryMap::new(), - func_locs: PrimaryMap::new(), - src_locs: PrimaryMap::new(), + namespaces: TryPrimaryMap::new(), + func_loc_starts: TryPrimaryMap::new(), + sparse_starts: TryPrimaryMap::new(), + src_loc_starts: TryPrimaryMap::new(), + sparse_indices: TryPrimaryMap::new(), + func_locs: TryPrimaryMap::new(), + src_locs: TryPrimaryMap::new(), }, } } @@ -289,7 +289,7 @@ pub struct CompiledFunctionsTable { /// their associated `NamespaceIndex`. That `NamespaceIndex` can then be /// used to find the range of other entity indices that are specific to that /// namespace. - namespaces: PrimaryMap, + namespaces: TryPrimaryMap, /// `self.func_loc_starts[i]..self.func_loc_starts[i+1]` describes the range /// within `self.func_locs` whose entries are associated with the namespace @@ -297,7 +297,7 @@ pub struct CompiledFunctionsTable { /// /// When `self.func_loc_starts[i+1]` is out of bounds, then the range is to /// the end of `self.func_locs`. - func_loc_starts: PrimaryMap, + func_loc_starts: TryPrimaryMap, /// `self.sparse_starts[i]..self.sparse_starts[i+1]` describes the range /// within `self.sparse_indices` whose entries are associated with the @@ -307,7 +307,7 @@ pub struct CompiledFunctionsTable { /// the end of `self.sparse_indices`. /// /// Entries are only valid for sparse, non-dense namespaces. - sparse_starts: PrimaryMap, + sparse_starts: TryPrimaryMap, /// `self.src_loc_starts[i]..self.src_loc_starts[i+1]` describes the range /// within `self.src_loc_indices` whose entries are associated with the @@ -318,7 +318,7 @@ pub struct CompiledFunctionsTable { /// /// Entries are only valid for namespaces whose functions have source /// locations. - src_loc_starts: PrimaryMap, + src_loc_starts: TryPrimaryMap, /// `self.sparse_indices[i]` contains the index part of /// `FuncKey::from_parts(ns, index)` where `ns` is determined by @@ -327,7 +327,7 @@ pub struct CompiledFunctionsTable { /// the namespace's start index.) /// /// This is sorted to allow for binary searches. - sparse_indices: PrimaryMap, + sparse_indices: TryPrimaryMap, /// `self.func_locs[i]` contains the location within the text section of /// `FuncKey::from_parts(self.namespaces[ns], i - start)`'s function, where @@ -338,14 +338,14 @@ pub struct CompiledFunctionsTable { /// /// The absence of a function location (for gaps in dense namespaces) is /// represented with `FunctionLoc::none()`. - func_locs: PrimaryMap, + func_locs: TryPrimaryMap, /// `self.src_locs[i]` contains the initial source location of /// `FuncKey::from_parts(self.namespaces[ns], i - start)`'s function, where /// `ns` and `start` are determined by `self.src_loc_starts`. /// /// The absence of a source location is represented by `FilePos::none()`. - src_locs: PrimaryMap, + src_locs: TryPrimaryMap, } impl CompiledFunctionsTable { diff --git a/crates/environ/src/module_types.rs b/crates/environ/src/module_types.rs index 0da4fb721d59..79af3abde9bb 100644 --- a/crates/environ/src/module_types.rs +++ b/crates/environ/src/module_types.rs @@ -1,7 +1,6 @@ use crate::{ ModuleInternedRecGroupIndex, ModuleInternedTypeIndex, PanicOnOom as _, TypeTrace, WasmSubType, - collections::{PrimaryMap, SecondaryMap}, - packed_option::PackedOption, + packed_option::PackedOption, prelude::*, }; use core::ops::{Index, Range}; use serde_derive::{Deserialize, Serialize}; @@ -12,9 +11,10 @@ use serde_derive::{Deserialize, Serialize}; /// implementations for this type. #[derive(Default, Serialize, Deserialize)] pub struct ModuleTypes { - rec_groups: PrimaryMap>, - wasm_types: PrimaryMap, - trampoline_types: SecondaryMap>, + rec_groups: TryPrimaryMap>, + wasm_types: TryPrimaryMap, + trampoline_types: + TrySecondaryMap>, } impl TypeTrace for ModuleTypes { diff --git a/crates/environ/src/prelude.rs b/crates/environ/src/prelude.rs index d49c74cd859e..70eba79316d3 100644 --- a/crates/environ/src/prelude.rs +++ b/crates/environ/src/prelude.rs @@ -20,10 +20,11 @@ //! and then `use crate::*` works as usual. pub use crate::collections::{ - EntitySet, TryClone, TryCollect, TryExtend, TryFromIterator, TryNew, TryString, TryToOwned, - try_new, + TryBTreeMap, TryClone, TryCollect, TryEntitySet, TryExtend, TryFromIterator, TryHashMap, + TryHashSet, TryIndexMap, TryNew, TryPrimaryMap, TrySecondaryMap, TryString, TryToOwned, TryVec, + try_new, try_vec, }; -pub use crate::error::{Context, Error, Result, bail, ensure, format_err}; +pub use crate::error::{Context, Error, OutOfMemory, Result, bail, ensure, format_err}; pub use alloc::borrow::ToOwned; pub use alloc::boxed::Box; pub use alloc::format; diff --git a/crates/environ/src/stack_map.rs b/crates/environ/src/stack_map.rs index e01230a62a41..fb6bba2d96a0 100644 --- a/crates/environ/src/stack_map.rs +++ b/crates/environ/src/stack_map.rs @@ -1,10 +1,10 @@ use cranelift_bitset::ScalarBitSet; -use object::{Bytes, LittleEndian, U32Bytes}; +use object::{Bytes, LittleEndian, U32}; struct StackMapSection<'a> { - pcs: &'a [U32Bytes], - pointers_to_stack_map: &'a [U32Bytes], - stack_map_data: &'a [U32Bytes], + pcs: &'a [U32], + pointers_to_stack_map: &'a [U32], + stack_map_data: &'a [U32], } impl<'a> StackMapSection<'a> { @@ -12,14 +12,13 @@ impl<'a> StackMapSection<'a> { let mut section = Bytes(section); // NB: this matches the encoding written by `append_to` in the // `compile::stack_map` module. - let pc_count = section.read::>().ok()?; + let pc_count = section.read::>().ok()?; let pc_count = usize::try_from(pc_count.get(LittleEndian)).ok()?; let (pcs, section) = - object::slice_from_bytes::>(section.0, pc_count).ok()?; + object::slice_from_bytes::>(section.0, pc_count).ok()?; let (pointers_to_stack_map, section) = - object::slice_from_bytes::>(section, pc_count).ok()?; - let stack_map_data = - object::slice_from_all_bytes::>(section).ok()?; + object::slice_from_bytes::>(section, pc_count).ok()?; + let stack_map_data = object::slice_from_all_bytes::>(section).ok()?; Some(StackMapSection { pcs, pointers_to_stack_map, @@ -65,7 +64,7 @@ impl<'a> StackMapSection<'a> { /// the docs over there. pub struct StackMap<'a> { frame_size: u32, - data: &'a [U32Bytes], + data: &'a [U32], } impl<'a> StackMap<'a> { diff --git a/crates/environ/src/string_pool.rs b/crates/environ/src/string_pool.rs index 241de57986b9..f72a1060bdf7 100644 --- a/crates/environ/src/string_pool.rs +++ b/crates/environ/src/string_pool.rs @@ -1,10 +1,6 @@ //! Simple string interning. -use crate::{ - collections::{HashMap, Vec}, - error::OutOfMemory, - prelude::*, -}; +use crate::{error::OutOfMemory, prelude::*}; use core::{fmt, mem, num::NonZeroU32}; /// An interned string associated with a particular string in a `StringPool`. @@ -32,11 +28,11 @@ pub struct Atom { pub struct StringPool { /// A map from each string in this pool (as an unsafe borrow from /// `self.strings`) to its `Atom`. - map: mem::ManuallyDrop>, + map: mem::ManuallyDrop>, /// Strings in this pool. These must never be mutated or reallocated once /// inserted. - strings: mem::ManuallyDrop>>, + strings: mem::ManuallyDrop>>, } impl Drop for StringPool { @@ -77,10 +73,20 @@ impl fmt::Debug for StringPool { impl TryClone for StringPool { fn try_clone(&self) -> Result { - Ok(StringPool { - map: self.map.try_clone()?, - strings: self.strings.try_clone()?, - }) + let mut new_pool = StringPool::new(); + // Re-intern strings in index order so that each Atom value is + // identical in the clone — callers that hold Atoms from the original + // can use them interchangeably with the clone. + // + // Directly cloning `self.map` would copy &'static str keys that point + // into the *original* pool's `strings` allocation. Those pointers + // become dangling once the original is dropped, leading to UB on any + // subsequent lookup. Re-interning ensures the cloned map's keys point + // into the clone's own `strings`. + for s in self.strings.iter() { + new_pool.insert(s)?; + } + Ok(new_pool) } } @@ -338,7 +344,7 @@ mod tests { let n = if cfg!(miri) { 100 } else { 10_000 }; for _ in 0..2 { - let atoms: Vec<_> = (0..n).map(|i| pool.insert(&i.to_string())).try_collect()?; + let atoms: TryVec<_> = (0..n).map(|i| pool.insert(&i.to_string())).try_collect()?; for atom in atoms { assert!(pool.contains(atom)); diff --git a/crates/environ/src/trap_encoding.rs b/crates/environ/src/trap_encoding.rs index cf011fe591f7..936aab7f062b 100644 --- a/crates/environ/src/trap_encoding.rs +++ b/crates/environ/src/trap_encoding.rs @@ -1,5 +1,5 @@ use core::fmt; -use object::{Bytes, LittleEndian, U32Bytes}; +use object::{Bytes, LittleEndian, U32}; /// Information about trap. #[derive(Debug, PartialEq, Eq, Clone)] @@ -216,6 +216,10 @@ generate_trap_type! { /// Cannot resume a thread which is not suspended. CannotResumeThread = "cannot resume thread which is not suspended", + /// Cannot issue a read/write on a future/stream while there is a + /// pending operation already. + ConcurrentFutureStreamOp = "cannot have concurrent operations active on a future/stream", + // if adding a variant here be sure to update `trap.rs` and `trap.h` as // mentioned above } @@ -253,13 +257,12 @@ pub fn lookup_trap_code(section: &[u8], offset: usize) -> Option { trap } -fn parse(section: &[u8]) -> Option<(&[U32Bytes], &[u8])> { +fn parse(section: &[u8]) -> Option<(&[U32], &[u8])> { let mut section = Bytes(section); // NB: this matches the encoding written by `append_to` above. - let count = section.read::>().ok()?; + let count = section.read::>().ok()?; let count = usize::try_from(count.get(LittleEndian)).ok()?; - let (offsets, traps) = - object::slice_from_bytes::>(section.0, count).ok()?; + let (offsets, traps) = object::slice_from_bytes::>(section.0, count).ok()?; debug_assert_eq!(traps.len(), count); Some((offsets, traps)) } diff --git a/crates/environ/src/types.rs b/crates/environ/src/types.rs index 066cc0cdc420..21b552636915 100644 --- a/crates/environ/src/types.rs +++ b/crates/environ/src/types.rs @@ -752,7 +752,7 @@ impl WasmFuncType { where D: serde::de::Deserializer<'de>, { - let tys: crate::collections::Vec = + let tys: crate::collections::TryVec = serde::Deserialize::deserialize(deserializer)?; let tys = tys .into_boxed_slice() @@ -766,7 +766,7 @@ impl WasmFuncType { params: impl IntoIterator, results: impl IntoIterator, ) -> Result { - let mut params_results: crate::collections::Vec<_> = params.into_iter().try_collect()?; + let mut params_results: crate::collections::TryVec<_> = params.into_iter().try_collect()?; let non_i31_gc_ref_params_count = params_results .iter() .filter(|p| p.is_vmgcref_type_and_not_i31()) @@ -2320,6 +2320,36 @@ impl Memory { let max = self.maximum_byte_size().unwrap_or(u64::MAX); max > tunables.memory_reservation } + + /// Tests whether this memory type is allowed to grow up to `size` bytes. + /// + /// This is only applicable to custom-page-size memories which have a page + /// size of a single byte. In that situation growth beyond `-1i32 as u32` + /// bytes is not allowed because at that point memory growth succeeding and + /// failing would be indistinguishable in the return value of `memory.grow`, + /// for example. To handle this 32-bit memories are only allowed to grow to + /// `-2i32 as u32`, for example, and 64-bit memories with a page size of 1 + /// are allowed to grow up to the maximum size. + pub fn allow_growth_to(&self, size: usize) -> bool { + if self.page_size_log2 != 0 { + return true; + } + match self.idx_type { + // For a 32-bit memory using 1-byte pages the last 2 bytes of the + // 32-bit address space are addressable but disallowed for now. A + // memory that is 4GiB in size cannot report its size via + // `memory.size`, and a memory that is 4GiB-1 bytes in size cannot + // be distinguished when 1 byte is added from an allocation + // failure. To handle this the memory is capped at 4GiB-2 which + // means that all memory-related instructions and such will have + // unambiguous return codes. + IndexType::I32 => size < 0xffff_ffff, + + // Assume that for a 64-bit memory using 1-byte pages it's going to + // exhaust system resources before a limit is actually reached. + IndexType::I64 => true, + } + } } #[derive(Copy, Clone, Debug)] diff --git a/crates/fiber/src/lib.rs b/crates/fiber/src/lib.rs index 3f8b1ef7f089..61920697782e 100644 --- a/crates/fiber/src/lib.rs +++ b/crates/fiber/src/lib.rs @@ -255,7 +255,7 @@ impl Suspend { inner: imp::Suspend, initial: Resume, func: impl FnOnce(Resume, &mut Suspend) -> Return, - ) { + ) -> imp::Suspend { let mut suspend = Suspend { inner, _phantom: PhantomData, @@ -278,7 +278,8 @@ impl Suspend { #[cfg(not(feature = "std"))] let result = RunResult::Returned((func)(initial, &mut suspend)); - suspend.inner.exit::(result); + suspend.inner.start_exit::(result); + suspend.inner } } @@ -428,4 +429,13 @@ mod tests { assert_eq!(fiber.resume(2.0), Err(4)); assert_eq!(fiber.resume(3.0), Ok("hello".to_string())); } + + #[test] + fn fiber_stack_max_size() { + if cfg!(windows) || cfg!(miri) { + return; + } + assert!(FiberStack::new(usize::MAX, true).is_err()); + assert!(FiberStack::new(usize::MAX, false).is_err()); + } } diff --git a/crates/fiber/src/miri.rs b/crates/fiber/src/miri.rs index a5049888268a..8382318c2dce 100644 --- a/crates/fiber/src/miri.rs +++ b/crates/fiber/src/miri.rs @@ -116,13 +116,17 @@ where // Execute this fiber through `Suspend::execute` and once that's done // deallocate the `state` that we have. let state = Arc::into_raw(state); - super::Suspend::::execute( + let mut suspend = super::Suspend::::execute( Suspend { state: state.cast(), }, init, func.0, ); + match suspend.block_until_notified::() { + State::Exiting => {} + _ => unreachable!(), + } unsafe { drop(Arc::from_raw(state)); } @@ -150,8 +154,7 @@ impl Fiber { let state = state.clone(); let func = IgnoreSendSync(func); move || run(state, func) - }) - .unwrap() + })? }; // Cast the fiber back into a raw pointer to lose the type parameters @@ -210,7 +213,7 @@ impl Fiber { } impl Suspend { - fn suspend(&mut self, result: RunResult) -> State { + fn set_result(&mut self, result: RunResult) { let state = unsafe { self.state() }; let mut lock = state.state.lock().unwrap(); @@ -219,9 +222,11 @@ impl Suspend { assert!(matches!(*lock, State::None)); *lock = State::SuspendWith(result); state.cond.notify_one(); + } - // Wait for the resumption to come back, which is returned from this - // method. + fn block_until_notified(&mut self) -> State { + let state = unsafe { self.state() }; + let mut lock = state.state.lock().unwrap(); lock = state .cond .wait_while(lock, |s| { @@ -232,17 +237,18 @@ impl Suspend { } pub(crate) fn switch(&mut self, result: RunResult) -> A { - match self.suspend(result) { + self.set_result(result); + + // Wait for the resumption to come back, which is returned from this + // method. + match self.block_until_notified::() { State::ResumeWith(RunResult::Resuming(a)) => a, _ => unreachable!(), } } - pub(crate) fn exit(&mut self, result: RunResult) { - match self.suspend(result) { - State::Exiting => {} - _ => unreachable!(), - } + pub(crate) fn start_exit(&mut self, result: RunResult) { + self.set_result(result); } unsafe fn state(&self) -> &SharedFiberState { diff --git a/crates/fiber/src/nostd.rs b/crates/fiber/src/nostd.rs index f7ff0ee4912d..13bab3dca566 100644 --- a/crates/fiber/src/nostd.rs +++ b/crates/fiber/src/nostd.rs @@ -28,7 +28,6 @@ use crate::stackswitch::*; use crate::{Result, RunResult, RuntimeFiberStack}; use alloc::boxed::Box; -use alloc::{vec, vec::Vec}; use core::cell::Cell; use core::ops::Range; use wasmtime_environ::prelude::*; @@ -42,7 +41,7 @@ pub struct FiberStack { len: usize, /// Backing storage, if owned. Allocated once at startup and then /// not reallocated afterward. - storage: Vec, + storage: TryVec, } struct BasePtr(*mut u8); @@ -66,10 +65,10 @@ impl FiberStack { pub fn new(size: usize, zeroed: bool) -> Result { // Round up the size to at least one page. let size = core::cmp::max(4096, size); - let mut storage = Vec::new(); - storage.reserve_exact(size); + let mut storage = TryVec::new(); + storage.reserve_exact(size)?; if zeroed { - storage.resize(size, 0); + storage.resize(size, 0)?; } let (base, len) = align_ptr(storage.as_mut_ptr(), size, STACK_ALIGN); Ok(FiberStack { @@ -81,7 +80,7 @@ impl FiberStack { pub unsafe fn from_raw_parts(base: *mut u8, guard_size: usize, len: usize) -> Result { Ok(FiberStack { - storage: vec![], + storage: TryVec::default(), base: BasePtr(unsafe { base.offset(isize::try_from(guard_size).unwrap()) }), len, }) @@ -115,14 +114,16 @@ pub struct Suspend { top_of_stack: *mut u8, } -extern "C" fn fiber_start(arg0: *mut u8, top_of_stack: *mut u8) +extern "C" fn fiber_start(arg0: *mut u8, top_of_stack: *mut u8) -> *mut u8 where F: FnOnce(A, &mut super::Suspend) -> C, { unsafe { let inner = Suspend { top_of_stack }; let initial = inner.take_resume::(); - super::Suspend::::execute(inner, initial, Box::from_raw(arg0.cast::())) + let inner = + super::Suspend::::execute(inner, initial, Box::from_raw(arg0.cast::())); + inner.top_of_stack } } @@ -137,7 +138,7 @@ impl Fiber { bail!("fibers unsupported on this host architecture"); } unsafe { - let data = Box::into_raw(Box::new(func)).cast(); + let data = Box::into_raw(try_new::>(func)?).cast(); wasmtime_fiber_init(stack.top().unwrap(), fiber_start::, data); } @@ -176,9 +177,10 @@ impl Suspend { } } - pub(crate) fn exit(&mut self, result: RunResult) { - self.switch(result); - unreachable!(); + pub(crate) fn start_exit(&mut self, result: RunResult) { + unsafe { + (*self.result_location::()).set(result); + } } unsafe fn take_resume(&self) -> A { diff --git a/crates/fiber/src/stackswitch.rs b/crates/fiber/src/stackswitch.rs index bf1842f2258d..a249e86c9563 100644 --- a/crates/fiber/src/stackswitch.rs +++ b/crates/fiber/src/stackswitch.rs @@ -65,7 +65,7 @@ mod unsupported { pub(crate) unsafe fn wasmtime_fiber_init( _top_of_stack: *mut u8, - _entry: extern "C" fn(*mut u8, *mut u8), + _entry: extern "C" fn(*mut u8, *mut u8) -> *mut u8, _entry_arg0: *mut u8, ) { unreachable!(); diff --git a/crates/fiber/src/stackswitch/aarch64.rs b/crates/fiber/src/stackswitch/aarch64.rs index f14e0628849b..c9ae0463429e 100644 --- a/crates/fiber/src/stackswitch/aarch64.rs +++ b/crates/fiber/src/stackswitch/aarch64.rs @@ -92,7 +92,7 @@ unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* x0 */) { pub(crate) unsafe fn wasmtime_fiber_init( top_of_stack: *mut u8, - entry_point: extern "C" fn(*mut u8, *mut u8), + entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, // x2 ) { #[repr(C)] @@ -132,6 +132,7 @@ pub(crate) unsafe fn wasmtime_fiber_init( x19: top_of_stack, x20: entry_point as *mut u8, x21: entry_arg0, + x22: wasmtime_fiber_switch_ as *mut u8, // We set up the newly initialized fiber, so that it resumes // execution from wasmtime_fiber_start(). As a result, we need a @@ -208,6 +209,11 @@ unsafe extern "C" fn wasmtime_fiber_start() -> ! { // ... and then we call the function! Note that this is a function call // so our frame stays on the stack to backtrace through. blr x20 + + // The entry function returns where to switch to as the final switch, so + // that's performed here in inline assembly. + blr x22 + // Unreachable, here for safety. This should help catch unexpected // behaviors. Use a noticeable payload so one can grep for it in the // codebase. diff --git a/crates/fiber/src/stackswitch/arm.rs b/crates/fiber/src/stackswitch/arm.rs index fe1937f46cb2..d145178a7349 100644 --- a/crates/fiber/src/stackswitch/arm.rs +++ b/crates/fiber/src/stackswitch/arm.rs @@ -36,7 +36,7 @@ unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* r0 */) { pub(crate) unsafe fn wasmtime_fiber_init( top_of_stack: *mut u8, - entry_point: extern "C" fn(*mut u8, *mut u8), + entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, ) { #[repr(C)] @@ -60,6 +60,7 @@ pub(crate) unsafe fn wasmtime_fiber_init( unsafe { let initial_stack = top_of_stack.cast::().sub(1); initial_stack.write(InitialStack { + r8: wasmtime_fiber_switch_ as *mut u8, r9: entry_arg0, r10: entry_point as *mut u8, r11: top_of_stack, @@ -103,6 +104,7 @@ unsafe extern "C" fn wasmtime_fiber_start() -> ! { mov r1, r11 mov r0, r9 blx r10 + blx r8 .cfi_endproc ", ); diff --git a/crates/fiber/src/stackswitch/riscv32imac.rs b/crates/fiber/src/stackswitch/riscv32imac.rs index 6072b6537e47..eac7b84826ed 100644 --- a/crates/fiber/src/stackswitch/riscv32imac.rs +++ b/crates/fiber/src/stackswitch/riscv32imac.rs @@ -72,7 +72,7 @@ unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* a0 */) { pub(crate) unsafe fn wasmtime_fiber_init( top_of_stack: *mut u8, - entry_point: extern "C" fn(*mut u8, *mut u8), + entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, ) { #[repr(C)] @@ -106,6 +106,7 @@ pub(crate) unsafe fn wasmtime_fiber_init( initial_stack.write(InitialStack { s1: entry_point as *mut u8, s2: entry_arg0, + s3: wasmtime_fiber_switch_ as *mut u8, fp: top_of_stack, ra: wasmtime_fiber_start as *mut u8, last_sp: initial_stack.cast(), @@ -147,6 +148,7 @@ unsafe extern "C" fn wasmtime_fiber_start() -> ! { mv a0, s2 mv a1, fp jalr s1 + jalr s3 // .4byte 0 will cause panic. // for safety just like x86_64.rs and riscv64.rs. .4byte 0 diff --git a/crates/fiber/src/stackswitch/riscv64.rs b/crates/fiber/src/stackswitch/riscv64.rs index f517995fd4f7..113a49ccbcbc 100644 --- a/crates/fiber/src/stackswitch/riscv64.rs +++ b/crates/fiber/src/stackswitch/riscv64.rs @@ -91,7 +91,7 @@ unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* a0 */) { pub(crate) unsafe fn wasmtime_fiber_init( top_of_stack: *mut u8, - entry_point: extern "C" fn(*mut u8, *mut u8), + entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, ) { #[repr(C)] @@ -126,6 +126,7 @@ pub(crate) unsafe fn wasmtime_fiber_init( initial_stack.write(InitialStack { s1: entry_point as *mut u8, s2: entry_arg0, + s3: wasmtime_fiber_switch_ as *mut u8, fp: top_of_stack, ra: wasmtime_fiber_start as *mut u8, last_sp: initial_stack.cast(), @@ -143,10 +144,10 @@ unsafe extern "C" fn wasmtime_fiber_start() -> ! { .cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \ - 5, /* the byte length of this expression */ \ - 0x52, /* DW_OP_reg2 (sp) */ \ - 0x06, /* DW_OP_deref */ \ - 0x08, 0xd0, /* DW_OP_const1u 0xc8 */ \ + 5, /* the byte length of this expression */ \ + 0x52, /* DW_OP_reg2 (sp) */ \ + 0x06, /* DW_OP_deref */ \ + 0x08, 0xd0, /* DW_OP_const1u 0xc8 */ \ 0x22 /* DW_OP_plus */ @@ -178,7 +179,9 @@ unsafe extern "C" fn wasmtime_fiber_start() -> ! { mv a0, s2 mv a1, fp - jalr s1 + jalr s1 // entry_point + jalr s3 // wasmtime_fiber_switch_ + // .4byte 0 will cause panic. // for safety just like x86_64.rs. .4byte 0 diff --git a/crates/fiber/src/stackswitch/s390x.rs b/crates/fiber/src/stackswitch/s390x.rs index 0d376b4647ce..52bbdbd2cc6d 100644 --- a/crates/fiber/src/stackswitch/s390x.rs +++ b/crates/fiber/src/stackswitch/s390x.rs @@ -52,7 +52,7 @@ unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* x0 */) { pub(crate) unsafe fn wasmtime_fiber_init( top_of_stack: *mut u8, - entry_point: extern "C" fn(*mut u8, *mut u8), + entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, ) { #[repr(C)] @@ -104,6 +104,7 @@ pub(crate) unsafe fn wasmtime_fiber_init( r6: top_of_stack, r7: entry_point as *mut u8, r8: entry_arg0, + r9: wasmtime_fiber_switch_ as *mut u8, last_sp: initial_stack.cast(), ..InitialStack::default() @@ -145,6 +146,10 @@ unsafe extern "C" fn wasmtime_fiber_start() -> ! { // ... and then we call the function! Note that this is a function call so // our frame stays on the stack to backtrace through. basr %r14, %r7 // entry_point + + // Perform the final switch. + basr %r14, %r9 // wasmtime_fiber_switch_ + // .. technically we shouldn't get here, so just trap. .word 0x0000 .cfi_endproc diff --git a/crates/fiber/src/stackswitch/x86.rs b/crates/fiber/src/stackswitch/x86.rs index 15b05ef92eee..1a09240dd42c 100644 --- a/crates/fiber/src/stackswitch/x86.rs +++ b/crates/fiber/src/stackswitch/x86.rs @@ -47,7 +47,7 @@ unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8) { pub(crate) unsafe fn wasmtime_fiber_init( top_of_stack: *mut u8, - entry_point: extern "C" fn(*mut u8, *mut u8), + entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, ) { // Our stack from top-to-bottom looks like: @@ -84,6 +84,7 @@ pub(crate) unsafe fn wasmtime_fiber_init( let initial_stack = top_of_stack.cast::().sub(1); initial_stack.write(InitialStack { ebp: entry_point as *mut u8, + esi: wasmtime_fiber_switch_ as *mut u8, return_address: wasmtime_fiber_start as *mut u8, arg1: entry_arg0, arg2: top_of_stack, @@ -112,8 +113,15 @@ unsafe extern "C" fn wasmtime_fiber_start() -> ! { .cfi_rel_offset edi, -20 // Our arguments and stack alignment are all prepped by - // `wasmtime_fiber_init`. + // `wasmtime_fiber_init`. After calling `entry_point` clean up the + // stack arguments. call ebp + pop edi + pop edi + + // Call `wasmtime_fiber_switch_`, pushing its argument onto the stack. + push eax + call esi ud2 .cfi_endproc ", diff --git a/crates/fiber/src/stackswitch/x86_64.rs b/crates/fiber/src/stackswitch/x86_64.rs index eb3ec36980e6..5cea3ac715e9 100644 --- a/crates/fiber/src/stackswitch/x86_64.rs +++ b/crates/fiber/src/stackswitch/x86_64.rs @@ -50,7 +50,7 @@ unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* rdi */) { pub(crate) unsafe fn wasmtime_fiber_init( top_of_stack: *mut u8, - entry_point: extern "C" fn(*mut u8, *mut u8), + entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, ) { #[repr(C)] @@ -72,6 +72,7 @@ pub(crate) unsafe fn wasmtime_fiber_init( unsafe { let initial_stack = top_of_stack.cast::().sub(1); initial_stack.write(InitialStack { + r13: wasmtime_fiber_switch_ as *mut u8, r12: entry_arg0, rbx: entry_point as *mut u8, rbp: top_of_stack, @@ -155,12 +156,18 @@ unsafe extern "C" fn wasmtime_fiber_start() -> ! { // `wasmtime_fiber_init` routine arranged the various values to be // materialized into the registers used here. Our job is to then move // the values into the ABI-defined registers and call the entry-point. - // Note that `call` is used here to leave this frame on the stack so we - // can use the dwarf info here for unwinding. The trailing `ud2` is just - // for safety. mov rdi, r12 mov rsi, rbp - call rbx + call rbx // entry_point + + // Once the entrypoint has returned the final switch for this fiber is + // executed. The address of the routine is in `r13` and its argument is + // the return value of the startup routine. + mov rdi, rax + call r13 // wasmtime_fiber_switch_ + + // This should never be reached, but in case it accidentally does then + // terminate immediately. ud2 .cfi_endproc ", diff --git a/crates/fiber/src/unix.rs b/crates/fiber/src/unix.rs index 1d8caeb11e1c..d39cb12fd03f 100644 --- a/crates/fiber/src/unix.rs +++ b/crates/fiber/src/unix.rs @@ -33,12 +33,13 @@ use crate::stackswitch::*; use crate::{RunResult, RuntimeFiberStack}; use std::boxed::Box; use std::cell::Cell; -use std::io; use std::ops::Range; use std::ptr; use std::sync::atomic::{AtomicUsize, Ordering}; +use wasmtime_environ::error::{OutOfMemory, Result}; +use wasmtime_environ::prelude::*; -pub type Error = io::Error; +pub type Error = wasmtime_environ::error::Error; pub struct FiberStack { base: BasePtr, @@ -78,9 +79,9 @@ fn host_page_size() -> usize { } impl FiberStack { - pub fn new(size: usize, zeroed: bool) -> io::Result { + pub fn new(size: usize, zeroed: bool) -> Result { let page_size = host_page_size(); - // The anonymous `mmap`s we use for `FiberStackStorage` are alawys + // The anonymous `mmap`s we use for `FiberStackStorage` are always // zeroed. let _ = zeroed; @@ -101,7 +102,7 @@ impl FiberStack { }) } - pub unsafe fn from_raw_parts(base: *mut u8, guard_size: usize, len: usize) -> io::Result { + pub unsafe fn from_raw_parts(base: *mut u8, guard_size: usize, len: usize) -> Result { // See comments in `mod asan` below for why asan has a different stack // allocation strategy. if cfg!(asan) { @@ -118,7 +119,7 @@ impl FiberStack { matches!(self.storage, FiberStackStorage::Unmanaged(_)) } - pub fn from_custom(custom: Box) -> io::Result { + pub fn from_custom(custom: Box) -> Result { let range = custom.range(); let page_size = host_page_size(); let start_ptr = range.start as *mut u8; @@ -168,14 +169,17 @@ unsafe impl Send for MmapFiberStack {} unsafe impl Sync for MmapFiberStack {} impl MmapFiberStack { - fn new(size: usize) -> io::Result { + fn new(size: usize) -> Result { // Round up our stack size request to the nearest multiple of the // page size. let page_size = host_page_size(); let size = if size == 0 { page_size } else { - (size + (page_size - 1)) & (!(page_size - 1)) + let with_extra = size + .checked_add(page_size - 1) + .ok_or_else(|| OutOfMemory::new(usize::MAX))?; + with_extra & (!(page_size - 1)) }; unsafe { @@ -218,7 +222,7 @@ pub struct Suspend { previous: asan::PreviousStack, } -extern "C" fn fiber_start(arg0: *mut u8, top_of_stack: *mut u8) +extern "C" fn fiber_start(arg0: *mut u8, top_of_stack: *mut u8) -> *mut u8 where F: FnOnce(A, &mut super::Suspend) -> C, { @@ -232,25 +236,25 @@ where previous, }; let initial = inner.take_resume::(); - super::Suspend::::execute(inner, initial, Box::from_raw(arg0.cast::())) + let mut inner = + super::Suspend::::execute(inner, initial, Box::from_raw(arg0.cast::())); + asan::fiber_exit(&mut inner.previous); + inner.top_of_stack } } impl Fiber { - pub fn new(stack: &FiberStack, func: F) -> io::Result + pub fn new(stack: &FiberStack, func: F) -> Result where F: FnOnce(A, &mut super::Suspend) -> C, { // On unsupported platforms `wasmtime_fiber_init` is a panicking shim so // return an error saying the host architecture isn't supported instead. if !SUPPORTED_ARCH { - return Err(io::Error::new( - io::ErrorKind::Other, - "fibers not supported on this host architecture", - )); + bail!("fibers not supported on this host architecture"); } unsafe { - let data = Box::into_raw(Box::new(func)).cast(); + let data = Box::into_raw(try_new::>(func)?).cast(); wasmtime_fiber_init(stack.top().unwrap(), fiber_start::, data); } @@ -266,11 +270,7 @@ impl Fiber { let addr = stack.top().unwrap().cast::().offset(-1); addr.write(result as *const _ as usize); - asan::fiber_switch( - stack.top().unwrap(), - false, - &mut asan::PreviousStack::new(stack), - ); + asan::fiber_switch(stack.top().unwrap(), &mut asan::PreviousStack::new(stack)); // null this out to help catch use-after-free addr.write(0); @@ -283,22 +283,19 @@ impl Fiber { impl Suspend { pub(crate) fn switch(&mut self, result: RunResult) -> A { unsafe { - let is_finishing = match &result { - RunResult::Returned(_) | RunResult::Panicked(_) => true, - RunResult::Executing | RunResult::Resuming(_) | RunResult::Yield(_) => false, - }; // Calculate 0xAff8 and then write to it (*self.result_location::()).set(result); - asan::fiber_switch(self.top_of_stack, is_finishing, &mut self.previous); + asan::fiber_switch(self.top_of_stack, &mut self.previous); self.take_resume::() } } - pub(crate) fn exit(&mut self, result: RunResult) { - self.switch(result); - unreachable!() + pub(crate) fn start_exit(&mut self, result: RunResult) { + unsafe { + (*self.result_location::()).set(result); + } } unsafe fn take_resume(&self) -> A { @@ -331,7 +328,7 @@ impl Suspend { /// called around every stack switch with some other fiddly bits as well. #[cfg(asan)] mod asan { - use super::{FiberStack, MmapFiberStack, RuntimeFiberStack, host_page_size}; + use super::*; use alloc::boxed::Box; use alloc::vec::Vec; use std::mem::ManuallyDrop; @@ -375,23 +372,10 @@ mod asan { /// final time; customizes how asan intrinsics are invoked. /// * `prev` - the stack we're switching to initially and saves the /// stack to return to upon resumption. - pub unsafe fn fiber_switch( - top_of_stack: *mut u8, - is_finishing: bool, - prev: &mut PreviousStack, - ) { + pub unsafe fn fiber_switch(top_of_stack: *mut u8, prev: &mut PreviousStack) { assert!(super::SUPPORTED_ARCH); let mut private_asan_pointer = std::ptr::null_mut(); - // If this fiber is finishing then NULL is passed to asan to let it know - // that it can deallocate the "fake stack" that it's tracking for this - // fiber. - let private_asan_pointer_ref = if is_finishing { - None - } else { - Some(&mut private_asan_pointer) - }; - // NB: in fiddling with asan an optimizations and such it appears that // these functions need to be "very close to each other". If other Rust // functions are invoked or added as an abstraction here that appears to @@ -399,12 +383,19 @@ mod asan { // module as-is where this function exists to have these three // functions very close to one another. unsafe { - __sanitizer_start_switch_fiber(private_asan_pointer_ref, prev.bottom, prev.size); + __sanitizer_start_switch_fiber(Some(&mut private_asan_pointer), prev.bottom, prev.size); super::wasmtime_fiber_switch(top_of_stack); __sanitizer_finish_switch_fiber(private_asan_pointer, &mut prev.bottom, &mut prev.size); } } + pub unsafe fn fiber_exit(prev: &mut PreviousStack) { + assert!(super::SUPPORTED_ARCH); + unsafe { + __sanitizer_start_switch_fiber(None, prev.bottom, prev.size); + } + } + /// Hook for when a fiber first starts, used to configure ASAN. pub unsafe fn fiber_start_complete() -> PreviousStack { let mut ret = PreviousStack::default(); @@ -449,9 +440,11 @@ mod asan { /// meaning that this should only ever be a relatively small set of stacks. static FIBER_STACKS: Mutex> = Mutex::new(Vec::new()); - pub fn new_fiber_stack(size: usize) -> std::io::Result> { + pub fn new_fiber_stack(size: usize) -> Result> { let page_size = host_page_size(); - let needed_size = size + page_size; + let needed_size = size + .checked_add(page_size) + .ok_or_else(|| OutOfMemory::new(usize::MAX))?; let mut stacks = FIBER_STACKS.lock().unwrap(); let stack = match stacks.iter().position(|i| needed_size <= i.mapping_len) { @@ -505,7 +498,7 @@ mod asan { // Shim module that's the same as above but only has stubs. #[cfg(not(asan))] mod asan_disabled { - use super::{FiberStack, RuntimeFiberStack}; + use super::*; use std::boxed::Box; #[derive(Default)] @@ -518,23 +511,21 @@ mod asan_disabled { } } - pub unsafe fn fiber_switch( - top_of_stack: *mut u8, - _is_finishing: bool, - _prev: &mut PreviousStack, - ) { + pub unsafe fn fiber_switch(top_of_stack: *mut u8, _prev: &mut PreviousStack) { assert!(super::SUPPORTED_ARCH); unsafe { super::wasmtime_fiber_switch(top_of_stack); } } + pub unsafe fn fiber_exit(_prev: &mut PreviousStack) {} + #[inline] pub unsafe fn fiber_start_complete() -> PreviousStack { PreviousStack } - pub fn new_fiber_stack(_size: usize) -> std::io::Result> { + pub fn new_fiber_stack(_size: usize) -> Result> { unimplemented!() } } diff --git a/crates/fiber/src/windows.rs b/crates/fiber/src/windows.rs index f3d66c94609b..2a85d1ca8952 100644 --- a/crates/fiber/src/windows.rs +++ b/crates/fiber/src/windows.rs @@ -90,7 +90,14 @@ where (*state).initial_closure.set(ptr::null_mut()); let suspend = Suspend { state }; let initial = suspend.take_resume::(); - super::Suspend::::execute(suspend, initial, *func); + let suspend = super::Suspend::::execute(suspend, initial, *func); + + let parent = (*suspend.state).parent.get(); + debug_assert!(!parent.is_null()); + + // Technically this means that this function `fiber_start` never + // returns, but that's how the fibers API works on Windows it seems. + SwitchToFiber(parent); } } @@ -187,9 +194,10 @@ impl Suspend { } } - pub(crate) fn exit(&mut self, result: RunResult) { - self.switch(result); - unreachable!() + pub(crate) fn start_exit(&mut self, result: RunResult) { + unsafe { + (*self.result_location::()).set(result); + } } unsafe fn take_resume(&self) -> A { diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 044a629e8c5b..cd661aad92dc 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -15,6 +15,7 @@ workspace = true wasmtime-test-util = { workspace = true, features = ['wast'] } [dependencies] +wasmtime-environ = { workspace = true } backtrace = { workspace = true } arbitrary = { workspace = true, features = ["derive"] } env_logger = { workspace = true } @@ -28,7 +29,7 @@ wasmparser = { workspace = true } wasmprinter = { workspace = true } wasmtime-wast = { workspace = true, features = ['component-model'] } wasmtime = { workspace = true, features = ['default', 'winch'] } -wasmtime-core = { workspace = true, features = ['backtrace'] } +wasmtime-core = { workspace = true, features = ['backtrace', 'serde'] } wasm-encoder = { workspace = true } wasm-smith = { workspace = true, features = ['serde'] } wasm-mutate = { workspace = true } @@ -73,6 +74,7 @@ rand = { version = "0.8.0", features = ["small_rng"] } wasmtime-environ = { workspace = true } cranelift-bforest = { workspace = true } cranelift-bitset = { workspace = true } +tokio = { workspace = true, features = ["rt", "time", "macros", "rt-multi-thread"] } # Only enable the `build-libinterpret` feature when fuzzing is enabled, enabling # commands like `cargo test --workspace` or similar to not need an ocaml @@ -82,6 +84,3 @@ wasm-spec-interpreter = { path = "./wasm-spec-interpreter", optional = true, fea [features] fuzz-spec-interpreter = ['dep:wasm-spec-interpreter'] - -# Fuzz proof-carrying code. Off by default. -fuzz-pcc = [] diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs index 1f04f0779293..a96a582f836c 100644 --- a/crates/fuzzing/src/generators.rs +++ b/crates/fuzzing/src/generators.rs @@ -13,6 +13,7 @@ mod async_config; mod codegen_settings; pub mod component_async; mod config; +pub mod exception_ops; pub mod gc_ops; mod instance_allocation_strategy; mod memory; @@ -27,6 +28,7 @@ pub use async_config::AsyncConfig; pub use codegen_settings::CodegenSettings; pub use config::CompilerStrategy; pub use config::{Config, WasmtimeConfig}; +pub use exception_ops::ExceptionOps; pub use gc_ops::{limits::GcOpsLimits, ops::GcOps}; pub use instance_allocation_strategy::InstanceAllocationStrategy; pub use memory::{HeapImage, MemoryAccesses, MemoryConfig}; diff --git a/crates/fuzzing/src/generators/component_async.rs b/crates/fuzzing/src/generators/component_async.rs index a7dc1ac213cd..cc4d542ffbe6 100644 --- a/crates/fuzzing/src/generators/component_async.rs +++ b/crates/fuzzing/src/generators/component_async.rs @@ -151,7 +151,7 @@ impl HandleStates { #[derive(Default)] struct HalfStates { /// All known handles of this type, where they're located, etc. - handles: IndexMap, + handles: IndexMap, /// All handles which can currently be dropped. Handles can't be dropped if /// they're in use, for example. @@ -164,12 +164,12 @@ struct HalfStates { /// All handles which can be transferred somewhere else. /// - /// Some examples of non-transferrable handles are: + /// Some examples of non-transferable handles are: /// /// * writers /// * handles with an outstanding read /// * host-based handles that have been used at least once (FIXME #12090) - transferrable: IndexSet, + transferable: IndexSet, /// Handles currently being read/written to. /// @@ -187,7 +187,7 @@ enum HalfState { } #[derive(Copy, Clone, PartialEq, Debug)] -enum Transferrable { +enum Transferable { Yes, No, } @@ -221,25 +221,25 @@ impl HalfStates { } /// Adds a new handle `id` to this set. - fn insert(&mut self, id: u32, scope: Scope, transferrable: Transferrable) { + fn insert(&mut self, id: u32, scope: Scope, transferable: Transferable) { let prev = self .handles - .insert(id, (scope, HalfState::Idle, transferrable)); + .insert(id, (scope, HalfState::Idle, transferable)); assert!(prev.is_none()); assert!(self.droppable.insert(id)); - if transferrable == Transferrable::Yes { - self.transferrable.insert(id); + if transferable == Transferable::Yes { + self.transferable.insert(id); } } /// Removes the handle `id` for closing. fn remove(&mut self, id: u32) -> Scope { - let (scope, state, transferrable) = self.handles.swap_remove(&id).unwrap(); + let (scope, state, transferable) = self.handles.swap_remove(&id).unwrap(); assert!(matches!(state, HalfState::Idle)); self.droppable.swap_remove(&id); self.ready.swap_remove(&id); - if transferrable == Transferrable::Yes { - assert!(self.transferrable.swap_remove(&id)); + if transferable == Transferable::Yes { + assert!(self.transferable.swap_remove(&id)); } scope } @@ -248,25 +248,25 @@ impl HalfStates { /// lifetime, preventing its transfer. This is used as a workaround for /// #12090. fn lock_in_place(&mut self, id: u32) { - let (_scope, state, transferrable) = self.handles.get_mut(&id).unwrap(); + let (_scope, state, transferable) = self.handles.get_mut(&id).unwrap(); assert!(matches!(state, HalfState::Idle)); - if matches!(transferrable, Transferrable::Yes) { - assert!(self.transferrable.swap_remove(&id)); - *transferrable = Transferrable::No; + if matches!(transferable, Transferable::Yes) { + assert!(self.transferable.swap_remove(&id)); + *transferable = Transferable::No; } } /// Starts an operation on the handle `id`. fn start(&mut self, id: u32, cancellable: Cancellable, payload: T) { - let (_scope, state, transferrable) = self.handles.get_mut(&id).unwrap(); + let (_scope, state, transferable) = self.handles.get_mut(&id).unwrap(); assert!(matches!(state, HalfState::Idle)); assert!(self.ready.swap_remove(&id)); self.droppable.swap_remove(&id); *state = HalfState::InUse; let prev = self.in_use.insert(id, (payload, OpState::Pending)); assert!(prev.is_none()); - if *transferrable == Transferrable::Yes { - assert!(self.transferrable.swap_remove(&id)); + if *transferable == Transferable::Yes { + assert!(self.transferable.swap_remove(&id)); } if cancellable == Cancellable::Yes { assert!(self.cancellable.insert(id)); @@ -276,13 +276,13 @@ impl HalfStates { /// Completes an operation on `id`, returning the state it was started with /// along with whether it was dropped. fn stop(&mut self, id: u32) -> (T, OpState) { - let (_scope, state, transferrable) = self.handles.get_mut(&id).unwrap(); + let (_scope, state, transferable) = self.handles.get_mut(&id).unwrap(); assert!(matches!(state, HalfState::InUse)); *state = HalfState::Idle; let dropped = self.in_use.swap_remove(&id).unwrap(); self.cancellable.swap_remove(&id); - if *transferrable == Transferrable::Yes { - assert!(self.transferrable.insert(id)); + if *transferable == Transferable::Yes { + assert!(self.transferable.insert(id)); } assert!(self.droppable.insert(id)); if dropped.1 != OpState::Dropped { @@ -374,7 +374,7 @@ impl State { if self.futures.readers.ready.len() > 0 { choices.push(Choice::FutureRead); } - if self.futures.readers.transferrable.len() > 0 { + if self.futures.readers.transferable.len() > 0 { choices.push(Choice::FutureReaderTransfer); } } @@ -396,7 +396,7 @@ impl State { // If more work is allowed then streams can be moved around and new // reads/writes may be started. if !finish { - if self.streams.readers.transferrable.len() > 0 { + if self.streams.readers.transferable.len() > 0 { choices.push(Choice::StreamReaderTransfer); } if self.streams.readers.ready.len() > 0 { @@ -483,14 +483,14 @@ impl State { let scope = *u.choose(Scope::ALL)?; let id = self.next_id(); commands.push((scope, Command::FutureNew(id))); - self.futures.readers.insert(id, scope, Transferrable::Yes); - self.futures.writers.insert(id, scope, Transferrable::No); + self.futures.readers.insert(id, scope, Transferable::Yes); + self.futures.writers.insert(id, scope, Transferable::No); // Future writers cannot be dropped without writing. assert!(self.futures.writers.droppable.swap_remove(&id)); } Choice::FutureReaderTransfer => { - let set = &mut self.futures.readers.transferrable; + let set = &mut self.futures.readers.transferable; let i = u.int_in_range(0..=set.len() - 1)?; let id = *set.get_index(i).unwrap(); let scope = &mut self.futures.readers.handles[&id].0; @@ -672,11 +672,11 @@ impl State { let scope = *u.choose(Scope::ALL)?; let id = self.next_id(); commands.push((scope, Command::StreamNew(id))); - self.streams.readers.insert(id, scope, Transferrable::Yes); - self.streams.writers.insert(id, scope, Transferrable::No); + self.streams.readers.insert(id, scope, Transferable::Yes); + self.streams.writers.insert(id, scope, Transferable::No); } Choice::StreamReaderTransfer => { - let set = &mut self.streams.readers.transferrable; + let set = &mut self.streams.readers.transferable; let i = u.int_in_range(0..=set.len() - 1)?; let id = *set.get_index(i).unwrap(); let scope = &mut self.streams.readers.handles[&id].0; diff --git a/crates/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs index cb3544982e2d..f6a0a6f65473 100644 --- a/crates/fuzzing/src/generators/config.rs +++ b/crates/fuzzing/src/generators/config.rs @@ -122,6 +122,7 @@ impl Config { /// to execute all tests. pub fn make_wast_test_compliant(&mut self, test: &WastTest) -> WastConfig { let wasmtime_test_util::wast::TestConfig { + bulk_memory, memory64, custom_page_sizes, multi_memory, @@ -140,6 +141,7 @@ impl Config { component_model_threading, component_model_error_context, component_model_gc, + component_model_map, component_model_fixed_length_lists, simd, exceptions, @@ -166,13 +168,14 @@ impl Config { self.module_config.component_model_error_context = component_model_error_context.unwrap_or(false); self.module_config.component_model_gc = component_model_gc.unwrap_or(false); + self.module_config.component_model_map = component_model_map.unwrap_or(false); self.module_config.component_model_fixed_length_lists = component_model_fixed_length_lists.unwrap_or(false); // Enable/disable proposals that wasm-smith has knobs for which will be // read when creating `wasmtime::Config`. let config = &mut self.module_config.config; - config.bulk_memory_enabled = true; + config.bulk_memory_enabled = bulk_memory.unwrap_or(false); config.multi_value_enabled = true; config.wide_arithmetic_enabled = wide_arithmetic.unwrap_or(false); config.memory64_enabled = memory64.unwrap_or(false); @@ -283,7 +286,7 @@ impl Config { self.wasmtime.memory_guaranteed_dense_image_size, )); cfg.wasm.async_stack_zeroing = Some(self.wasmtime.async_stack_zeroing); - cfg.wasm.bulk_memory = Some(true); + cfg.wasm.bulk_memory = Some(self.module_config.config.bulk_memory_enabled); cfg.wasm.component_model_async = Some(self.module_config.component_model_async); cfg.wasm.component_model_async_builtins = Some(self.module_config.component_model_async_builtins); @@ -293,6 +296,7 @@ impl Config { cfg.wasm.component_model_error_context = Some(self.module_config.component_model_error_context); cfg.wasm.component_model_gc = Some(self.module_config.component_model_gc); + cfg.wasm.component_model_map = Some(self.module_config.component_model_map); cfg.wasm.component_model_fixed_length_lists = Some(self.module_config.component_model_fixed_length_lists); cfg.wasm.custom_page_sizes = Some(self.module_config.config.custom_page_sizes_enabled); @@ -328,13 +332,6 @@ impl Config { self.wasmtime.codegen.configure(&mut cfg); - // Determine whether we will actually enable PCC -- this is - // disabled if the module requires memory64, which is not yet - // compatible (due to the need for dynamic checks). - let pcc = cfg!(feature = "fuzz-pcc") - && self.wasmtime.pcc - && !self.module_config.config.memory64_enabled; - cfg.codegen.inlining = self.wasmtime.inlining; // Only set cranelift specific flags when the Cranelift strategy is @@ -389,8 +386,6 @@ impl Config { )); } - cfg.codegen.pcc = Some(pcc); - // Eager init is currently only supported on Cranelift, not Winch. cfg.opts.table_lazy_init = Some(self.wasmtime.table_lazy_init); } @@ -408,22 +403,7 @@ impl Config { // `CustomUnaligned` variant isn't actually safe to use with a shared // memory. if !self.module_config.config.threads_enabled { - // If PCC is enabled, force other options to be compatible: PCC is currently only - // supported when bounds checks are elided. - let memory_config = if pcc { - MemoryConfig { - memory_reservation: Some(4 << 30), // 4 GiB - memory_guard_size: Some(2 << 30), // 2 GiB - memory_reservation_for_growth: Some(0), - guard_before_linear_memory: false, - memory_init_cow: true, - // Doesn't matter, only using virtual memory. - cranelift_enable_heap_access_spectre_mitigations: None, - } - } else { - self.wasmtime.memory_config.clone() - }; - + let memory_config = self.wasmtime.memory_config.clone(); memory_config.configure(&mut cfg); }; @@ -617,9 +597,6 @@ pub struct WasmtimeConfig { collector: Collector, table_lazy_init: bool, - /// Whether or not fuzzing should enable PCC. - pcc: bool, - /// Configuration for whether wasm is invoked in an async fashion and how /// it's cooperatively time-sliced. pub async_config: AsyncConfig, diff --git a/crates/fuzzing/src/generators/exception_ops.rs b/crates/fuzzing/src/generators/exception_ops.rs new file mode 100644 index 000000000000..c3f5e788b033 --- /dev/null +++ b/crates/fuzzing/src/generators/exception_ops.rs @@ -0,0 +1,629 @@ +//! Generator for exception-handling fuzz test cases. +//! +//! Generates Wasm modules that exercise `throw`/`try_table`/`catch` in +//! various combinations: multiple tags with different signatures, nested +//! handler scopes, throws at various call depths, catch vs catch_all, and +//! multiple catch clauses per try_table. + +use mutatis::{Candidates, Context, DefaultMutate, Generate, Mutate, Result as MutResult}; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; +use wasm_encoder::{ + BlockType, CodeSection, EntityType, ExportKind, ExportSection, Function, FunctionSection, + ImportSection, Instruction, Module, TagKind, TagSection, TagType, TypeSection, ValType, +}; + +/// Max number of tags. +pub const NUM_TAGS_MAX: u32 = 8; +/// Max call-chain depth (number of functions between run and thrower). +pub const CALL_DEPTH_MAX: u32 = 6; +/// Max number of scenarios (throw+catch combos) per test case. +pub const MAX_SCENARIOS: usize = 16; +/// Maximum params per tag signature. +pub const MAX_TAG_PARAMS: usize = 4; +/// Maximum number of decoy catches. +pub const MAX_DECOY_CATCHES: usize = 4; + +/// Limits controlling the structure of a generated Wasm module. +#[derive(Debug, Default, Clone, Serialize, Deserialize, Mutate)] +pub struct ExceptionOpsLimits { + /// Number of distinct tags to define. + pub(crate) num_tags: u32, + /// Depth of the call chain (number of intermediate functions). + pub(crate) call_depth: u32, +} + +impl ExceptionOpsLimits { + pub(crate) fn fixup(&mut self) { + self.num_tags = self.num_tags.clamp(1, NUM_TAGS_MAX); + self.call_depth = self.call_depth.clamp(1, CALL_DEPTH_MAX); + } +} + +/// A tag signature. +#[derive(Debug, Clone, Serialize, Deserialize, Mutate)] +pub struct TagSig { + pub(crate) params: Vec, +} + +/// Subset of ValType that we generate for tag params. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[allow(missing_docs, reason = "self-describing")] +pub enum SimpleValType { + I32, + I64, + F32, + F64, +} + +impl SimpleValType { + fn to_val_type(self) -> ValType { + match self { + Self::I32 => ValType::I32, + Self::I64 => ValType::I64, + Self::F32 => ValType::F32, + Self::F64 => ValType::F64, + } + } + + /// A deterministic "interesting" constant for the given type and index, + /// used as the thrown payload so the oracle can verify the catch. + fn test_value(self, idx: u32) -> Instruction<'static> { + match self { + Self::I32 => Instruction::I32Const(0x1000_i32.wrapping_add(idx.cast_signed())), + Self::I64 => Instruction::I64Const(0x2000_i64.wrapping_add(i64::from(idx))), + Self::F32 => Instruction::F32Const(wasm_encoder::Ieee32::new(0x4000_0000 + idx)), + Self::F64 => Instruction::F64Const(wasm_encoder::Ieee64::new( + 0x4000_0000_0000_0000 + u64::from(idx), + )), + } + } +} + +/// Which kind of catch clause to use for the handler. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum CatchKind { + /// `(catch $tag $label)`: specific tag, payload on branch. + Catch, + /// `(catch_all $label)`: any tag, no payload on branch. + CatchAll, +} + +/// One throw-and-catch scenario. +#[derive(Debug, Clone, Serialize, Deserialize, Mutate)] +pub struct Scenario { + /// Index (into our tag list) of the tag to throw. + pub(crate) throw_tag: u32, + /// The function depth at which the throw happens (0 = run, call_depth = deepest). + pub(crate) throw_depth: u32, + /// The function depth at which the handler lives. Must be <= throw_depth. + pub(crate) catch_depth: u32, + /// What kind of catch clause to use. + pub(crate) catch_kind: CatchKind, + /// Extra catch clauses (tag indices) to place *before* the real one in the + /// try_table, exercising the "skip non-matching" path. These must be + /// indices of tags different from `throw_tag`. + pub(crate) decoy_catches: Vec, + /// Extra catch clauses (tag indices) to place *after* the real one in the + /// try_table, exercising the "clauses past the match" path. These must be + /// indices of tags different from `throw_tag`. + pub(crate) decoy_catches_after: Vec, +} + +/// A description of a Wasm module that exercises exception throw/catch. +#[derive(Debug, Default, Clone, Serialize, Deserialize, Mutate)] +pub struct ExceptionOps { + pub(crate) limits: ExceptionOpsLimits, + pub(crate) tag_sigs: Vec, + pub(crate) scenarios: Vec, +} + +impl ExceptionOps { + /// Encode as a Wasm module. + pub fn to_wasm_binary(&mut self) -> Vec { + self.fixup(); + self.encode() + } + + /// Fix up the test case to ensure all indices and structures are valid. + pub fn fixup(&mut self) { + self.limits.fixup(); + let num_tags = usize::try_from(self.limits.num_tags).unwrap(); + + // Ensure we have exactly num_tags tag signatures. + while self.tag_sigs.len() < num_tags { + // Default: single i32 param. + self.tag_sigs.push(TagSig { + params: vec![SimpleValType::I32], + }); + } + self.tag_sigs.truncate(num_tags); + + // Clamp param counts. + for sig in &mut self.tag_sigs { + sig.params.truncate(MAX_TAG_PARAMS); + if sig.params.is_empty() { + sig.params.push(SimpleValType::I32); + } + } + + // Ensure at least one scenario. + if self.scenarios.is_empty() { + self.scenarios.push(Scenario { + throw_tag: 0, + throw_depth: self.limits.call_depth, + catch_depth: 0, + catch_kind: CatchKind::Catch, + decoy_catches: vec![], + decoy_catches_after: vec![], + }); + } + self.scenarios.truncate(MAX_SCENARIOS); + + let num_tags = self.limits.num_tags; + let depth = self.limits.call_depth; + for s in &mut self.scenarios { + s.throw_tag = s.throw_tag % num_tags; + s.throw_depth = s.throw_depth.clamp(1, depth); + s.catch_depth = s.catch_depth.clamp(0, s.throw_depth - 1); + // Decoy catches: keep only tags different from the real one. + s.decoy_catches.retain(|t| *t % num_tags != s.throw_tag); + for t in &mut s.decoy_catches { + *t = *t % num_tags; + } + s.decoy_catches.truncate(MAX_DECOY_CATCHES); + s.decoy_catches_after + .retain(|t| *t % num_tags != s.throw_tag); + for t in &mut s.decoy_catches_after { + *t = *t % num_tags; + } + s.decoy_catches_after.truncate(MAX_DECOY_CATCHES); + } + } + + /// Module layout: + /// + /// Types: + /// 0..num_tags tag signatures: (params) -> () + /// num_tags..2*num_tags catch block types: () -> (params) + /// 2*num_tags () -> (i32) (run & relay functions) + /// 2*num_tags + 1 (i32, i32) -> () (check_i32) + /// 2*num_tags + 2 () -> () (thrower functions) + /// + /// Tags: 0..num_tags (using type indices 0..num_tags) + /// Imports: 0: "check_i32" + /// Functions: per-scenario call chains + "run" + /// Exports: "run" + fn encode(&self) -> Vec { + let num_tags = self.limits.num_tags; + let call_depth = self.limits.call_depth; + let num_scenarios = u32::try_from(self.scenarios.len()).unwrap(); + + let mut module = Module::new(); + + let mut types = TypeSection::new(); + + // Type indices 0..num_tags: tag signatures (params -> []) + for sig in &self.tag_sigs { + let params: Vec = sig.params.iter().map(|t| t.to_val_type()).collect(); + types.ty().function(params, vec![]); + } + + // Type indices num_tags..2*num_tags: catch block types ([] -> params) + // These are used as block types for the catch target blocks, since + // `catch $tag $label` delivers the tag's param types at the label. + let catch_block_type_base = num_tags; + for sig in &self.tag_sigs { + let results: Vec = sig.params.iter().map(|t| t.to_val_type()).collect(); + types.ty().function(vec![], results); + } + + // Utility function types + let fn_type_void_to_i32 = types.len(); + types.ty().function(vec![], vec![ValType::I32]); + + let fn_type_check = types.len(); + types + .ty() + .function(vec![ValType::I32, ValType::I32], vec![]); + + let fn_type_void_to_void = types.len(); + types.ty().function(vec![], vec![]); + + let mut tags = TagSection::new(); + for i in 0..num_tags { + tags.tag(TagType { + kind: TagKind::Exception, + func_type_idx: i, + }); + } + + let mut imports = ImportSection::new(); + let check_func_idx: u32 = 0; + imports.import("", "check_i32", EntityType::Function(fn_type_check)); + let import_count: u32 = imports.len(); + + // For each scenario: (call_depth + 1) functions at depths 0..=call_depth. + // - depth == catch_depth: handler function () -> (i32) + // - depth == throw_depth: thrower function () -> () + // - other depths < throw_depth: relay function () -> (i32) + // - other depths > throw_depth: unreachable () -> () + // + // Plus one "run" function that calls each scenario's depth-0 function. + + let funcs_per_scenario = call_depth + 1; + let run_defined_idx = num_scenarios * funcs_per_scenario; + let run_func_idx = import_count + run_defined_idx; + + let mut functions = FunctionSection::new(); + let mut code = CodeSection::new(); + + for (si, scenario) in self.scenarios.iter().enumerate() { + let scenario_base = import_count + u32::try_from(si).unwrap() * funcs_per_scenario; + let tag_sig = &self.tag_sigs[usize::try_from(scenario.throw_tag).unwrap()]; + + for d in 0..=call_depth { + if d == scenario.throw_depth { + // -- Thrower: pushes payload, throws tag -- + functions.function(fn_type_void_to_void); + let mut f = Function::new(vec![]); + for (pi, param_ty) in tag_sig.params.iter().enumerate() { + f.instruction(¶m_ty.test_value( + scenario.throw_tag * u32::try_from(MAX_TAG_PARAMS).unwrap() + + u32::try_from(pi).unwrap(), + )); + } + f.instruction(&Instruction::Throw(scenario.throw_tag)); + f.instruction(&Instruction::End); + code.function(&f); + } else if d == scenario.catch_depth { + // -- Handler: try_table with catch clauses -- + functions.function(fn_type_void_to_i32); + let mut f = Function::new(vec![]); + + // All decoys (before + after the real catch) share the same + // block structure; only the catch-clause ordering differs. + let all_decoys: Vec = scenario + .decoy_catches + .iter() + .chain(scenario.decoy_catches_after.iter()) + .copied() + .collect(); + let num_decoys = all_decoys.len(); + let num_before = scenario.decoy_catches.len(); + + // Block nesting (outermost to innermost): + // block $result (result i32) + // block $catch (result or empty) + // block $decoy_0 (result ) + // ... + // block $decoy_{n-1} (result ) + // try_table ... + // + // Catch clause labels are relative to the enclosing blocks, + // not counting the try_table itself. So from the catch: + // label 0 = $decoy_{n-1} (innermost block) + // label num_decoys-1 = $decoy_0 + // label num_decoys = $catch + // label num_decoys+1 = $result + // + // br instructions inside the try_table body do count the + // try_table, so they need +1 compared to catch labels. + + let catch_label = u32::try_from(num_decoys).unwrap(); + let result_label = u32::try_from(num_decoys + 1).unwrap(); + + // For br inside try_table body, add 1 for the try_table scope + let br_result_label = result_label + 1; + + // Build catch clauses: before-decoys, real catch, after-decoys + let mut catches: Vec = Vec::new(); + for (di, &decoy_tag) in scenario.decoy_catches.iter().enumerate() { + let decoy_label = u32::try_from(num_decoys - 1 - di).unwrap(); + catches.push(wasm_encoder::Catch::One { + tag: decoy_tag, + label: decoy_label, + }); + } + match scenario.catch_kind { + CatchKind::Catch => { + catches.push(wasm_encoder::Catch::One { + tag: scenario.throw_tag, + label: catch_label, + }); + } + CatchKind::CatchAll => { + catches.push(wasm_encoder::Catch::All { label: catch_label }); + } + } + for (i, &decoy_tag) in scenario.decoy_catches_after.iter().enumerate() { + let di = num_before + i; + let decoy_label = u32::try_from(num_decoys - 1 - di).unwrap(); + catches.push(wasm_encoder::Catch::One { + tag: decoy_tag, + label: decoy_label, + }); + } + + // Emit blocks (outermost first) + // block $result (result i32) + f.instruction(&Instruction::Block(BlockType::Result(ValType::I32))); + + // block $catch + match scenario.catch_kind { + CatchKind::Catch => { + let bt = + BlockType::FunctionType(catch_block_type_base + scenario.throw_tag); + f.instruction(&Instruction::Block(bt)); + } + CatchKind::CatchAll => { + f.instruction(&Instruction::Block(BlockType::Empty)); + } + } + + // Decoy blocks (decoy_0 outermost, decoy_{n-1} innermost) + for &decoy_tag in &all_decoys { + let bt = BlockType::FunctionType(catch_block_type_base + decoy_tag); + f.instruction(&Instruction::Block(bt)); + } + + // try_table (empty body type -- all exits via branches) + f.instruction(&Instruction::TryTable( + BlockType::Empty, + Cow::Borrowed(&catches), + )); + + // Body: call next deeper function + let next_func = scenario_base + d + 1; + f.instruction(&Instruction::Call(next_func)); + + // Normal completion (no exception): push 0, branch to $result + f.instruction(&Instruction::I32Const(0)); + f.instruction(&Instruction::Br(br_result_label)); + + f.instruction(&Instruction::End); // end try_table + // Normal flow always exits via br above; make fall-through unreachable + f.instruction(&Instruction::Unreachable); + + // End decoy blocks (innermost first) — drop all payload, push -1 + // After ending $decoy_{di}, we're inside $decoy_{di-1} + // (or $catch if di==0). Depth to $result = di + 1. + for di in (0..num_decoys).rev() { + f.instruction(&Instruction::End); // end block $decoy_{di} + // Drop the caught payload values + let decoy_tag = all_decoys[di]; + let decoy_sig = &self.tag_sigs[usize::try_from(decoy_tag).unwrap()]; + for _ in &decoy_sig.params { + f.instruction(&Instruction::Drop); + } + // Wrong tag caught -- return -1 + f.instruction(&Instruction::I32Const(-1)); + let depth_to_result = u32::try_from(di).unwrap() + 1; + f.instruction(&Instruction::Br(depth_to_result)); + } + + // End $catch block -- verify payload and return 1 + f.instruction(&Instruction::End); // end block $catch + match scenario.catch_kind { + CatchKind::Catch => { + // All tag param values are on the stack. + // Verify the first i32 value if present, drop the rest. + let nparams = tag_sig.params.len(); + // Drop all values from top (last param) down to first + for _ in 1..nparams { + f.instruction(&Instruction::Drop); + } + // First param is now on top + if tag_sig.params[0] == SimpleValType::I32 { + let idx = + scenario.throw_tag * u32::try_from(MAX_TAG_PARAMS).unwrap(); + let expected = 0x1000_i32.wrapping_add(i32::try_from(idx).unwrap()); + f.instruction(&Instruction::I32Const(expected)); + f.instruction(&Instruction::Call(check_func_idx)); + } else { + f.instruction(&Instruction::Drop); + } + f.instruction(&Instruction::I32Const(1)); + } + CatchKind::CatchAll => { + f.instruction(&Instruction::I32Const(1)); + } + } + + f.instruction(&Instruction::End); // end block $result + f.instruction(&Instruction::End); // end function + code.function(&f); + } else if d < scenario.throw_depth && d < scenario.catch_depth { + // -- Relay above handler: calls next deeper, returns i32 -- + functions.function(fn_type_void_to_i32); + let mut f = Function::new(vec![]); + let next_func = scenario_base + d + 1; + f.instruction(&Instruction::Call(next_func)); + f.instruction(&Instruction::End); + code.function(&f); + } else if d < scenario.throw_depth { + // -- Relay between handler and thrower: void -- + // These functions will never return normally (thrower always throws), + // but the validator needs the types to be correct. + functions.function(fn_type_void_to_void); + let mut f = Function::new(vec![]); + let next_func = scenario_base + d + 1; + f.instruction(&Instruction::Call(next_func)); + f.instruction(&Instruction::End); + code.function(&f); + } else { + // -- Dead code beyond throw depth -- + functions.function(fn_type_void_to_void); + let mut f = Function::new(vec![]); + f.instruction(&Instruction::Unreachable); + f.instruction(&Instruction::End); + code.function(&f); + } + } + } + + // -- "run" function: calls each scenario entry, sums results -- + functions.function(fn_type_void_to_i32); + { + let mut f = Function::new(vec![(1, ValType::I32)]); + for si in 0..num_scenarios { + let scenario_entry = import_count + si * funcs_per_scenario; + f.instruction(&Instruction::Call(scenario_entry)); + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::I32Add); + f.instruction(&Instruction::LocalSet(0)); + } + f.instruction(&Instruction::LocalGet(0)); + f.instruction(&Instruction::End); + code.function(&f); + } + + let mut exports = ExportSection::new(); + exports.export("run", ExportKind::Func, run_func_idx); + + module + .section(&types) + .section(&imports) + .section(&functions) + .section(&tags) + .section(&exports) + .section(&code); + + module.finish() + } + + /// Pop the last scenario. Returns true if one was removed. + pub fn pop(&mut self) -> bool { + self.scenarios.pop().is_some() + } + + /// Number of scenarios; expected return value from "run" when all + /// catches succeed. + pub fn expected_result(&mut self) -> i32 { + self.fixup(); + i32::try_from(self.scenarios.len()).unwrap() + } +} + +/// Mutator for unit-variant enums ([`SimpleValType`] and [`CatchKind`]), +/// which need manual impls because `#[derive(Mutate)]` doesn't switch +/// between variants. +#[derive(Debug, Default)] +pub struct EnumMutator; + +impl Mutate for EnumMutator { + fn mutate(&mut self, c: &mut Candidates<'_>, value: &mut SimpleValType) -> MutResult<()> { + c.mutation(|ctx| { + let choices = [ + SimpleValType::I32, + SimpleValType::I64, + SimpleValType::F32, + SimpleValType::F64, + ]; + *value = *ctx.rng().choose(&choices).unwrap(); + Ok(()) + })?; + Ok(()) + } +} + +impl Generate for EnumMutator { + fn generate(&mut self, ctx: &mut Context) -> MutResult { + let choices = [ + SimpleValType::I32, + SimpleValType::I64, + SimpleValType::F32, + SimpleValType::F64, + ]; + Ok(*ctx.rng().choose(&choices).unwrap()) + } +} + +impl DefaultMutate for SimpleValType { + type DefaultMutate = EnumMutator; +} + +impl Mutate for EnumMutator { + fn mutate(&mut self, c: &mut Candidates<'_>, value: &mut CatchKind) -> MutResult<()> { + c.mutation(|ctx| { + let choices = [CatchKind::Catch, CatchKind::CatchAll]; + *value = *ctx.rng().choose(&choices).unwrap(); + Ok(()) + })?; + Ok(()) + } +} + +impl Generate for EnumMutator { + fn generate(&mut self, ctx: &mut Context) -> MutResult { + let choices = [CatchKind::Catch, CatchKind::CatchAll]; + Ok(*ctx.rng().choose(&choices).unwrap()) + } +} + +impl DefaultMutate for CatchKind { + type DefaultMutate = EnumMutator; +} + +impl Generate for TagSigMutator { + fn generate(&mut self, ctx: &mut Context) -> MutResult { + let count = ctx.rng().gen_index(MAX_TAG_PARAMS).unwrap_or(0) + 1; + let params = (0..count) + .map(|_| EnumMutator.generate(ctx)) + .collect::>>()?; + Ok(TagSig { params }) + } +} + +impl Generate for ScenarioMutator { + fn generate(&mut self, ctx: &mut Context) -> MutResult { + let mut m = mutatis::mutators::u32(); + Ok(Scenario { + throw_tag: mutatis::Generate::::generate(&mut m, ctx)?, + throw_depth: mutatis::Generate::::generate(&mut m, ctx)?, + catch_depth: mutatis::Generate::::generate(&mut m, ctx)?, + catch_kind: EnumMutator.generate(ctx)?, + decoy_catches: vec![], + decoy_catches_after: vec![], + }) + } +} + +impl Generate for ExceptionOpsMutator { + fn generate(&mut self, _ctx: &mut Context) -> MutResult { + let mut ops = ExceptionOps::default(); + let mut session = mutatis::Session::new(); + for _ in 0..32 { + session.mutate(&mut ops)?; + } + Ok(ops) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use wasmparser::WasmFeatures; + + #[test] + fn always_produces_valid_wasm() { + mutatis::check::Check::new() + .iters(200) + .run(|ops: &ExceptionOps| { + let mut ops = ops.clone(); + let wasm = ops.to_wasm_binary(); + let features = WasmFeatures::EXCEPTIONS + | WasmFeatures::GC_TYPES + | WasmFeatures::REFERENCE_TYPES + | WasmFeatures::MULTI_VALUE + | WasmFeatures::FLOATS + | WasmFeatures::SIMD; + let mut validator = wasmparser::Validator::new_with_features(features); + validator + .validate_all(&wasm) + .map(|_| ()) + .map_err(|e| format!("{e}\n{ops:#?}")) + }) + .unwrap(); + } +} diff --git a/crates/fuzzing/src/generators/gc_ops/mutator.rs b/crates/fuzzing/src/generators/gc_ops/mutator.rs index 9ff1761777d5..8150440fbe6e 100644 --- a/crates/fuzzing/src/generators/gc_ops/mutator.rs +++ b/crates/fuzzing/src/generators/gc_ops/mutator.rs @@ -1,411 +1,441 @@ //! Mutators for the `gc` operations. - +use crate::generators::gc_ops::limits::GcOpsLimits; use crate::generators::gc_ops::ops::{GcOp, GcOps}; -use crate::generators::gc_ops::types::{RecGroupId, TypeId}; -use mutatis::{Candidates, Context, DefaultMutate, Generate, Mutate, Result as MutResult}; +use crate::generators::gc_ops::types::{TypeId, Types}; +use mutatis::{ + Candidates, Context, DefaultMutate, Generate, Mutate, Result as MutResult, mutators as m, +}; use smallvec::SmallVec; +use std::collections::BTreeMap; -/// A mutator for the gc ops. -#[derive(Debug)] -pub struct GcOpsMutator; - -impl GcOpsMutator { - // Define a mutation that adds an operation to the ops list. - fn add_operation(&mut self, c: &mut Candidates<'_>, ops: &mut GcOps) -> mutatis::Result<()> { - if c.shrink() { - return Ok(()); - } - c.mutation(|ctx| { - if let Some(idx) = ctx.rng().gen_index(ops.ops.len() + 1) { - let op = GcOp::generate(ctx)?; - ops.ops.insert(idx, op); - log::debug!("Added operation {op:?} to ops list"); - } - Ok(()) - })?; - Ok(()) - } - - // Define a mutation that removes an operation from the ops list. - fn remove_operation(&mut self, c: &mut Candidates<'_>, ops: &mut GcOps) -> mutatis::Result<()> { - if ops.ops.is_empty() { - return Ok(()); - } - c.mutation(|ctx| { - let idx = ctx.rng().gen_index(ops.ops.len()).expect("ops not empty"); - let removed = ops.ops.remove(idx); - log::debug!("Removed operation {removed:?} from ops list"); - Ok(()) - })?; - Ok(()) - } +/// Mutator for [`Types`]: handles adding/removing types and all rec-group +/// structural mutations (duplicate, remove, merge, split, move). +#[derive(Debug, Default)] +pub struct TypesMutator; - // Define a mutation that adds an empty struct type to an existing (rec ...) group. - fn add_new_struct_type_to_rec_group( +impl TypesMutator { + /// Add an empty struct type to a random existing rec group. + fn add_struct( &mut self, c: &mut Candidates<'_>, - ops: &mut GcOps, + types: &mut Types, + limits: &GcOpsLimits, ) -> mutatis::Result<()> { if c.shrink() - || ops.types.rec_groups.is_empty() - || ops.types.type_defs.len() >= usize::try_from(ops.limits.max_types).unwrap() + || types.rec_groups.is_empty() + || types.type_defs.len() + >= usize::try_from(limits.max_types).expect("max_types is too large") { return Ok(()); } c.mutation(|ctx| { - let group_id = ctx - .rng() - .choose(&ops.types.rec_groups) - .copied() - .expect("rec_groups not empty"); - let new_tid = ops.types.fresh_type_id(ctx.rng()); - ops.types.insert_empty_struct(new_tid, group_id); - log::debug!("Added empty struct type {new_tid:?} to rec group {group_id:?}"); + let Some(gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else { + return Ok(()); + }; + let tid = types.fresh_type_id(ctx.rng()); + let is_final = (ctx.rng().gen_u32() % 4) == 0; + let supertype = if (ctx.rng().gen_u32() % 4) == 0 { + ctx.rng().choose(types.type_defs.keys()).copied() + } else { + None + }; + types.insert_empty_struct(tid, gid, is_final, supertype); + log::debug!("Added empty struct {tid:?} to rec group {gid:?}"); Ok(()) })?; Ok(()) } - // Define a mutation that removes a struct type from an existing (rec ...). - // It may result in empty rec groups. Empty rec groups are allowed. - fn remove_struct_type_from_rec_group( - &mut self, - c: &mut Candidates<'_>, - ops: &mut GcOps, - ) -> mutatis::Result<()> { - if ops.types.type_defs.is_empty() { + /// Remove a random type from its rec group. + fn remove_struct(&mut self, c: &mut Candidates<'_>, types: &mut Types) -> mutatis::Result<()> { + if types.type_defs.is_empty() { return Ok(()); } c.mutation(|ctx| { - let tid = ctx - .rng() - .choose(ops.types.type_defs.keys()) - .copied() - .expect("type_defs not empty"); - ops.types.type_defs.remove(&tid); + let Some(tid) = ctx.rng().choose(types.type_defs.keys()).copied() else { + return Ok(()); + }; + types.remove_type(tid); log::debug!("Removed struct type {tid:?}"); Ok(()) })?; Ok(()) } - // Define a mutation that moves a struct type within an existing rec group. - fn move_struct_type_within_rec_group( + /// Swap two random types within a rec group. + fn swap_within_group( &mut self, c: &mut Candidates<'_>, - ops: &mut GcOps, + types: &mut Types, ) -> mutatis::Result<()> { - if ops.types.rec_groups.is_empty() || ops.types.type_defs.len() < 2 { + if types.rec_groups.is_empty() || types.type_defs.len() <= 2 { return Ok(()); } c.mutation(|ctx| { - let mut chosen: Option<(RecGroupId, TypeId, TypeId)> = None; - - // Randomly choose a rec group. - for _ in 0..ops.limits.max_rec_groups { - let gid = ctx - .rng() - .choose(&ops.types.rec_groups) - .copied() - .expect("rec_groups not empty"); - - // Collect member TypeIds of that rec group. - let mut members: SmallVec<[TypeId; 32]> = SmallVec::new(); - for (tid, def) in ops.types.type_defs.iter() { - if def.rec_group == gid { - members.push(*tid); - } - } + for _ in 0..16 { + let Some(gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else { + return Ok(()); + }; - // If this is a singleton/empty group, try another rec group. + let Some(member_set) = types.rec_groups.get(&gid) else { + continue; + }; + let members: SmallVec<[TypeId; 32]> = member_set.iter().copied().collect(); if members.len() < 2 { continue; } - // Pick two distinct members randomly. - let tid_a = *ctx.rng().choose(&members).expect("len >= 2"); - let mut tid_b = *ctx.rng().choose(&members).expect("len >= 2"); + let Some(tid_a) = ctx.rng().choose(&members).copied() else { + return Ok(()); + }; + let Some(mut tid_b) = ctx.rng().choose(&members).copied() else { + return Ok(()); + }; for _ in 0..members.len() { if tid_a != tid_b { break; } - tid_b = *ctx.rng().choose(&members).unwrap(); + let Some(next_tid) = ctx.rng().choose(&members).copied() else { + return Ok(()); + }; + tid_b = next_tid; } if tid_a == tid_b { continue; } - chosen = Some((gid, tid_a, tid_b)); - break; - } - // Move within group - reorder for encoding by swapping map values. - if let Some((gid, tid_a, tid_b)) = chosen { - let a_def = ops.types.type_defs.remove(&tid_a).expect("tid_a present"); - let b_def = ops.types.type_defs.remove(&tid_b).expect("tid_b present"); - debug_assert!(a_def.rec_group == gid); - debug_assert!(b_def.rec_group == gid); - ops.types.type_defs.insert(tid_a, b_def); - ops.types.type_defs.insert(tid_b, a_def); - log::debug!("Reordered types {tid_a:?} and {tid_b:?} in rec group {gid:?}"); + let Some(a_def) = types.type_defs.remove(&tid_a) else { + return Ok(()); + }; + let Some(b_def) = types.type_defs.remove(&tid_b) else { + types.type_defs.insert(tid_a, a_def); + return Ok(()); + }; + types.type_defs.insert(tid_a, b_def); + types.type_defs.insert(tid_b, a_def); + log::debug!("Swapped types {tid_a:?} and {tid_b:?} in rec group {gid:?}"); + return Ok(()); } - Ok(()) })?; Ok(()) } - // Define a mutation that moves a struct type from one (rec ...) group to another. - // It will be a different rec group with high probability but it may try - // to move it to the same rec group. - fn move_struct_type_between_rec_groups( + /// Move a random type from one rec group to another. + fn move_between_groups( &mut self, c: &mut Candidates<'_>, - ops: &mut GcOps, + types: &mut Types, ) -> mutatis::Result<()> { - if ops.types.type_defs.is_empty() || ops.types.rec_groups.len() < 2 { + if types.rec_groups.len() < 2 { return Ok(()); } c.mutation(|ctx| { - let tid = ctx - .rng() - .choose(ops.types.type_defs.keys()) - .copied() - .expect("type_defs not empty"); - let new_gid = ctx - .rng() - .choose(&ops.types.rec_groups) - .copied() - .expect("rec_groups not empty"); - let old_gid = ops.types.type_defs.get(&tid).unwrap().rec_group; - ops.types.type_defs.get_mut(&tid).unwrap().rec_group = new_gid; - log::debug!("Moved type {tid:?} from rec group {old_gid:?} to {new_gid:?}"); + let mut old_gid = None; + for _ in 0..16 { + let Some(gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else { + return Ok(()); + }; + if types + .rec_groups + .get(&gid) + .map(|s| !s.is_empty()) + .unwrap_or(false) + { + old_gid = Some(gid); + break; + } + } + let Some(old_gid) = old_gid else { + return Ok(()); + }; + + let Some(tid) = types + .rec_groups + .get(&old_gid) + .and_then(|members| ctx.rng().choose(members.iter()).copied()) + else { + return Ok(()); + }; + + let Some(new_gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else { + return Ok(()); + }; + + let Some(old_members) = types.rec_groups.get_mut(&old_gid) else { + return Ok(()); + }; + old_members.remove(&tid); + let Some(new_members) = types.rec_groups.get_mut(&new_gid) else { + return Ok(()); + }; + new_members.insert(tid); + log::debug!("Moved type {tid:?} from {old_gid:?} to {new_gid:?}"); Ok(()) })?; Ok(()) } - // Define a mutation that duplicates a (rec ...) group. - fn duplicate_rec_group( + /// Duplicate a rec group (copy its structure with fresh type ids). + fn duplicate_group( &mut self, c: &mut Candidates<'_>, - ops: &mut GcOps, + types: &mut Types, + limits: &GcOpsLimits, ) -> mutatis::Result<()> { if c.shrink() - || ops.types.rec_groups.is_empty() - || ops.types.rec_groups.len() >= usize::try_from(ops.limits.max_rec_groups).unwrap() - || ops.types.type_defs.len() >= usize::try_from(ops.limits.max_types).unwrap() + || types.rec_groups.is_empty() + || types.rec_groups.len() + >= usize::try_from(limits.max_rec_groups).expect("max_rec_groups is too large") + || types.type_defs.len() + >= usize::try_from(limits.max_types).expect("max_types is too large") { return Ok(()); } c.mutation(|ctx| { - let source_gid = ctx - .rng() - .choose(&ops.types.rec_groups) - .copied() - .expect("rec_groups not empty"); + let Some(src_gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else { + return Ok(()); + }; + let Some(src_members) = types.rec_groups.get(&src_gid) else { + return Ok(()); + }; + if src_members.is_empty() { + return Ok(()); + } - // Create a new rec group. - let new_gid = ops.types.fresh_rec_group_id(ctx.rng()); - ops.types.insert_rec_group(new_gid); - - let count = ops - .types - .type_defs - .values() - .filter(|def| def.rec_group == source_gid) - .count(); - - // Skip empty rec groups. - if count == 0 { + // Collect (TypeId, is_final, supertype) for members of the source group. + let members: SmallVec<[(TypeId, bool, Option); 32]> = src_members + .iter() + .filter_map(|tid| { + types + .type_defs + .get(tid) + .map(|def| (*tid, def.is_final, def.supertype)) + }) + .collect(); + + if members.is_empty() { return Ok(()); } - // Since our structs are empty, we can just insert them into the new rec group. - // We will update mutators while adding new features to the fuzzer. - for _ in 0..count { - ops.types - .insert_empty_struct(ops.types.fresh_type_id(ctx.rng()), new_gid); + // Create a new rec group. + let new_gid = types.fresh_rec_group_id(ctx.rng()); + types.insert_rec_group(new_gid); + + // Allocate fresh type ids for each member and build old-to-new map. + let mut old_to_new: BTreeMap = BTreeMap::new(); + for (old_tid, _, _) in &members { + old_to_new.insert(*old_tid, types.fresh_type_id(ctx.rng())); + } + + // Insert duplicated defs, rewriting intra-group supertype edges to cloned ids. + for (old_tid, is_final, supertype) in &members { + let new_tid = old_to_new[old_tid]; + let mapped_super = supertype.map(|st| *old_to_new.get(&st).unwrap_or(&st)); + types.insert_empty_struct(new_tid, new_gid, *is_final, mapped_super); } log::debug!( - "Duplicated rec group {source_gid:?} as new group {new_gid:?} ({count} types)" + "Duplicated rec group {src_gid:?} as {new_gid:?} ({count} types)", + count = members.len() ); Ok(()) })?; Ok(()) } - // Define a mutation that removes a whole (rec ...) group. - fn remove_rec_group(&mut self, c: &mut Candidates<'_>, ops: &mut GcOps) -> mutatis::Result<()> { - if ops.types.rec_groups.len() <= 2 { + /// Remove a whole rec group and all its member types. + fn remove_group(&mut self, c: &mut Candidates<'_>, types: &mut Types) -> mutatis::Result<()> { + if types.rec_groups.len() <= 2 { return Ok(()); } c.mutation(|ctx| { - let gid = ctx - .rng() - .choose(&ops.types.rec_groups) - .copied() - .expect("rec_groups not empty"); - - ops.types.type_defs.retain(|_, def| def.rec_group != gid); - ops.types.rec_groups.remove(&gid); - - log::debug!("Removed rec group {gid:?} and its member types"); + let Some(gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else { + return Ok(()); + }; + let Some(members) = types.rec_groups.remove(&gid) else { + return Ok(()); + }; + for tid in &members { + types.type_defs.remove(tid); + } + log::debug!( + "Removed rec group {gid:?} and its {len} member types", + len = members.len() + ); Ok(()) })?; Ok(()) } - // Define a mutation that merges two (rec ...) groups. - fn merge_rec_groups(&mut self, c: &mut Candidates<'_>, ops: &mut GcOps) -> mutatis::Result<()> { - if ops.types.rec_groups.is_empty() || ops.types.rec_groups.len() <= 2 { + /// Merge two rec groups into one. + fn merge_groups(&mut self, c: &mut Candidates<'_>, types: &mut Types) -> mutatis::Result<()> { + if types.rec_groups.len() <= 2 { return Ok(()); } c.mutation(|ctx| { - let dst_gid = ctx - .rng() - .choose(&ops.types.rec_groups) - .copied() - .expect("rec_groups not empty"); + let Some(dst_gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else { + return Ok(()); + }; + // Find a distinct source group. let mut src_gid = None; for _ in 0..16 { - let g = ctx - .rng() - .choose(&ops.types.rec_groups) - .copied() - .expect("rec_groups not empty"); - + let Some(g) = ctx.rng().choose(types.rec_groups.keys()).copied() else { + return Ok(()); + }; if g != dst_gid { src_gid = Some(g); break; } } - let Some(src_gid) = src_gid else { - // Could not find a distinct group (should be very unlikely with len>2). return Ok(()); }; - // Collect all members of src_gid. - let mut members: SmallVec<[TypeId; 32]> = SmallVec::new(); - for (tid, def) in ops.types.type_defs.iter() { - if def.rec_group == src_gid { - members.push(*tid); - } - } - - // Move all types from src_gid into dst_gid. - for tid in members { - if let Some(def) = ops.types.type_defs.get_mut(&tid) { - def.rec_group = dst_gid; - } - } - - // Remove the now-merged-away group id. - ops.types.rec_groups.remove(&src_gid); + // Move all members from src into dst. + let Some(src_members) = types.rec_groups.remove(&src_gid) else { + return Ok(()); + }; + let Some(dst_members) = types.rec_groups.get_mut(&dst_gid) else { + return Ok(()); + }; + dst_members.extend(src_members.iter()); log::debug!("Merged rec group {src_gid:?} into {dst_gid:?}"); - Ok(()) })?; Ok(()) } - // Define a mutation that splits a (rec ...) group in two, if possible. - fn split_rec_group(&mut self, c: &mut Candidates<'_>, ops: &mut GcOps) -> mutatis::Result<()> { + /// Split a rec group into two. + fn split_group( + &mut self, + c: &mut Candidates<'_>, + types: &mut Types, + limits: &GcOpsLimits, + ) -> mutatis::Result<()> { if c.shrink() - || ops.types.rec_groups.len() >= usize::try_from(ops.limits.max_rec_groups).unwrap() - || ops.types.type_defs.len() < 2 + || types.rec_groups.is_empty() + || types.type_defs.len() < 2 + || types.rec_groups.len() + >= usize::try_from(limits.max_rec_groups).expect("max_rec_groups is too large") { return Ok(()); } c.mutation(|ctx| { - // Pick a rec group with at least 2 members. + // Find a group with >= 2 members. let mut old_gid = None; - let mut members: SmallVec<[TypeId; 32]> = SmallVec::new(); - for _ in 0..16 { - let gid = ctx - .rng() - .choose(&ops.types.rec_groups) - .copied() - .expect("rec_groups not empty"); - - members.clear(); - for (tid, def) in ops.types.type_defs.iter() { - if def.rec_group == gid { - members.push(*tid); - } - } - - if members.len() >= 2 { + let Some(gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else { + return Ok(()); + }; + if types.rec_groups.get(&gid).map(|s| s.len()).unwrap_or(0) >= 2 { old_gid = Some(gid); break; } } - let Some(old_gid) = old_gid else { return Ok(()); }; - // Create a new rec group. - let new_gid = ops.types.fresh_rec_group_id(ctx.rng()); - ops.types.insert_rec_group(new_gid); + let new_gid = types.fresh_rec_group_id(ctx.rng()); + types.insert_rec_group(new_gid); - // Choose k in [1, len-1] (so both groups remain non-empty). + // Collect members so we can pick from them. + let Some(old_members) = types.rec_groups.get(&old_gid) else { + return Ok(()); + }; + let mut members: SmallVec<[TypeId; 32]> = old_members.iter().copied().collect(); let len = members.len(); + + // Choose k in [1, len-1] so both groups stay non-empty. let Some(k_minus_1) = ctx.rng().gen_index(len - 1) else { return Ok(()); }; let k = k_minus_1 + 1; - // Move k distinct members by removing them from `members` as we pick. for _ in 0..k { let Some(i) = ctx.rng().gen_index(members.len()) else { break; }; let tid = members.remove(i); - if let Some(def) = ops.types.type_defs.get_mut(&tid) { - def.rec_group = new_gid; - } + let Some(old_members) = types.rec_groups.get_mut(&old_gid) else { + return Ok(()); + }; + old_members.remove(&tid); + let Some(new_members) = types.rec_groups.get_mut(&new_gid) else { + return Ok(()); + }; + new_members.insert(tid); } - log::debug!( - "Split rec group {old_gid:?}: moved {k} of {len} members into new group {new_gid:?}" - ); + log::debug!("Split rec group {old_gid:?}: moved {k} of {len} members into {new_gid:?}"); Ok(()) })?; Ok(()) } + + /// Run all type / rec-group mutations. [`GcOpsLimits`] come from [`GcOps`]. + fn mutate_with_limits( + &mut self, + c: &mut Candidates<'_>, + types: &mut Types, + limits: &GcOpsLimits, + ) -> mutatis::Result<()> { + self.add_struct(c, types, limits)?; + self.remove_struct(c, types)?; + self.swap_within_group(c, types)?; + self.move_between_groups(c, types)?; + self.duplicate_group(c, types, limits)?; + self.remove_group(c, types)?; + self.merge_groups(c, types)?; + self.split_group(c, types, limits)?; + Ok(()) + } } -impl Mutate for GcOpsMutator { - fn mutate(&mut self, c: &mut Candidates<'_>, ops: &mut GcOps) -> mutatis::Result<()> { - self.add_operation(c, ops)?; - self.remove_operation(c, ops)?; - self.add_new_struct_type_to_rec_group(c, ops)?; - self.remove_struct_type_from_rec_group(c, ops)?; - self.move_struct_type_within_rec_group(c, ops)?; - self.move_struct_type_between_rec_groups(c, ops)?; - self.duplicate_rec_group(c, ops)?; - self.remove_rec_group(c, ops)?; - self.merge_rec_groups(c, ops)?; - self.split_rec_group(c, ops)?; +/// Mutator for [`GcOps`]. +/// +/// Also implements [`Mutate`] / [`Generate`] for [`GcOp`] so `m::vec` can mutate +/// `Vec` without a second struct. +#[derive(Debug, Default)] +pub struct GcOpsMutator { + types_mutator: TypesMutator, +} +impl Mutate for GcOpsMutator { + fn mutate(&mut self, c: &mut Candidates<'_>, value: &mut GcOp) -> MutResult<()> { + c.mutation(|ctx| { + *value = GcOp::generate(ctx)?; + Ok(()) + })?; Ok(()) } } -impl DefaultMutate for GcOps { +impl Generate for GcOpsMutator { + fn generate(&mut self, context: &mut Context) -> MutResult { + GcOp::generate(context) + } +} + +impl DefaultMutate for GcOp { type DefaultMutate = GcOpsMutator; } -impl Default for GcOpsMutator { - fn default() -> Self { - GcOpsMutator +impl Mutate for GcOpsMutator { + fn mutate(&mut self, c: &mut Candidates<'_>, ops: &mut GcOps) -> mutatis::Result<()> { + m::vec(GcOpsMutator::default()).mutate(c, &mut ops.ops)?; + self.types_mutator + .mutate_with_limits(c, &mut ops.types, &ops.limits)?; + Ok(()) } } +impl DefaultMutate for GcOps { + type DefaultMutate = GcOpsMutator; +} + impl<'a> arbitrary::Arbitrary<'a> for GcOps { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { let mut session = mutatis::Session::new().seed(u.arbitrary()?); diff --git a/crates/fuzzing/src/generators/gc_ops/ops.rs b/crates/fuzzing/src/generators/gc_ops/ops.rs index 205b3dd95634..0c8049e7a661 100644 --- a/crates/fuzzing/src/generators/gc_ops/ops.rs +++ b/crates/fuzzing/src/generators/gc_ops/ops.rs @@ -69,7 +69,9 @@ impl GcOps { ); // 1: "run" - let mut params: Vec = Vec::with_capacity(self.limits.num_params as usize); + let mut params: Vec = Vec::with_capacity( + usize::try_from(self.limits.num_params).expect("num_params is too large"), + ); for _i in 0..self.limits.num_params { params.push(ValType::EXTERNREF); } @@ -104,24 +106,52 @@ impl GcOps { let struct_type_base: u32 = types.len(); - let mut rec_groups: BTreeMap> = self - .types - .rec_groups + // Sort all types globally so supertypes come before subtypes. + // This is used to order types *within* each rec group. + let mut type_order = Vec::new(); + self.types.sort_types_topo(&mut type_order); + + // Build a position map so we can sort each group's members + // according to the global type order. + let type_position: BTreeMap = type_order .iter() - .copied() - .map(|id| (id, Vec::new())) + .enumerate() + .map(|(i, &id)| (id, i)) .collect(); - for (id, ty) in self.types.type_defs.iter() { - rec_groups.entry(ty.rec_group).or_default().push(*id); + // Topological sort of rec groups: if a type in group G has a + // supertype in group H, then H appears before G. + let mut group_order = Vec::new(); + self.types.sort_rec_groups_topo(&mut group_order); + + // For each group, collect its members sorted by the global type order. + let mut group_members: BTreeMap> = BTreeMap::new(); + for &gid in &group_order { + if let Some(member_set) = self.types.rec_groups.get(&gid) { + let mut members: Vec = member_set.iter().copied().collect(); + members.sort_by_key(|tid| type_position.get(tid).copied().unwrap_or(usize::MAX)); + group_members.insert(gid, members); + } + } + + // Build the type-id-to-wasm-index map directly. + let mut type_ids_to_index: BTreeMap = BTreeMap::new(); + let mut next_idx = struct_type_base; + for g in &group_order { + if let Some(members) = group_members.get(g) { + for &tid in members { + type_ids_to_index.insert(tid, next_idx); + next_idx += 1; + } + } } let encode_ty_id = |ty_id: &TypeId| -> wasm_encoder::SubType { let def = &self.types.type_defs[ty_id]; match &def.composite_type { CompositeType::Struct(StructType {}) => wasm_encoder::SubType { - is_final: true, - supertype_idx: None, + is_final: def.is_final, + supertype_idx: def.supertype.map(|st| type_ids_to_index[&st]), composite_type: wasm_encoder::CompositeType { inner: wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType { fields: Box::new([]), @@ -136,10 +166,12 @@ impl GcOps { let mut struct_count = 0; - for type_ids in rec_groups.values() { + // Emit rec groups in the derived order. + for g in &group_order { + let type_ids = group_members.get(g).map(|v| v.as_slice()).unwrap_or(&[]); let members: Vec = type_ids.iter().map(encode_ty_id).collect(); types.ty().rec(members); - struct_count += type_ids.len() as u32; + struct_count += u32::try_from(type_ids.len()).unwrap(); } let typed_fn_type_base: u32 = struct_type_base + struct_count; diff --git a/crates/fuzzing/src/generators/gc_ops/tests.rs b/crates/fuzzing/src/generators/gc_ops/tests.rs index 57f3073d8c5c..e3e856cbe25c 100644 --- a/crates/fuzzing/src/generators/gc_ops/tests.rs +++ b/crates/fuzzing/src/generators/gc_ops/tests.rs @@ -59,7 +59,10 @@ fn test_ops(num_params: u32, num_globals: u32, table_size: u32) -> GcOps { if t.limits.max_rec_groups > 0 { for i in 0..t.limits.max_types { let gid = RecGroupId(rng.gen_range(0..t.limits.max_rec_groups)); - t.types.insert_empty_struct(TypeId(i), gid); + let is_final = false; + let supertype = None; + t.types + .insert_empty_struct(TypeId(i), gid, is_final, supertype); } } @@ -83,6 +86,7 @@ fn mutate_gc_ops_with_default_mutator() -> mutatis::Result<()> { session.mutate(&mut ops)?; let wasm = ops.to_wasm_binary(); + println!("wasm: {}", wasmprinter::print_bytes(&wasm).unwrap()); crate::oracles::log_wasm(&wasm); let mut validator = wasmparser::Validator::new_with_features(features); @@ -192,7 +196,7 @@ fn every_op_generated() -> mutatis::Result<()> { } #[test] -fn emits_empty_rec_groups_and_validates() -> mutatis::Result<()> { +fn emits_rec_groups_and_validates() -> mutatis::Result<()> { let _ = env_logger::try_init(); let mut ops = test_ops(5, 5, 5); @@ -212,8 +216,16 @@ fn emits_empty_rec_groups_and_validates() -> mutatis::Result<()> { let recs = wat.matches("(rec").count(); let structs = wat.matches("(struct)").count(); - assert_eq!(recs, 7, "expected 2 (rec) blocks, got {recs}"); - assert_eq!(structs, 10, "expected no struct types, got {structs}"); + assert_eq!( + recs, + ops.types.rec_groups.len(), + "one (rec) block per rec group" + ); + assert_eq!( + structs, + ops.types.type_defs.len(), + "one (struct) per struct type in type_defs" + ); Ok(()) } @@ -287,3 +299,259 @@ fn fixup_check_types_and_indexes() -> mutatis::Result<()> { Ok(()) } + +#[test] +fn sort_types_by_supertype_orders_supertype_before_subtype_across_rec_groups() { + let mut types = Types::new(); + let ga = RecGroupId(0); + let gb = RecGroupId(1); + let gc = RecGroupId(2); + let gd = RecGroupId(3); + types.insert_rec_group(ga); + types.insert_rec_group(gb); + types.insert_rec_group(gc); + types.insert_rec_group(gd); + + let a = TypeId(0); + let b = TypeId(1); + let c = TypeId(2); + let d = TypeId(3); + + // Cross-rec-group chain: C <: A <: B <: D. + types.insert_empty_struct(a, ga, false, Some(b)); // A <: B + types.insert_empty_struct(b, gb, false, Some(d)); // B <: D + types.insert_empty_struct(c, gc, false, Some(a)); // C <: A + types.insert_empty_struct(d, gd, false, None); // D + + let mut sorted = Vec::new(); + types.sort_types_topo(&mut sorted); + + // Rec-group boundaries do not change topological ordering by supertype. + assert_eq!( + sorted, + [TypeId(3), TypeId(1), TypeId(0), TypeId(2)], + "topo order: supertype before subtype across rec groups" + ); +} + +#[test] +fn fixup_preserves_subtyping_within_same_rec_group() { + let _ = env_logger::try_init(); + + let mut types = Types::new(); + let g = RecGroupId(0); + types.insert_rec_group(g); + + let super_ty = TypeId(0); + let sub_ty = TypeId(1); + + // Both types are in the same rec group. + // The second subtypes the first. + types.insert_empty_struct(super_ty, g, false, None); + types.insert_empty_struct(sub_ty, g, false, Some(super_ty)); + + let limits = GcOpsLimits { + num_params: 0, + num_globals: 0, + table_size: 0, + max_rec_groups: 10, + max_types: 10, + }; + + types.fixup(&limits); + + assert_eq!(types.rec_group_of(super_ty), Some(g)); + assert_eq!(types.rec_group_of(sub_ty), Some(g)); + assert_eq!( + types.type_defs.get(&sub_ty).unwrap().supertype, + Some(super_ty) + ); +} + +#[test] +fn fixup_breaks_one_edge_in_multi_rec_group_type_cycle() { + let _ = env_logger::try_init(); + + let mut types = Types::new(); + + let g_a = RecGroupId(0); + let g_bc = RecGroupId(1); + let g_d = RecGroupId(2); + + types.insert_rec_group(g_a); + types.insert_rec_group(g_bc); + types.insert_rec_group(g_d); + + let a = TypeId(0); + let b = TypeId(1); + let c = TypeId(2); + let d = TypeId(3); + + // Rec(a) + types.insert_empty_struct(a, g_a, false, Some(d)); + + // Rec(b, c) + types.insert_empty_struct(b, g_bc, false, None); + types.insert_empty_struct(c, g_bc, false, Some(a)); + + // Rec(d) + types.insert_empty_struct(d, g_d, false, Some(c)); + + let limits = GcOpsLimits { + num_params: 0, + num_globals: 0, + table_size: 0, + max_rec_groups: 10, + max_types: 10, + }; + + types.fixup(&limits); + + let a_super = types.type_defs.get(&a).unwrap().supertype; + let c_super = types.type_defs.get(&c).unwrap().supertype; + let d_super = types.type_defs.get(&d).unwrap().supertype; + + let cleared = [a_super, c_super, d_super] + .into_iter() + .filter(|st| st.is_none()) + .count(); + + assert!( + cleared == 1, + "fixup should clear exactly one edge to break the cycle" + ); +} + +#[test] +fn sort_rec_groups_topo_orders_dependencies_first() { + let mut types = Types::new(); + + let g0 = RecGroupId(0); + let g1 = RecGroupId(1); + let g2 = RecGroupId(2); + let g3 = RecGroupId(3); + + types.insert_rec_group(g0); + types.insert_rec_group(g1); + types.insert_rec_group(g2); + types.insert_rec_group(g3); + + let a = TypeId(0); + let b = TypeId(1); + let c = TypeId(2); + let d = TypeId(3); + let e = TypeId(4); + let f = TypeId(5); + + types.insert_empty_struct(a, g0, false, Some(b)); // g0 -> g1 + types.insert_empty_struct(b, g1, false, Some(c)); // g1 -> g2 + types.insert_empty_struct(c, g2, false, Some(d)); // g2 ->g3 + types.insert_empty_struct(d, g3, false, None); + types.insert_empty_struct(e, g0, false, None); + types.insert_empty_struct(f, g2, false, None); + + let mut sorted = Vec::new(); + types.sort_rec_groups_topo(&mut sorted); + + // g3 has no deps, g2 depends on g3, g1 on g2, g0 on g1. + assert_eq!( + sorted, + [RecGroupId(3), RecGroupId(2), RecGroupId(1), RecGroupId(0)], + "topo order: depended-on groups before dependent groups" + ); +} + +#[test] +fn break_rec_group_cycles() { + let mut types = Types::new(); + + let g0 = RecGroupId(0); + let g1 = RecGroupId(1); + let g2 = RecGroupId(2); + let g3 = RecGroupId(3); + + types.insert_rec_group(g0); + types.insert_rec_group(g1); + types.insert_rec_group(g2); + types.insert_rec_group(g3); + + let a0 = TypeId(0); + let a1 = TypeId(1); + let b0 = TypeId(2); + let b1 = TypeId(3); + let c0 = TypeId(4); + let c1 = TypeId(5); + let c2 = TypeId(6); + let d0 = TypeId(7); + let d1 = TypeId(8); + + // Before: t + // + // ---------------------------------- + // | outer cycle │ + // v │ + // +----+ +----+ +----+ │ + // | g0 |------>| g1 |------>| g2 |--- + // +----+ +----+ +----+ + // ^ │ + // │ inner │ + // │ cycle v + // │ +----+ + // -----------| g3 | + // +----+ + // + // After: back edges dropped, clean chain + // + // +----+ +----+ +----+ +----+ + // | g0 |------>| g1 |------>| g2 |------>| g3 | + // +----+ +----+ +----+ +----+ + + types.insert_empty_struct(a0, g0, false, Some(b0)); // g0 -> g1 + types.insert_empty_struct(a1, g0, false, None); + + types.insert_empty_struct(b0, g1, false, None); + types.insert_empty_struct(b1, g1, false, Some(c0)); // g1 -> g2 + + types.insert_empty_struct(c0, g2, false, None); + types.insert_empty_struct(c1, g2, false, Some(a1)); // g2 -> g0 (outer back edge) + types.insert_empty_struct(c2, g2, false, Some(d0)); // g2 -> g3 + + types.insert_empty_struct(d0, g3, false, None); + types.insert_empty_struct(d1, g3, false, Some(b0)); // g3 -> g1 (inner back edge) + + // Type graph is acyclic — breaking supertype cycles changes nothing. + types.break_supertype_cycles(); + assert_eq!(types.type_defs.get(&a0).unwrap().supertype, Some(b0)); + assert_eq!(types.type_defs.get(&b1).unwrap().supertype, Some(c0)); + assert_eq!(types.type_defs.get(&c1).unwrap().supertype, Some(a1)); + assert_eq!(types.type_defs.get(&c2).unwrap().supertype, Some(d0)); + assert_eq!(types.type_defs.get(&d1).unwrap().supertype, Some(b0)); + + assert_eq!(types.rec_groups.len(), 4); + + types.break_rec_group_cycles(); + + // All four groups preserved. + assert_eq!(types.rec_groups.len(), 4); + assert!(types.rec_groups.contains_key(&g0)); + assert!(types.rec_groups.contains_key(&g1)); + assert!(types.rec_groups.contains_key(&g2)); + assert!(types.rec_groups.contains_key(&g3)); + + // Back edge (g2->g0): c1's supertype cleared. + assert_eq!(types.type_defs.get(&c1).unwrap().supertype, None); + + // Back edge (g3->g1): d1's supertype cleared. + assert_eq!(types.type_defs.get(&d1).unwrap().supertype, None); + + // All other cross-group supertypes preserved. + assert_eq!(types.type_defs.get(&a0).unwrap().supertype, Some(b0)); + assert_eq!(types.type_defs.get(&b1).unwrap().supertype, Some(c0)); + assert_eq!(types.type_defs.get(&c2).unwrap().supertype, Some(d0)); + + // Result is a clean chain: g0 -> g1 -> g2 -> g3 + let mut topo = Vec::new(); + types.sort_rec_groups_topo(&mut topo); + assert_eq!(topo.len(), 4); + assert_eq!(topo, vec![g3, g2, g1, g0]); +} diff --git a/crates/fuzzing/src/generators/gc_ops/types.rs b/crates/fuzzing/src/generators/gc_ops/types.rs index 34187e1e101c..d7a822fa956a 100644 --- a/crates/fuzzing/src/generators/gc_ops/types.rs +++ b/crates/fuzzing/src/generators/gc_ops/types.rs @@ -3,67 +3,141 @@ use crate::generators::gc_ops::limits::GcOpsLimits; use crate::generators::gc_ops::ops::GcOp; use serde::{Deserialize, Serialize}; +use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet}; +use wasmtime_environ::graphs::{Dfs, DfsEvent, Graph}; -/// RecGroup ID struct definition. +/// Identifies a `(rec ...)` group. #[derive( Debug, Copy, Clone, Eq, PartialOrd, PartialEq, Ord, Hash, Default, Serialize, Deserialize, )] pub struct RecGroupId(pub(crate) u32); -/// TypeID struct definition. +/// Identifies a type within a rec group. #[derive( Debug, Copy, Clone, Eq, PartialOrd, PartialEq, Ord, Hash, Default, Serialize, Deserialize, )] pub struct TypeId(pub(crate) u32); -/// StructType definition. +/// A struct type definition. #[derive(Debug, Default, Serialize, Deserialize)] -pub struct StructType { - // Empty for now; fields will come in a future PR. -} +pub struct StructType {} -/// CompsiteType definition. +/// A composite type: currently only structs. #[derive(Debug, Serialize, Deserialize)] pub enum CompositeType { - /// Struct Type definition. + /// A struct composite type. Struct(StructType), } -/// SubType definition +/// A sub-type definition (the per-type payload). #[derive(Debug, Serialize, Deserialize)] pub struct SubType { - pub(crate) rec_group: RecGroupId, + pub(crate) is_final: bool, + pub(crate) supertype: Option, pub(crate) composite_type: CompositeType, } -/// Struct types definition. + +/// Supertype graph: edges go from a type to its supertype. +struct SupertypeGraph<'a> { + type_defs: &'a BTreeMap, +} + +impl Graph for SupertypeGraph<'_> { + type NodesIter<'a> + = std::iter::Copied> + where + Self: 'a; + + fn nodes(&self) -> Self::NodesIter<'_> { + self.type_defs.keys().copied() + } + + type SuccessorsIter<'a> + = std::option::IntoIter + where + Self: 'a; + + fn successors(&self, node: TypeId) -> Self::SuccessorsIter<'_> { + self.type_defs + .get(&node) + .and_then(|def| def.supertype) + .into_iter() + } +} + +/// Rec-group dependency graph: group A depends on group B when a type +/// in A has a supertype in B. +struct RecGroupGraph<'a> { + type_defs: &'a BTreeMap, + rec_groups: &'a BTreeMap>, + type_to_group: &'a BTreeMap, +} + +impl Graph for RecGroupGraph<'_> { + type NodesIter<'a> + = std::iter::Copied>> + where + Self: 'a; + + fn nodes(&self) -> Self::NodesIter<'_> { + self.rec_groups.keys().copied() + } + + type SuccessorsIter<'a> + = std::vec::IntoIter + where + Self: 'a; + + fn successors(&self, group: RecGroupId) -> Self::SuccessorsIter<'_> { + let mut deps = BTreeSet::new(); + + if let Some(type_ids) = self.rec_groups.get(&group) { + for &ty in type_ids { + if let Some(super_ty) = self.type_defs.get(&ty).and_then(|d| d.supertype) { + if let Some(&super_group) = self.type_to_group.get(&super_ty) { + if super_group != group { + deps.insert(super_group); + } + } + } + } + } + + deps.into_iter().collect::>().into_iter() + } +} + +/// All type and rec-group state. +/// +/// Rec groups own sets of [`TypeId`]s; moving a type between groups is +/// just a set remove + set insert with no cascading index fixups. #[derive(Debug, Default, Serialize, Deserialize)] pub struct Types { - pub(crate) rec_groups: BTreeSet, + /// Map from rec-group id to the set of types it contains. + pub(crate) rec_groups: BTreeMap>, + /// Map from type id to its definition. pub(crate) type_defs: BTreeMap, } impl Types { - /// Create a fresh `Types` allocator with no recursive groups defined yet. + /// Create empty type state. pub fn new() -> Self { - Self { - rec_groups: Default::default(), - type_defs: Default::default(), - } + Self::default() } - /// Returns a fresh rec-group id that is not already in use. + /// Return a fresh [`RecGroupId`] not already in use. pub fn fresh_rec_group_id(&self, rng: &mut mutatis::Rng) -> RecGroupId { for _ in 0..1000 { let id = RecGroupId(rng.gen_u32()); - if !self.rec_groups.contains(&id) { + if !self.rec_groups.contains_key(&id) { return id; } } - panic!("failed to generate a new RecGroupId in 1000 iterations; bad RNG?"); + panic!("failed to generate a new RecGroupId in 1000 iterations"); } - /// Returns a fresh type id that is not already in use. + /// Return a fresh [`TypeId`] not already in use. pub fn fresh_type_id(&self, rng: &mut mutatis::Rng) -> TypeId { for _ in 0..1000 { let id = TypeId(rng.gen_u32()); @@ -71,60 +145,356 @@ impl Types { return id; } } - panic!("failed to generate a new TypeId in 1000 iterations; bad RNG?"); + panic!("failed to generate a new TypeId in 1000 iterations"); } - /// Insert a rec-group id. Returns true if newly inserted, false if it already existed. + /// Insert an empty rec group. Returns `true` if it was newly inserted. pub fn insert_rec_group(&mut self, id: RecGroupId) -> bool { - self.rec_groups.insert(id) + match self.rec_groups.entry(id) { + Entry::Vacant(e) => { + e.insert(BTreeSet::new()); + true + } + Entry::Occupied(_) => false, + } } - /// Insert a rec-group id. - pub fn insert_empty_struct(&mut self, id: TypeId, group: RecGroupId) { + /// Insert an empty struct type into the given rec group. + /// + /// The rec group must already exist. + pub fn insert_empty_struct( + &mut self, + id: TypeId, + group: RecGroupId, + is_final: bool, + supertype: Option, + ) { + self.rec_groups + .get_mut(&group) + .expect("rec group must exist") + .insert(id); self.type_defs.insert( id, SubType { - rec_group: group, + is_final, + supertype, composite_type: CompositeType::Struct(StructType::default()), }, ); } - /// Removes any entries beyond the given limit. + /// Remove a type from its rec group and from `type_defs`. + pub fn remove_type(&mut self, id: TypeId) { + self.type_defs.remove(&id); + for members in self.rec_groups.values_mut() { + members.remove(&id); + } + } + + /// Find which rec group a type belongs to, if any. + pub fn rec_group_of(&self, id: TypeId) -> Option { + self.rec_groups + .iter() + .find(|(_, members)| members.contains(&id)) + .map(|(gid, _)| *gid) + } + + /// Topological sort of types by their supertype (supertype before subtype). + pub fn sort_types_topo(&self, out: &mut Vec) { + let graph = SupertypeGraph { + type_defs: &self.type_defs, + }; + + let mut dfs = Dfs::new(graph.nodes()); + let mut seen = BTreeSet::new(); + + out.clear(); + out.reserve(self.type_defs.len()); + + while let Some(event) = dfs.next(&graph, |id| seen.contains(&id)) { + match event { + DfsEvent::Pre(id) => { + seen.insert(id); + } + DfsEvent::Post(id) => { + out.push(id); + } + DfsEvent::AfterEdge(_, _) => {} + } + } + } + + /// Topological sort of rec groups: if a type in group G has a + /// supertype in group H, then H appears before G in the output. + pub fn sort_rec_groups_topo(&self, out: &mut Vec) { + let type_to_group = self.type_to_group_map(); + let graph = RecGroupGraph { + type_defs: &self.type_defs, + rec_groups: &self.rec_groups, + type_to_group: &type_to_group, + }; + + let mut dfs = Dfs::new(graph.nodes()); + let mut seen = BTreeSet::new(); + + out.clear(); + out.reserve(self.rec_groups.len()); + + while let Some(event) = dfs.next(&graph, |id| seen.contains(&id)) { + match event { + DfsEvent::Pre(id) => { + seen.insert(id); + } + DfsEvent::Post(id) => { + out.push(id); + } + DfsEvent::AfterEdge(_, _) => {} + } + } + } + + /// Break cycles in the [type -> supertype] graph by dropping some supertype edges. + pub fn break_supertype_cycles(&mut self) { + let graph = SupertypeGraph { + type_defs: &self.type_defs, + }; + + let mut dfs = Dfs::new(graph.nodes()); + let mut seen = BTreeSet::new(); + let mut active = BTreeSet::new(); + let mut to_clear = BTreeSet::new(); + + while let Some(event) = dfs.next(&graph, |id| seen.contains(&id)) { + match event { + DfsEvent::Pre(id) => { + seen.insert(id); + active.insert(id); + } + DfsEvent::Post(id) => { + active.remove(&id); + } + DfsEvent::AfterEdge(from, to) => { + if active.contains(&to) { + to_clear.insert(from); + } + } + } + } + + for id in to_clear { + if let Some(def) = self.type_defs.get_mut(&id) { + def.supertype = None; + } + } + } + + /// Build a reverse map from type id to its owning rec group. + fn type_to_group_map(&self) -> BTreeMap { + self.rec_groups + .iter() + .flat_map(|(&gid, members)| members.iter().map(move |&tid| (tid, gid))) + .collect() + } + + /// Break cycles in the rec-group dependency graph by dropping cross-group + /// supertype edges that are DFS back edges. + pub fn break_rec_group_cycles(&mut self) { + let type_to_group = self.type_to_group_map(); + let graph = RecGroupGraph { + type_defs: &self.type_defs, + rec_groups: &self.rec_groups, + type_to_group: &type_to_group, + }; + + let mut seen = BTreeSet::new(); + let mut back_edges: BTreeSet<(RecGroupId, RecGroupId)> = BTreeSet::new(); + let mut dfs = Dfs::default(); + + for &root in self.rec_groups.keys() { + if seen.contains(&root) { + continue; + } + dfs.add_root(root); + let mut active = BTreeSet::new(); + + while let Some(event) = dfs.next(&graph, |id| seen.contains(&id)) { + match event { + DfsEvent::Pre(id) => { + seen.insert(id); + active.insert(id); + } + DfsEvent::Post(id) => { + active.remove(&id); + } + DfsEvent::AfterEdge(from, to) => { + if active.contains(&to) { + back_edges.insert((from, to)); + } + } + } + } + } + + // Drop supertype edges that correspond to back edges. + if !back_edges.is_empty() { + for (&tid, def) in self.type_defs.iter_mut() { + if let Some(st) = def.supertype { + if let (Some(&sg), Some(&spg)) = + (type_to_group.get(&tid), type_to_group.get(&st)) + { + if back_edges.contains(&(sg, spg)) { + def.supertype = None; + } + } + } + } + } + } + + /// Fix up the types to ensure they are within the limits. pub fn fixup(&mut self, limits: &GcOpsLimits) { - while self.rec_groups.len() > limits.max_rec_groups as usize { + let max_rec_groups = + usize::try_from(limits.max_rec_groups).expect("max_rec_groups is too large"); + let max_types = usize::try_from(limits.max_types).expect("max_types is too large"); + + // 1. Trim excess types. + while self.type_defs.len() > max_types { + if let Some((tid, _)) = self.type_defs.pop_last() { + for members in self.rec_groups.values_mut() { + members.remove(&tid); + } + } + } + + // 2. Drop dangling references and deduplicate across groups. + let mut seen = BTreeSet::new(); + for members in self.rec_groups.values_mut() { + members.retain(|tid| self.type_defs.contains_key(tid) && seen.insert(*tid)); + } + + // 3. Trim excess rec groups. + while self.rec_groups.len() > max_rec_groups { self.rec_groups.pop_last(); } - // Drop any types whose rec-group has been trimmed out. - self.type_defs - .retain(|_, ty| self.rec_groups.contains(&ty.rec_group)); + // 4. Find all orphans (from trimmed groups or never in any group). + let housed: BTreeSet = self + .rec_groups + .values() + .flat_map(|m| m.iter().copied()) + .collect(); + let orphans: Vec = self + .type_defs + .keys() + .filter(|tid| !housed.contains(tid)) + .copied() + .collect(); - // Then enforce the max types limit. - while self.type_defs.len() > limits.max_types as usize { - self.type_defs.pop_last(); + // 5. Adopt orphans or drop them. + if let Some(first_members) = self.rec_groups.values_mut().next() { + first_members.extend(orphans); + } else { + for tid in &orphans { + self.type_defs.remove(tid); + } } - debug_assert!( - self.type_defs - .values() - .all(|ty| self.rec_groups.contains(&ty.rec_group)), - "type_defs must only reference existing rec_groups" - ); + // 6. Clear supertypes that reference removed types. + let valid_type_ids: BTreeSet = self.type_defs.keys().copied().collect(); + for def in self.type_defs.values_mut() { + if let Some(st) = def.supertype { + if !valid_type_ids.contains(&st) { + def.supertype = None; + } + } + } + + // 7. A subtype cannot have a final supertype. + let final_type_ids: BTreeSet = self + .type_defs + .iter() + .filter(|(_, d)| d.is_final) + .map(|(id, _)| *id) + .collect(); + for def in self.type_defs.values_mut() { + if let Some(st) = def.supertype { + if final_type_ids.contains(&st) { + def.supertype = None; + } + } + } + + // 8. Break supertype cycles and rec-group dependency cycles. + self.break_supertype_cycles(); + self.break_rec_group_cycles(); + + debug_assert!(self.is_well_formed(limits)); + } + + /// Check if the types are well-formed and within configured limits, i.e. + /// rec/type counts are within limits, + /// every type belongs to exactly one rec group, + /// and every rec group member must exist in type_defs. + fn is_well_formed(&self, limits: &GcOpsLimits) -> bool { + if self.rec_groups.len() + > usize::try_from(limits.max_rec_groups).expect("max_rec_groups is too large") + { + log::debug!("[-] Failed: rec_groups.len() > max_rec_groups"); + return false; + } + if self.type_defs.len() > usize::try_from(limits.max_types).expect("max_types is too large") + { + log::debug!("[-] Failed: type_defs.len() > max_types"); + return false; + } + let mut all = BTreeSet::new(); + for members in self.rec_groups.values() { + for tid in members { + if !self.type_defs.contains_key(tid) { + log::debug!("[-] Failed: type_defs.contains_key(tid) is false"); + return false; + } + if !all.insert(*tid) { + log::debug!("[-] Failed: all.insert(tid) is false"); + return false; + } + } + } + if !self.type_defs.keys().all(|tid| all.contains(tid)) { + log::debug!("[-] Failed: type_defs.keys().all(|tid| all.contains(tid)) is false"); + return false; + } + // Every supertype must exist and must not be final. + for (&tid, def) in &self.type_defs { + if let Some(st) = def.supertype { + match self.type_defs.get(&st) { + None => { + log::debug!("[-] Failed: supertype {st:?} missing for subtype {tid:?}"); + return false; + } + Some(super_def) if super_def.is_final => { + log::debug!("[-] Failed: subtype {tid:?} has final supertype {st:?}"); + return false; + } + _ => {} + } + } + } + true } } -/// This is used to track the requirements for the operands of an operation. +/// Tracks the required operand type on the abstract value stack. #[derive(Copy, Clone, Debug)] pub enum StackType { /// `externref`. ExternRef, - /// `(ref $*)`. + /// `(ref $*)` — optionally with a concrete type index. Struct(Option), } impl StackType { - /// Fixes the stack type to match the given requirement. + /// Ensure the top of `stack` satisfies `req`, emitting fixup ops as needed. pub fn fixup( req: Option, stack: &mut Vec, @@ -137,7 +507,7 @@ impl StackType { if stack.is_empty() { Self::emit(GcOp::NullExtern, stack, out, num_types, &mut result_types); } - stack.pop(); // always consume exactly one value + stack.pop(); } Some(Self::ExternRef) => match stack.last() { Some(Self::ExternRef) => { @@ -145,7 +515,7 @@ impl StackType { } _ => { Self::emit(GcOp::NullExtern, stack, out, num_types, &mut result_types); - stack.pop(); // consume just-synthesized externref + stack.pop(); } }, Some(Self::Struct(wanted)) => { @@ -169,8 +539,6 @@ impl StackType { Self::emit(GcOp::NullStruct, stack, out, num_types, &mut result_types); stack.pop(); } - - // Typed struct requirement: only satisfiable if we have concrete types. Some(t) => { debug_assert_ne!( num_types, 0, @@ -192,6 +560,7 @@ impl StackType { } } + /// Emit an opcode and update the stack. pub(crate) fn emit( op: GcOp, stack: &mut Vec, @@ -211,6 +580,7 @@ impl StackType { } } + /// Clamp a type index to the number of types. fn clamp(t: u32, n: u32) -> u32 { if n == 0 { 0 } else { t % n } } diff --git a/crates/fuzzing/src/generators/module.rs b/crates/fuzzing/src/generators/module.rs index db89d56b5b82..6d8198649418 100644 --- a/crates/fuzzing/src/generators/module.rs +++ b/crates/fuzzing/src/generators/module.rs @@ -22,6 +22,7 @@ pub struct ModuleConfig { pub component_model_threading: bool, pub component_model_error_context: bool, pub component_model_gc: bool, + pub component_model_map: bool, pub component_model_fixed_length_lists: bool, pub legacy_exceptions: bool, pub shared_memory: bool, @@ -80,6 +81,7 @@ impl<'a> Arbitrary<'a> for ModuleConfig { component_model_threading: false, component_model_error_context: false, component_model_gc: false, + component_model_map: false, component_model_fixed_length_lists: false, legacy_exceptions: false, shared_memory: false, diff --git a/crates/fuzzing/src/oom.rs b/crates/fuzzing/src/oom.rs index 848a672eb8d3..e4620395a855 100644 --- a/crates/fuzzing/src/oom.rs +++ b/crates/fuzzing/src/oom.rs @@ -4,8 +4,16 @@ //! https://firefox-source-docs.mozilla.org/js/hacking_tips.html#how-to-debug-oomtest-failures use backtrace::Backtrace; -use std::{alloc::GlobalAlloc, cell::Cell, mem, ptr, time}; -use wasmtime_core::error::{Error, OutOfMemory, Result, bail}; +use std::{ + alloc::GlobalAlloc, + cell::Cell, + mem, + pin::Pin, + ptr, + task::{Context, Poll}, + time, +}; +use wasmtime_core::error::{OutOfMemory, Result, bail}; /// An allocator for use with `OomTest`. #[non_exhaustive] @@ -46,28 +54,23 @@ fn set_oom_state(state: OomState) -> OomState { /// RAII helper to set the OOM state within a block of code and reset it upon /// exiting that block (even if exiting via panic unwinding). -struct ScopedOomState { - prev_state: OomState, +struct ScopedOomState<'a> { + state: &'a mut OomState, } -impl ScopedOomState { - fn new(state: OomState) -> Self { - ScopedOomState { - prev_state: set_oom_state(state), - } - } - - /// Finish this OOM state scope early, resetting the OOM state to what it - /// was before this scope was created, and returning the previous state that - /// was just overwritten by the reset. - fn finish(&self) -> OomState { - set_oom_state(self.prev_state.clone()) +impl<'a> ScopedOomState<'a> { + /// The OOM state will be initialized to `*state` on construction, and then + /// `state` will be updated to be the old OOM state that existed when the + /// original state is replaced on this type's drop. + fn new(state: &'a mut OomState) -> Self { + *state = set_oom_state(mem::take(state)); + ScopedOomState { state } } } -impl Drop for ScopedOomState { +impl Drop for ScopedOomState<'_> { fn drop(&mut self) { - set_oom_state(mem::take(&mut self.prev_state)); + *self.state = set_oom_state(mem::take(self.state)); } } @@ -178,7 +181,7 @@ unsafe impl GlobalAlloc for OomTestAllocator { /// use wasmtime_fuzzing::oom::{OomTest, OomTestAllocator}; /// /// #[global_allocator] -/// static GLOBAL_ALOCATOR: OomTestAllocator = OomTestAllocator::new(); +/// static GLOBAL_ALLOCATOR: OomTestAllocator = OomTestAllocator::new(); /// /// #[test] /// fn my_oom_test() -> Result<()> { @@ -250,6 +253,12 @@ impl OomTest { /// Returns early once the test function returns `Ok(())` before an OOM has /// been injected. pub fn test(&self, test_func: impl Fn() -> Result<()>) -> Result<()> { + let future = self.test_async(|| async { test_func() }); + crate::block_on(future) + } + + /// The same as `test` but `async`. + pub async fn test_async(&self, test_func: impl AsyncFn() -> Result<()>) -> Result<()> { let start = time::Instant::now(); for i in 0.. { @@ -260,19 +269,18 @@ impl OomTest { } log::trace!("=== Injecting OOM after {i} allocations ==="); - let (result, old_state) = { - let guard = ScopedOomState::new(OomState::OomOnAlloc { + + let future = std::pin::pin!(test_func()); + let (result, oom_state) = OomTestFuture::new( + future, + OomState::OomOnAlloc { counter: i, allow_alloc_after: self.allow_alloc_after_oom, - }); - assert_eq!(guard.prev_state, OomState::OutsideOomTest); - - let result = test_func(); - - (result, guard.finish()) - }; + }, + ) + .await; - match (result, old_state) { + match (result, oom_state) { (_, OomState::OutsideOomTest) => unreachable!(), // The test function completed successfully before we ran out of @@ -281,7 +289,7 @@ impl OomTest { // We injected an OOM and the test function handled it // correctly; continue to the next iteration. - (Err(e), OomState::DidOom { .. }) if self.is_oom_error(&e) => {} + (Err(e), OomState::DidOom { .. }) if e.is::() => {} // Missed OOMs. (Ok(()), OomState::DidOom { .. }) => { @@ -304,8 +312,46 @@ impl OomTest { Ok(()) } +} + +struct OomTestFuture<'a, F> { + inner: Pin<&'a mut F>, + state: OomState, +} - fn is_oom_error(&self, e: &Error) -> bool { - e.is::() +impl<'a, F> OomTestFuture<'a, F> { + fn new(inner: Pin<&'a mut F>, state: OomState) -> Self { + OomTestFuture { inner, state } + } +} + +impl Future for OomTestFuture<'_, F> +where + F: Future>, +{ + type Output = (Result<()>, OomState); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + log::trace!("OomTestFuture::poll: Entered ---"); + + let result = { + let OomTestFuture { inner, state } = &mut *self; + + let guard = ScopedOomState::new(state); + assert_eq!(*guard.state, OomState::OutsideOomTest); + + inner.as_mut().poll(cx) + }; + + log::trace!("OomTestFuture::poll: inner result: {result:?}"); + log::trace!("OomTestFuture::poll: old state: {:?}", self.state); + + match result { + Poll::Pending => { + set_oom_state(OomState::OutsideOomTest); + Poll::Pending + } + Poll::Ready(result) => Poll::Ready((result, mem::take(&mut self.state))), + } } } diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index 9b89849c07b2..03d68d408c16 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -23,6 +23,7 @@ mod stacks; use self::diff_wasmtime::WasmtimeInstance; use self::engine::{DiffEngine, DiffInstance}; +use crate::generators::ExceptionOps; use crate::generators::GcOps; use crate::generators::{self, CompilerStrategy, DiffValue, DiffValueType}; use crate::single_module_fuzzer::KnownValid; @@ -324,17 +325,8 @@ fn compile_module( ) -> Option { log_wasm(bytes); - fn is_pcc_error(e: &wasmtime::Error) -> bool { - // NOTE: please keep this predicate in sync with the display format of CodegenError, - // defined in `wasmtime/cranelift/codegen/src/result.rs` - e.to_string().to_lowercase().contains("proof-carrying-code") - } - match config.compile(engine, bytes) { Ok(module) => Some(module), - Err(e) if is_pcc_error(&e) => { - panic!("pcc error in input: {e:#?}"); - } Err(_) if known_valid == KnownValid::No => None, Err(e) => { if let generators::InstanceAllocationStrategy::Pooling(c) = &config.wasmtime.strategy { @@ -780,6 +772,7 @@ pub fn wast_test(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<()> { suppress_prints: true, }) .unwrap(); + wast_context.register_wasmtime().unwrap(); wast_context .run_wast(test.path.to_str().unwrap(), test.contents.as_bytes()) .unwrap(); @@ -1030,6 +1023,74 @@ pub fn gc_ops(mut fuzz_config: generators::Config, mut ops: GcOps) -> Result Result<()> { + match fuzz_config.wasmtime.compiler_strategy { + // Winch doesn't support exceptions; force to Cranelift. + CompilerStrategy::Winch => { + fuzz_config.wasmtime.compiler_strategy = CompilerStrategy::CraneliftNative; + } + CompilerStrategy::CraneliftNative | CompilerStrategy::CraneliftPulley => {} + } + + let module_cfg = &mut fuzz_config.module_config.config; + // Force exceptions + GC on (exceptions require GC). + module_cfg.gc_enabled = true; + module_cfg.exceptions_enabled = true; + module_cfg.reference_types_enabled = true; + + let expected = ops.expected_result(); + + let wasm = ops.to_wasm_binary(); + log_wasm(&wasm); + + let mut store = fuzz_config.to_store(); + + let module = compile_module(store.engine(), &wasm, KnownValid::No, &fuzz_config) + .ok_or_else(|| wasmtime::format_err!("Compilation failed"))?; + let mut linker = Linker::new(store.engine()); + + let check_ty = FuncType::new(store.engine(), [ValType::I32, ValType::I32], []); + let check_func = Func::new(&mut store, check_ty, |_caller, params, _results| { + let actual = params[0].unwrap_i32(); + let expected = params[1].unwrap_i32(); + assert_eq!(actual, expected, "check_i32 mismatch"); + Ok(()) + }); + linker.define(&store, "", "check_i32", check_func).unwrap(); + + let instance = linker.instantiate(&mut store, &module).unwrap(); + let run = instance.get_func(&mut store, "run").unwrap(); + + let mut results = [Val::I32(0)]; + match run.call(&mut store, &[], &mut results) { + Ok(()) => { + let actual = results[0].unwrap_i32(); + assert_eq!( + actual, expected, + "exception_ops: run returned {actual}, expected {expected} \ + (one catch per scenario)" + ); + } + Err(e) => { + // AllocationTooLarge / GcHeapOutOfMemory are acceptable resource-limit traps. + if let Some(trap) = e.downcast_ref::() { + match trap { + Trap::AllocationTooLarge => return Ok(()), + _ => {} + } + } + if e.is::>() { + return Ok(()); + } + // Any other error (including ThrownException) is unexpected. + panic!("exception_ops: unexpected error during execution: {e:?}"); + } + } + + Ok(()) +} + #[derive(Default)] struct HelperThread { state: Arc, diff --git a/crates/fuzzing/src/oracles/component_api.rs b/crates/fuzzing/src/oracles/component_api.rs index 56b187251a69..cf950bf0bd3d 100644 --- a/crates/fuzzing/src/oracles/component_api.rs +++ b/crates/fuzzing/src/oracles/component_api.rs @@ -118,6 +118,17 @@ fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrary::R .collect::>()?, ), + Type::Map(map) => { + let mut pairs = Vec::new(); + input.arbitrary_loop(Some(MIN_LIST_LENGTH), Some(MAX_LIST_LENGTH), |input| { + let key = arbitrary_val(&map.key(), input)?; + let value = arbitrary_val(&map.value(), input)?; + pairs.push((key, value)); + Ok(ControlFlow::Continue(())) + })?; + Val::Map(pairs) + } + // Resources, futures, streams, and error contexts aren't fuzzed at this time. Type::Own(_) | Type::Borrow(_) | Type::Future(_) | Type::Stream(_) | Type::ErrorContext => { unreachable!() @@ -131,10 +142,12 @@ fn store(input: &mut Unstructured<'_>, val: T) -> arbitrary::Result> let mut config = input.arbitrary::()?; config.enable_async(input)?; config.module_config.config.multi_value_enabled = true; + config.module_config.config.bulk_memory_enabled = true; config.module_config.config.reference_types_enabled = true; config.module_config.config.max_memories = 2; config.module_config.component_model_async = true; config.module_config.component_model_async_stackful = true; + config.module_config.component_model_map = true; if config.wasmtime.compiler_strategy == CompilerStrategy::Winch { config.wasmtime.compiler_strategy = CompilerStrategy::CraneliftNative; } diff --git a/crates/fuzzing/src/oracles/memory.rs b/crates/fuzzing/src/oracles/memory.rs index d6ed4806d677..9f01dc2e102b 100644 --- a/crates/fuzzing/src/oracles/memory.rs +++ b/crates/fuzzing/src/oracles/memory.rs @@ -31,9 +31,6 @@ pub fn check_memory_accesses(input: MemoryAccesses) { Err(e) => { let e = format!("{e:?}"); log::info!("Failed to create `Module`: {e}"); - if cfg!(feature = "fuzz-pcc") && e.contains("Compilation error: Proof-carrying-code") { - return; - } assert!( e.contains("bytes which exceeds the configured maximum of") || e.contains("exceeds the limit of"), diff --git a/crates/fuzzing/src/oracles/stacks.rs b/crates/fuzzing/src/oracles/stacks.rs index cfa54876cd09..9bc620153b2d 100644 --- a/crates/fuzzing/src/oracles/stacks.rs +++ b/crates/fuzzing/src/oracles/stacks.rs @@ -78,7 +78,13 @@ pub fn check_stacks(stacks: Stacks) -> usize { .get_memory(&mut store, "memory") .expect("should have `memory` export"); - let host_trace = trap.downcast_ref::().unwrap().frames(); + let host_trace = match trap.downcast_ref::() { + Some(bt) => bt.frames(), + None => { + assert!(stacks.limit.is_none()); + continue; + } + }; let trap = trap.downcast_ref::().unwrap(); max_stack_depth = max_stack_depth.max(host_trace.len()); assert_stack_matches( diff --git a/crates/fuzzing/tests/oom.rs b/crates/fuzzing/tests/oom.rs deleted file mode 100644 index 8b9d95d5dd0c..000000000000 --- a/crates/fuzzing/tests/oom.rs +++ /dev/null @@ -1,1116 +0,0 @@ -use cranelift_bitset::CompoundBitSet; -use std::{ - alloc::{Layout, alloc, dealloc}, - fmt::{self, Write}, - iter, - ops::Deref, - sync::atomic::{AtomicU32, Ordering::SeqCst}, -}; -use wasmtime::{error::OutOfMemory, *}; -use wasmtime_core::alloc::TryCollect; -use wasmtime_environ::{ - PrimaryMap, SecondaryMap, - collections::{self, *}, -}; -use wasmtime_fuzzing::oom::{OomTest, OomTestAllocator}; - -#[global_allocator] -static GLOBAL_ALOCATOR: OomTestAllocator = OomTestAllocator::new(); - -/// RAII wrapper around a raw allocation to deallocate it on drop. -struct Alloc { - layout: Layout, - ptr: *mut u8, -} - -impl Drop for Alloc { - fn drop(&mut self) { - if !self.ptr.is_null() { - unsafe { - dealloc(self.ptr, self.layout); - } - } - } -} - -impl Deref for Alloc { - type Target = *mut u8; - - fn deref(&self) -> &Self::Target { - &self.ptr - } -} - -impl Alloc { - /// Safety: same as `std::alloc::alloc`. - unsafe fn new(layout: Layout) -> Self { - let ptr = unsafe { alloc(layout) }; - Alloc { layout, ptr } - } -} - -#[test] -fn smoke_test_ok() -> Result<()> { - OomTest::new().test(|| Ok(())) -} - -#[test] -fn smoke_test_missed_oom() -> Result<()> { - let err = OomTest::new() - .test(|| unsafe { - let _ = Alloc::new(Layout::new::()); - Ok(()) - }) - .unwrap_err(); - let err = format!("{err:?}"); - assert!( - err.contains("OOM test function missed an OOM"), - "should have missed an OOM, got: {err}" - ); - Ok(()) -} - -#[test] -fn smoke_test_disallow_alloc_after_oom() -> Result<()> { - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - let _ = OomTest::new().test(|| unsafe { - let layout = Layout::new::(); - let p = Alloc::new(layout); - let _q = Alloc::new(layout); - if p.is_null() { - Err(OutOfMemory::new(layout.size()).into()) - } else { - Ok(()) - } - }); - })); - assert!(result.is_err()); - Ok(()) -} - -#[test] -fn smoke_test_allow_alloc_after_oom() -> Result<()> { - OomTest::new().allow_alloc_after_oom(true).test(|| unsafe { - let layout = Layout::new::(); - let p = Alloc::new(layout); - let q = Alloc::new(layout); - if p.is_null() || q.is_null() { - Err(OutOfMemory::new(layout.size()).into()) - } else { - Ok(()) - } - }) -} - -#[test] -#[cfg(arc_try_new)] -fn try_new_arc() -> Result<()> { - use std::sync::Arc; - - OomTest::new().test(|| { - let _arc = try_new::>(42)?; - Ok(()) - }) -} - -#[test] -fn try_new_box() -> Result<()> { - OomTest::new().test(|| { - let _box = try_new::>(36)?; - Ok(()) - }) -} - -#[test] -fn compound_bit_set_try_with_capacity() -> Result<()> { - OomTest::new().test(|| { - let _bitset = CompoundBitSet::::try_with_capacity(32)?; - Ok(()) - }) -} - -#[test] -fn compound_bit_set_try_ensure_capacity() -> Result<()> { - OomTest::new().test(|| { - let mut bitset = CompoundBitSet::new(); - bitset.try_ensure_capacity(100)?; - Ok(()) - }) -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct Key(u32); -wasmtime_environ::entity_impl!(Key); - -#[test] -fn primary_map_try_with_capacity() -> Result<()> { - OomTest::new().test(|| { - let _map = PrimaryMap::::try_with_capacity(32)?; - Ok(()) - }) -} - -#[test] -fn primary_map_try_reserve() -> Result<()> { - OomTest::new().test(|| { - let mut map = PrimaryMap::::new(); - map.try_reserve(100)?; - Ok(()) - }) -} - -#[test] -fn primary_map_try_reserve_exact() -> Result<()> { - OomTest::new().test(|| { - let mut map = PrimaryMap::::new(); - map.try_reserve_exact(13)?; - Ok(()) - }) -} - -#[test] -fn secondary_map_try_with_capacity() -> Result<()> { - OomTest::new().test(|| { - let _map = SecondaryMap::::try_with_capacity(32)?; - Ok(()) - }) -} - -#[test] -fn secondary_map_try_resize() -> Result<()> { - OomTest::new().test(|| { - let mut map = SecondaryMap::::new(); - map.try_resize(100)?; - Ok(()) - }) -} - -#[test] -fn secondary_map_try_insert() -> Result<()> { - OomTest::new().test(|| { - let mut map = SecondaryMap::::new(); - map.try_insert(Key::from_u32(42), 100)?; - Ok(()) - }) -} - -#[test] -fn entity_set_ensure_capacity() -> Result<()> { - OomTest::new().test(|| { - let mut set = EntitySet::::new(); - set.ensure_capacity(100)?; - Ok(()) - }) -} - -#[test] -fn entity_set_insert() -> Result<()> { - OomTest::new().test(|| { - let mut set = EntitySet::::new(); - set.insert(Key::from_u32(256))?; - Ok(()) - }) -} - -#[test] -fn hash_set_with_capacity() -> Result<()> { - OomTest::new().test(|| { - let _s = HashSet::::with_capacity(100)?; - Ok(()) - }) -} - -#[test] -fn hash_set_reserve() -> Result<()> { - OomTest::new().test(|| { - let mut set = HashSet::::new(); - set.reserve(100)?; - Ok(()) - }) -} - -#[test] -fn hash_set_insert() -> Result<()> { - OomTest::new().test(|| { - let mut set = HashSet::::new(); - for i in 0..1024 { - set.insert(i)?; - } - for i in 0..1024 { - set.insert(i)?; - } - Ok(()) - }) -} - -#[test] -fn hash_map_with_capacity() -> Result<()> { - OomTest::new().test(|| { - let _s = HashMap::::with_capacity(100)?; - Ok(()) - }) -} - -#[test] -fn hash_map_reserve() -> Result<()> { - OomTest::new().test(|| { - let mut map = HashMap::::new(); - map.reserve(100)?; - Ok(()) - }) -} - -#[test] -fn hash_map_insert() -> Result<()> { - OomTest::new().test(|| { - let mut map = HashMap::::new(); - for i in 0..1024 { - map.insert(i, i * 2)?; - } - for i in 0..1024 { - map.insert(i, i * 2)?; - } - Ok(()) - }) -} - -#[test] -fn hash_map_try_clone() -> Result<()> { - OomTest::new().test(|| { - let mut map = HashMap::new(); - for i in 0..10 { - map.insert(i, i * 2)?; - } - let map2 = map.try_clone()?; - assert_eq!(map, map2); - Ok(()) - }) -} - -#[test] -fn vec_with_capacity() -> Result<()> { - OomTest::new().test(|| { - let _v = wasmtime_environ::collections::Vec::::with_capacity(100)?; - Ok(()) - }) -} - -#[test] -fn vec_reserve() -> Result<()> { - OomTest::new().test(|| { - let mut v = wasmtime_environ::collections::Vec::::new(); - v.reserve(10)?; - Ok(()) - }) -} - -#[test] -fn vec_reserve_exact() -> Result<()> { - OomTest::new().test(|| { - let mut v = wasmtime_environ::collections::Vec::::new(); - v.reserve_exact(3)?; - Ok(()) - }) -} - -#[test] -fn vec_push() -> Result<()> { - OomTest::new().test(|| { - let mut v = wasmtime_environ::collections::Vec::new(); - v.push(42)?; - Ok(()) - }) -} - -#[test] -fn try_string_with_capacity() -> Result<()> { - OomTest::new().test(|| { - let _s = TryString::with_capacity(100)?; - Ok(()) - }) -} - -#[test] -fn try_string_reserve() -> Result<()> { - OomTest::new().test(|| { - let mut s = TryString::new(); - s.reserve(10)?; - Ok(()) - }) -} - -#[test] -fn try_string_reserve_exact() -> Result<()> { - OomTest::new().test(|| { - let mut s = TryString::new(); - s.reserve_exact(3)?; - Ok(()) - }) -} - -#[test] -fn try_string_push() -> Result<()> { - OomTest::new().test(|| { - let mut s = TryString::new(); - s.push('c')?; - Ok(()) - }) -} - -#[test] -fn try_string_push_str() -> Result<()> { - OomTest::new().test(|| { - let mut s = TryString::new(); - s.push_str("hello")?; - Ok(()) - }) -} - -#[test] -fn try_string_shrink_to_fit() -> Result<()> { - OomTest::new().test(|| { - // len == cap == 0 - let mut s = TryString::new(); - s.shrink_to_fit()?; - - // len == 0 < cap - let mut s = TryString::with_capacity(4)?; - s.shrink_to_fit()?; - - // 0 < len < cap - let mut s = TryString::with_capacity(4)?; - s.push('a')?; - s.shrink_to_fit()?; - - // 0 < len == cap - let mut s = TryString::new(); - s.reserve_exact(2)?; - s.push('a')?; - s.push('a')?; - s.shrink_to_fit()?; - - Ok(()) - }) -} - -#[test] -fn try_string_into_boxed_str() -> Result<()> { - OomTest::new().test(|| { - // len == cap == 0 - let s = TryString::new(); - let _ = s.into_boxed_str()?; - - // len == 0 < cap - let s = TryString::with_capacity(4)?; - let _ = s.into_boxed_str()?; - - // 0 < len < cap - let mut s = TryString::with_capacity(4)?; - s.push('a')?; - let _ = s.into_boxed_str()?; - - // 0 < len == cap - let mut s = TryString::new(); - s.reserve_exact(2)?; - s.push('a')?; - s.push('a')?; - let _ = s.into_boxed_str()?; - - Ok(()) - }) -} - -#[test] -fn config_new() -> Result<()> { - OomTest::new().test(|| { - let mut config = Config::new(); - config.enable_compiler(false); - Ok(()) - }) -} - -#[test] -#[cfg(arc_try_new)] -fn engine_new() -> Result<()> { - OomTest::new().test(|| { - let mut config = Config::new(); - config.enable_compiler(false); - let _ = Engine::new(&config)?; - Ok(()) - }) -} - -#[test] -#[cfg(arc_try_new)] -fn func_type_try_new() -> Result<()> { - let mut config = Config::new(); - config.enable_compiler(false); - let engine = Engine::new(&config)?; - - // Run this OOM test a few times to make sure that we leave the engine's - // type registry in a good state when failing to register new types. - for i in 1..6 { - OomTest::new().test(|| { - let ty1 = FuncType::try_new( - &engine, - std::iter::repeat(ValType::ANYREF).take(i), - std::iter::repeat(ValType::ANYREF).take(i), - )?; - assert_eq!(ty1.params().len(), i); - assert_eq!(ty1.results().len(), i); - - let ty2 = FuncType::try_new( - &engine, - std::iter::repeat(ValType::ANYREF).take(i), - std::iter::repeat(ValType::ANYREF).take(i), - )?; - assert_eq!(ty2.params().len(), i); - assert_eq!(ty2.results().len(), i); - - let ty3 = FuncType::try_new(&engine, [], [])?; - assert_eq!(ty3.params().len(), 0); - assert_eq!(ty3.results().len(), 0); - - assert!( - !FuncType::eq(&ty2, &ty3), - "{ty2:?} should not be equal to {ty3:?}" - ); - - Ok(()) - })?; - } - - Ok(()) -} - -#[test] -#[cfg(arc_try_new)] -fn linker_new() -> Result<()> { - OomTest::new().test(|| { - let mut config = Config::new(); - config.enable_compiler(false); - let engine = Engine::new(&config)?; - let _linker = Linker::<()>::new(&engine); - Ok(()) - }) -} - -#[test] -#[cfg(arc_try_new)] -fn linker_func_wrap() -> Result<()> { - OomTest::new().test(|| { - let mut config = Config::new(); - config.enable_compiler(false); - let engine = Engine::new(&config)?; - let mut linker = Linker::<()>::new(&engine); - linker.func_wrap("module", "func", |x: i32| x * 2)?; - Ok(()) - }) -} - -#[test] -#[cfg(arc_try_new)] -fn linker_instantiate_pre() -> Result<()> { - let module_bytes = { - let mut config = Config::new(); - config.concurrency_support(false); - let engine = Engine::new(&config)?; - let module = Module::new( - &engine, - r#" - (module - (import "module" "func" (func (param i32) (result i32))) - - (memory (export "memory") 1) - (data (i32.const 0) "a") - - (table (export "table") 1 funcref) - (elem (i32.const 0) func 1) - - (func (export "func") (param i32) (result i32) - (call 0 (local.get 0)) - ) - ) - "#, - )?; - module.serialize()? - }; - - let mut config = Config::new(); - config.enable_compiler(false); - config.concurrency_support(false); - - let engine = Engine::new(&config)?; - - let mut linker = Linker::<()>::new(&engine); - linker.func_wrap("module", "func", |x: i32| x * 2)?; - - let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; - - OomTest::new().test(|| { - let _ = linker.instantiate_pre(&module)?; - Ok(()) - }) -} - -#[test] -#[cfg(arc_try_new)] -fn module_deserialize() -> Result<()> { - let module_bytes = { - let mut config = Config::new(); - config.concurrency_support(false); - let engine = Engine::new(&config)?; - let module = Module::new( - &engine, - r#" - (module - (import "module" "func" (func (param i32) (result i32))) - - (memory (export "memory") 1) - (data (i32.const 0) "a") - - (table (export "table") 1 funcref) - (elem (i32.const 0) func 1) - - (func (export "func") (param i32) (result i32) - (call 0 (local.get 0)) - ) - ) - "#, - )?; - module.serialize()? - }; - - let mut config = Config::new(); - config.enable_compiler(false); - config.concurrency_support(false); - let engine = Engine::new(&config)?; - - OomTest::new() - // NB: We use `postcard` to deserialize module metadata, and it will - // return a `postcard::Error::SerdeDeCustom` when we generate an - // `OutOfMemory` error during deserialization. That is then converted - // into a `wasmtime::Error`, and in the process we will attempt to box - // that into an `Error` trait object. There is no good way to avoid all - // this, so just allow allocation attempts after OOM here. - .allow_alloc_after_oom(true) - .test(|| unsafe { - let _ = Module::deserialize(&engine, &module_bytes)?; - Ok(()) - }) -} - -#[test] -#[cfg(arc_try_new)] -fn store_try_new() -> Result<()> { - let mut config = Config::new(); - config.enable_compiler(false); - config.concurrency_support(false); - let engine = Engine::new(&config)?; - OomTest::new().test(|| { - let _ = Store::try_new(&engine, ())?; - Ok(()) - }) -} - -fn ok_if_not_oom(error: Error) -> Result<()> { - if error.is::() { - Err(error) - } else { - Ok(()) - } -} - -#[test] -fn error_new() -> Result<()> { - OomTest::new().test(|| { - let error = Error::new(u8::try_from(u32::MAX).unwrap_err()); - ok_if_not_oom(error) - }) -} - -#[test] -fn error_msg() -> Result<()> { - OomTest::new().test(|| { - let error = Error::msg("ouch"); - ok_if_not_oom(error) - }) -} - -static X: AtomicU32 = AtomicU32::new(42); - -#[test] -fn error_fmt() -> Result<()> { - OomTest::new().test(|| { - let x = X.load(SeqCst); - let error = format_err!("ouch: {x}"); - ok_if_not_oom(error) - }) -} - -#[test] -fn error_context() -> Result<()> { - OomTest::new().test(|| { - let error = Error::msg("hello"); - let error = error.context("goodbye"); - ok_if_not_oom(error) - }) -} - -#[test] -fn error_chain() -> Result<()> { - OomTest::new().test(|| { - let error = Error::msg("hello"); - let error = error.context("goodbye"); - for _ in error.chain() { - // Nothing to do here, just exercising the iteration. - } - ok_if_not_oom(error) - }) -} - -struct Null; -impl Write for Null { - fn write_str(&mut self, _s: &str) -> fmt::Result { - Ok(()) - } -} - -#[test] -fn display_fmt_error() -> Result<()> { - OomTest::new().test(|| { - let error = Error::msg("hello"); - let error = error.context("goodbye"); - write!(&mut Null, "{error}").unwrap(); - ok_if_not_oom(error) - }) -} - -#[test] -fn alternate_display_fmt_error() -> Result<()> { - OomTest::new().test(|| { - let error = Error::msg("hello"); - let error = error.context("goodbye"); - write!(&mut Null, "{error:?}").unwrap(); - ok_if_not_oom(error) - }) -} - -#[test] -fn debug_fmt_error() -> Result<()> { - OomTest::new().test(|| { - let error = Error::msg("hello"); - let error = error.context("goodbye"); - write!(&mut Null, "{error:?}").unwrap(); - ok_if_not_oom(error) - }) -} - -#[test] -fn alternate_debug_fmt_error() -> Result<()> { - OomTest::new().test(|| { - let error = Error::msg("hello"); - let error = error.context("goodbye"); - write!(&mut Null, "{error:#?}").unwrap(); - ok_if_not_oom(error) - }) -} - -#[test] -fn vec_and_boxed_slice() -> Result<()> { - use wasmtime_core::alloc::Vec; - - OomTest::new().test(|| { - // Nonzero-sized type. - let mut vec = Vec::new(); - vec.push(1)?; - let slice = vec.into_boxed_slice()?; // len > 0, cap > 0 - - let mut vec = Vec::from(slice); - vec.pop(); - let slice = vec.into_boxed_slice()?; // len = 0, cap > 0 - - let vec = Vec::from(slice); - let _slice = vec.into_boxed_slice()?; // len = 0, cap = 0 - - let mut vec = Vec::new(); - vec.reserve_exact(3)?; - vec.push(2)?; - vec.push(2)?; - vec.push(2)?; - let _slice = vec.into_boxed_slice()?; // len = cap, len > 0 - - for i in 0..12 { - let mut vec = Vec::new(); - for j in 0..i { - vec.push(j)?; - } - let _slice = vec.into_boxed_slice()?; // len ?= cap - } - - // Zero-sized type. - let mut vec = Vec::new(); - vec.push(())?; - let slice = vec.into_boxed_slice()?; // len > 0, cap > 0 - let mut vec = Vec::from(slice); - vec.pop(); - let slice = vec.into_boxed_slice()?; // len = 0, cap > 0 - let vec = Vec::from(slice); - let _ = vec.into_boxed_slice()?; // len = 0, cap = 0 - - Ok(()) - }) -} - -#[test] -fn vec_shrink_to_fit() -> Result<()> { - use wasmtime_core::alloc::Vec; - - #[derive(Default)] - struct ZeroSized; - - #[derive(Default)] - struct NonZeroSized { - _unused: usize, - } - - fn do_test() -> Result<()> { - // len == cap == 0 - let mut v = Vec::::new(); - v.shrink_to_fit()?; - - // len == 0 < cap - let mut v = Vec::::with_capacity(4)?; - v.shrink_to_fit()?; - - // 0 < len < cap - let mut v = Vec::with_capacity(4)?; - v.push(T::default())?; - v.shrink_to_fit()?; - - // 0 < len == cap - let mut v = Vec::new(); - v.reserve_exact(2)?; - v.push(T::default())?; - v.push(T::default())?; - v.shrink_to_fit()?; - - Ok(()) - } - - OomTest::new().test(|| do_test::())?; - OomTest::new().test(|| do_test::())?; - Ok(()) -} - -#[test] -fn vec_resize() -> Result<()> { - OomTest::new().test(|| { - let mut v = Vec::new(); - v.resize(10, 'a')?; // Grow. - v.resize(1, 'b')?; // Truncate. - v.resize(1, 'c')?; // Same length. - v.resize(3, 'd')?; // Grow again. - assert_eq!(&*v, &['a', 'd', 'd']); - Ok(()) - }) -} - -#[test] -fn vec_try_collect() -> Result<()> { - OomTest::new().test(|| { - iter::repeat(1).take(0).try_collect::, _>()?; - iter::repeat(1).take(1).try_collect::, _>()?; - iter::repeat(1).take(100).try_collect::, _>()?; - iter::repeat(()).take(100).try_collect::, _>()?; - Ok(()) - }) -} - -#[test] -fn vec_extend() -> Result<()> { - use wasmtime_core::alloc::{TryExtend, Vec}; - OomTest::new().test(|| { - let mut vec = Vec::new(); - vec.try_extend([])?; - vec.try_extend([1])?; - vec.try_extend([1, 2, 3, 4])?; - - let mut vec = Vec::new(); - vec.try_extend([])?; - vec.try_extend([()])?; - vec.try_extend([(), (), ()])?; - Ok(()) - }) -} - -#[test] -fn vec_macro_elems() -> Result<()> { - OomTest::new().test(|| { - let v = collections::vec![100, 200, 300, 400]?; - assert_eq!(&*v, &[100, 200, 300, 400]); - Ok(()) - }) -} - -#[test] -fn vec_macro_elem_len() -> Result<()> { - OomTest::new().test(|| { - let v = collections::vec![100; 3]?; - assert_eq!(&*v, &[100, 100, 100]); - Ok(()) - }) -} - -#[test] -fn index_map_try_clone() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map1 = IndexMap::new(); - map1.insert("a", try_new::>(42)?)?; - map1.insert("b", try_new::>(36)?)?; - let map2 = map1.try_clone()?; - assert_eq!(map1, map2); - Ok(()) - }) -} - -#[test] -fn index_map_with_capacity() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let _map = IndexMap::<&str, usize>::with_capacity(100)?; - Ok(()) - }) -} - -#[test] -fn index_map_split_off() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map1 = IndexMap::new(); - map1.insert("a", 42)?; - map1.insert("b", 36)?; - - let map2 = map1.split_off(1)?; - - assert_eq!(map1.len(), 1); - assert_eq!(map2.len(), 1); - assert_eq!(map1[&"a"], 42); - assert_eq!(map1[0], 42); - assert_eq!(map2[&"b"], 36); - assert_eq!(map2[0], 36); - - Ok(()) - }) -} - -#[test] -fn index_map_reserve() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map = IndexMap::::new(); - map.reserve(100)?; - Ok(()) - }) -} - -#[test] -fn index_map_reserve_exact() -> Result<()> { - OomTest::new().test(|| { - let mut map = IndexMap::::new(); - map.reserve_exact(100)?; - Ok(()) - }) -} - -#[test] -fn index_map_insert() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map = IndexMap::new(); - map.insert(10, 20)?; - Ok(()) - }) -} - -#[test] -fn index_map_insert_full() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map = IndexMap::new(); - map.insert_full(10, 20)?; - Ok(()) - }) -} - -#[test] -fn index_map_insert_sorted() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map = IndexMap::new(); - map.insert_sorted(10, 20)?; - Ok(()) - }) -} - -#[test] -fn index_map_insert_sorted_by() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map = IndexMap::new(); - map.insert_sorted_by(10, 20, |_k, _v, _k2, _v2| core::cmp::Ordering::Less)?; - Ok(()) - }) -} - -#[test] -fn index_map_insert_sorted_by_key() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map = IndexMap::new(); - map.insert_sorted_by_key(10, 20, |_k, v| *v)?; - Ok(()) - }) -} - -#[test] -fn index_map_insert_before() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map = IndexMap::new(); - map.insert("a", 20)?; - map.insert("b", 30)?; - map.insert_before(1, "c", 40)?; - assert_eq!(map[0], 20); - assert_eq!(map[1], 40); - assert_eq!(map[2], 30); - Ok(()) - }) -} - -#[test] -fn index_map_shift_insert() -> Result<()> { - OomTest::new() - // `indexmap` will first try to double its capacity, and, if that fails, - // will then try to allocate only as much as it absolutely must. - .allow_alloc_after_oom(true) - .test(|| { - let mut map = IndexMap::new(); - map.insert("a", 20)?; - map.insert("b", 30)?; - map.shift_insert(1, "c", 40)?; - assert_eq!(map[0], 20); - assert_eq!(map[1], 40); - assert_eq!(map[2], 30); - Ok(()) - }) -} - -#[test] -fn bforest_map() -> Result<()> { - use cranelift_bforest::*; - OomTest::new().test(|| { - let mut forest = MapForest::new(); - let mut map = Map::new(); - for i in 0..100 { - map.try_insert(Key(i), i, &mut forest, &())?; - } - for i in 0..100 { - assert_eq!(map.get(Key(i), &forest, &()), Some(i)); - } - Ok(()) - }) -} - -#[test] -fn bforest_set() -> Result<()> { - use cranelift_bforest::*; - OomTest::new().test(|| { - let mut forest = SetForest::new(); - let mut set = Set::new(); - for i in 0..100 { - set.try_insert(Key(i), &mut forest, &())?; - } - for i in 0..100 { - assert!(set.contains(Key(i), &forest, &())); - } - Ok(()) - }) -} - -#[test] -fn btree_map() -> Result<()> { - use collections::btree_map::Entry; - type M = collections::BTreeMap; - OomTest::new().test(|| { - let mut m = M::new(); - - m.insert(100, 100.0)?; - - m.entry(0).or_insert(99.0)?; - m.entry(0).or_default()?; - - match m.entry(1) { - Entry::Occupied(_) => unreachable!(), - Entry::Vacant(e) => { - let e = e.insert_entry(42.0)?; - e.insert(43.0); - } - }; - - match m.entry(1) { - Entry::Occupied(e) => { - e.remove_entry(); - } - Entry::Vacant(_) => unreachable!(), - } - - match m.entry(2) { - Entry::Occupied(_) => unreachable!(), - Entry::Vacant(e) => { - e.insert(99.0)?; - } - }; - - let _ = m.iter().count(); - let _ = m.iter_mut().count(); - let _ = m.keys().count(); - let _ = m.values().count(); - let _ = m.values_mut().count(); - let _ = m.range(..3).count(); - let _ = m.range_mut(..3).count(); - - Ok(()) - }) -} diff --git a/crates/fuzzing/tests/oom/arc.rs b/crates/fuzzing/tests/oom/arc.rs new file mode 100644 index 000000000000..ccc351ef1364 --- /dev/null +++ b/crates/fuzzing/tests/oom/arc.rs @@ -0,0 +1,14 @@ +#![cfg(arc_try_new)] + +use std::sync::Arc; +use wasmtime::Result; +use wasmtime_environ::collections::try_new; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +pub(crate) fn try_new_arc() -> Result<()> { + OomTest::new().test(|| { + let _arc = try_new::>(42)?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/bforest_map.rs b/crates/fuzzing/tests/oom/bforest_map.rs new file mode 100644 index 000000000000..c3104822f00b --- /dev/null +++ b/crates/fuzzing/tests/oom/bforest_map.rs @@ -0,0 +1,19 @@ +use super::Key; +use cranelift_bforest::{Map, MapForest}; +use wasmtime::Result; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn bforest_map() -> Result<()> { + OomTest::new().test(|| { + let mut forest = MapForest::new(); + let mut map = Map::new(); + for i in 0..100 { + map.try_insert(Key(i), i, &mut forest, &())?; + } + for i in 0..100 { + assert_eq!(map.get(Key(i), &forest, &()), Some(i)); + } + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/bforest_set.rs b/crates/fuzzing/tests/oom/bforest_set.rs new file mode 100644 index 000000000000..452fc6d7b974 --- /dev/null +++ b/crates/fuzzing/tests/oom/bforest_set.rs @@ -0,0 +1,19 @@ +use super::Key; +use cranelift_bforest::{Set, SetForest}; +use wasmtime::Result; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn bforest_set() -> Result<()> { + OomTest::new().test(|| { + let mut forest = SetForest::new(); + let mut set = Set::new(); + for i in 0..100 { + set.try_insert(Key(i), &mut forest, &())?; + } + for i in 0..100 { + assert!(set.contains(Key(i), &forest, &())); + } + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/bit_set.rs b/crates/fuzzing/tests/oom/bit_set.rs new file mode 100644 index 000000000000..1fdc493a6454 --- /dev/null +++ b/crates/fuzzing/tests/oom/bit_set.rs @@ -0,0 +1,20 @@ +use cranelift_bitset::CompoundBitSet; +use wasmtime::Result; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn compound_bit_set_try_with_capacity() -> Result<()> { + OomTest::new().test(|| { + let _bitset = CompoundBitSet::::try_with_capacity(32)?; + Ok(()) + }) +} + +#[test] +fn compound_bit_set_try_ensure_capacity() -> Result<()> { + OomTest::new().test(|| { + let mut bitset = CompoundBitSet::new(); + bitset.try_ensure_capacity(100)?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/boxed.rs b/crates/fuzzing/tests/oom/boxed.rs new file mode 100644 index 000000000000..4b0a2fdfd19f --- /dev/null +++ b/crates/fuzzing/tests/oom/boxed.rs @@ -0,0 +1,11 @@ +use wasmtime::Result; +use wasmtime_environ::collections::try_new; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn try_new_box() -> Result<()> { + OomTest::new().test(|| { + let _box = try_new::>(36)?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/btree_map.rs b/crates/fuzzing/tests/oom/btree_map.rs new file mode 100644 index 000000000000..1d0d50e51fd3 --- /dev/null +++ b/crates/fuzzing/tests/oom/btree_map.rs @@ -0,0 +1,49 @@ +use wasmtime::Result; +use wasmtime_environ::collections::{TryBTreeMap, btree_map::Entry}; +use wasmtime_fuzzing::oom::OomTest; + +type M = TryBTreeMap; + +#[test] +fn btree_map() -> Result<()> { + OomTest::new().test(|| { + let mut m = M::new(); + + m.insert(100, 100.0)?; + + m.entry(0).or_insert(99.0)?; + m.entry(0).or_default()?; + + match m.entry(1) { + Entry::Occupied(_) => unreachable!(), + Entry::Vacant(e) => { + let e = e.insert_entry(42.0)?; + e.insert(43.0); + } + }; + + match m.entry(1) { + Entry::Occupied(e) => { + e.remove_entry(); + } + Entry::Vacant(_) => unreachable!(), + } + + match m.entry(2) { + Entry::Occupied(_) => unreachable!(), + Entry::Vacant(e) => { + e.insert(99.0)?; + } + }; + + let _ = m.iter().count(); + let _ = m.iter_mut().count(); + let _ = m.keys().count(); + let _ = m.values().count(); + let _ = m.values_mut().count(); + let _ = m.range(..3).count(); + let _ = m.range_mut(..3).count(); + + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/caller.rs b/crates/fuzzing/tests/oom/caller.rs new file mode 100644 index 000000000000..c15071fa7bfa --- /dev/null +++ b/crates/fuzzing/tests/oom/caller.rs @@ -0,0 +1,120 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Func, FuncType, Module, Result, Store}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn caller_get_export() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + r#"(module + (import "" "f" (func)) + (memory (export "m") 1) + (func (export "run") (call 0)) + )"#, + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let host_func = Func::try_new( + &mut store, + FuncType::try_new(&engine, [], [])?, + |mut caller, _params, _results| { + let mem = caller.get_export("m"); + assert!(mem.is_some()); + Ok(()) + }, + )?; + let instance = wasmtime::Instance::new(&mut store, &module, &[host_func.into()])?; + let run = instance.get_typed_func::<(), ()>(&mut store, "run")?; + run.call(&mut store, ())?; + Ok(()) + }) +} + +#[test] +fn caller_data() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + r#"(module + (import "" "f" (func)) + (func (export "run") (call 0)) + )"#, + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, 42u32)?; + let host_func = Func::try_new( + &mut store, + FuncType::try_new(&engine, [], [])?, + |caller, _params, _results| { + assert_eq!(*caller.data(), 42u32); + Ok(()) + }, + )?; + let instance = wasmtime::Instance::new(&mut store, &module, &[host_func.into()])?; + let run = instance.get_typed_func::<(), ()>(&mut store, "run")?; + run.call(&mut store, ())?; + Ok(()) + }) +} + +#[test] +fn caller_engine() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + r#"(module + (import "" "f" (func)) + (func (export "run") (call 0)) + )"#, + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let host_func = Func::try_new( + &mut store, + FuncType::try_new(&engine, [], [])?, + |caller, _params, _results| { + let _engine = caller.engine(); + Ok(()) + }, + )?; + let instance = wasmtime::Instance::new(&mut store, &module, &[host_func.into()])?; + let run = instance.get_typed_func::<(), ()>(&mut store, "run")?; + run.call(&mut store, ())?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/config.rs b/crates/fuzzing/tests/oom/config.rs new file mode 100644 index 000000000000..dd54e9e4723b --- /dev/null +++ b/crates/fuzzing/tests/oom/config.rs @@ -0,0 +1,11 @@ +use wasmtime::{Config, Result}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn config_new() -> Result<()> { + OomTest::new().test(|| { + let mut config = Config::new(); + config.enable_compiler(false); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/engine.rs b/crates/fuzzing/tests/oom/engine.rs new file mode 100644 index 000000000000..2f666edf5a30 --- /dev/null +++ b/crates/fuzzing/tests/oom/engine.rs @@ -0,0 +1,14 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Result}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn engine_new() -> Result<()> { + OomTest::new().test(|| { + let mut config = Config::new(); + config.enable_compiler(false); + let _ = Engine::new(&config)?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/entity_set.rs b/crates/fuzzing/tests/oom/entity_set.rs new file mode 100644 index 000000000000..ddb6228ab5ee --- /dev/null +++ b/crates/fuzzing/tests/oom/entity_set.rs @@ -0,0 +1,22 @@ +use super::Key; +use wasmtime::Result; +use wasmtime_environ::collections::TryEntitySet; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn try_entity_set_ensure_capacity() -> Result<()> { + OomTest::new().test(|| { + let mut set = TryEntitySet::::new(); + set.ensure_capacity(100)?; + Ok(()) + }) +} + +#[test] +fn try_entity_set_insert() -> Result<()> { + OomTest::new().test(|| { + let mut set = TryEntitySet::::new(); + set.insert(Key::from_u32(256))?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/error.rs b/crates/fuzzing/tests/oom/error.rs new file mode 100644 index 000000000000..97e4a3332627 --- /dev/null +++ b/crates/fuzzing/tests/oom/error.rs @@ -0,0 +1,109 @@ +use std::{ + fmt::{self, Write}, + sync::atomic::{AtomicU32, Ordering::SeqCst}, +}; +use wasmtime::{Error, Result, error::OutOfMemory, format_err}; +use wasmtime_fuzzing::oom::OomTest; + +fn ok_if_not_oom(error: Error) -> Result<()> { + if error.is::() { + Err(error) + } else { + Ok(()) + } +} + +#[test] +fn error_new() -> Result<()> { + OomTest::new().test(|| { + let error = Error::new(u8::try_from(u32::MAX).unwrap_err()); + ok_if_not_oom(error) + }) +} + +#[test] +fn error_msg() -> Result<()> { + OomTest::new().test(|| { + let error = Error::msg("ouch"); + ok_if_not_oom(error) + }) +} + +static X: AtomicU32 = AtomicU32::new(42); + +#[test] +fn error_fmt() -> Result<()> { + OomTest::new().test(|| { + let x = X.load(SeqCst); + let error = format_err!("ouch: {x}"); + ok_if_not_oom(error) + }) +} + +#[test] +fn error_context() -> Result<()> { + OomTest::new().test(|| { + let error = Error::msg("hello"); + let error = error.context("goodbye"); + ok_if_not_oom(error) + }) +} + +#[test] +fn error_chain() -> Result<()> { + OomTest::new().test(|| { + let error = Error::msg("hello"); + let error = error.context("goodbye"); + for _ in error.chain() { + // Nothing to do here, just exercising the iteration. + } + ok_if_not_oom(error) + }) +} + +struct Null; +impl Write for Null { + fn write_str(&mut self, _s: &str) -> fmt::Result { + Ok(()) + } +} + +#[test] +fn display_fmt_error() -> Result<()> { + OomTest::new().test(|| { + let error = Error::msg("hello"); + let error = error.context("goodbye"); + write!(&mut Null, "{error}").unwrap(); + ok_if_not_oom(error) + }) +} + +#[test] +fn alternate_display_fmt_error() -> Result<()> { + OomTest::new().test(|| { + let error = Error::msg("hello"); + let error = error.context("goodbye"); + write!(&mut Null, "{error:?}").unwrap(); + ok_if_not_oom(error) + }) +} + +#[test] +fn debug_fmt_error() -> Result<()> { + OomTest::new().test(|| { + let error = Error::msg("hello"); + let error = error.context("goodbye"); + write!(&mut Null, "{error:?}").unwrap(); + ok_if_not_oom(error) + }) +} + +#[test] +fn alternate_debug_fmt_error() -> Result<()> { + OomTest::new().test(|| { + let error = Error::msg("hello"); + let error = error.context("goodbye"); + write!(&mut Null, "{error:#?}").unwrap(); + ok_if_not_oom(error) + }) +} diff --git a/crates/fuzzing/tests/oom/func.rs b/crates/fuzzing/tests/oom/func.rs new file mode 100644 index 000000000000..5f5dcf3873fc --- /dev/null +++ b/crates/fuzzing/tests/oom/func.rs @@ -0,0 +1,148 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Func, FuncType, Linker, Module, Result, Store, Val, ValType}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn func_new() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let _func = Func::try_wrap(&mut store, |x: i32| x * 2)?; + Ok(()) + }) +} + +#[test] +fn func_new_with_type() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = FuncType::try_new(&engine, [ValType::I32], [ValType::I32])?; + let _func = Func::try_new(&mut store, ty, |_caller, params, results| { + results[0] = params[0].clone(); + Ok(()) + })?; + Ok(()) + }) +} + +#[test] +fn func_call() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + r#"(module (func (export "id") (param i32) (result i32) (local.get 0)))"#, + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let id = instance.get_func(&mut store, "id").unwrap(); + let mut results = [Val::I32(0)]; + id.call(&mut store, &[Val::I32(42)], &mut results)?; + assert_eq!(results[0].unwrap_i32(), 42); + Ok(()) + }) +} + +#[tokio::test] +async fn func_call_async() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + r#"(module (func (export "id") (param i32) (result i32) (local.get 0)))"#, + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new() + .test_async(|| async { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate_async(&mut store).await?; + let id = instance.get_func(&mut store, "id").unwrap(); + let mut results = [Val::I32(0)]; + id.call_async(&mut store, &[Val::I32(42)], &mut results) + .await?; + assert_eq!(results[0].unwrap_i32(), 42); + Ok(()) + }) + .await +} + +#[test] +fn func_typed() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + r#"(module (func (export "id") (param i32) (result i32) (local.get 0)))"#, + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let id = instance.get_typed_func::(&mut store, "id")?; + let result = id.call(&mut store, 42)?; + assert_eq!(result, 42); + Ok(()) + }) +} + +#[test] +fn func_ty() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let func = Func::try_wrap(&mut store, |x: i32| x * 2)?; + let ty = func.ty(&store); + assert_eq!(ty.params().len(), 1); + assert_eq!(ty.results().len(), 1); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/func_type.rs b/crates/fuzzing/tests/oom/func_type.rs new file mode 100644 index 000000000000..960f3047a577 --- /dev/null +++ b/crates/fuzzing/tests/oom/func_type.rs @@ -0,0 +1,47 @@ +#![cfg(arc_try_new)] + +use std::iter; +use wasmtime::{Config, Engine, FuncType, Result, ValType}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn func_type_try_new() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + let engine = Engine::new(&config)?; + + // Run this OOM test a few times to make sure that we leave the engine's + // type registry in a good state when failing to register new types. + for i in 1..6 { + OomTest::new().test(|| { + let ty1 = FuncType::try_new( + &engine, + iter::repeat(ValType::ANYREF).take(i), + iter::repeat(ValType::ANYREF).take(i), + )?; + assert_eq!(ty1.params().len(), i); + assert_eq!(ty1.results().len(), i); + + let ty2 = FuncType::try_new( + &engine, + iter::repeat(ValType::ANYREF).take(i), + iter::repeat(ValType::ANYREF).take(i), + )?; + assert_eq!(ty2.params().len(), i); + assert_eq!(ty2.results().len(), i); + + let ty3 = FuncType::try_new(&engine, [], [])?; + assert_eq!(ty3.params().len(), 0); + assert_eq!(ty3.results().len(), 0); + + assert!( + !FuncType::eq(&ty2, &ty3), + "{ty2:?} should not be equal to {ty3:?}" + ); + + Ok(()) + })?; + } + + Ok(()) +} diff --git a/crates/fuzzing/tests/oom/global.rs b/crates/fuzzing/tests/oom/global.rs new file mode 100644 index 000000000000..08f9031a639d --- /dev/null +++ b/crates/fuzzing/tests/oom/global.rs @@ -0,0 +1,71 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Global, GlobalType, Mutability, Result, Store, Val, ValType}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn global_new() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = GlobalType::new(ValType::I32, Mutability::Var); + let _global = wasmtime::Global::new(&mut store, ty, Val::I32(42))?; + Ok(()) + }) +} + +#[test] +fn global_get() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = GlobalType::new(ValType::I32, Mutability::Var); + let global = Global::new(&mut store, ty, Val::I32(42))?; + let val = global.get(&mut store); + assert_eq!(val.unwrap_i32(), 42); + Ok(()) + }) +} + +#[test] +fn global_set() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = GlobalType::new(ValType::I32, Mutability::Var); + let global = Global::new(&mut store, ty, Val::I32(42))?; + global.set(&mut store, Val::I32(99))?; + let val = global.get(&mut store); + assert_eq!(val.unwrap_i32(), 99); + Ok(()) + }) +} + +#[test] +fn global_ty() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = GlobalType::new(ValType::I32, Mutability::Var); + let global = Global::new(&mut store, ty, Val::I32(42))?; + let ty = global.ty(&store); + assert!(ty.content().is_i32()); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/hash_map.rs b/crates/fuzzing/tests/oom/hash_map.rs new file mode 100644 index 000000000000..0e15e856c292 --- /dev/null +++ b/crates/fuzzing/tests/oom/hash_map.rs @@ -0,0 +1,47 @@ +use wasmtime::Result; +use wasmtime_environ::collections::{TryClone, TryHashMap}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn try_hash_map_with_capacity() -> Result<()> { + OomTest::new().test(|| { + let _s = TryHashMap::::with_capacity(100)?; + Ok(()) + }) +} + +#[test] +fn try_hash_map_reserve() -> Result<()> { + OomTest::new().test(|| { + let mut map = TryHashMap::::new(); + map.reserve(100)?; + Ok(()) + }) +} + +#[test] +fn try_hash_map_insert() -> Result<()> { + OomTest::new().test(|| { + let mut map = TryHashMap::::new(); + for i in 0..1024 { + map.insert(i, i * 2)?; + } + for i in 0..1024 { + map.insert(i, i * 2)?; + } + Ok(()) + }) +} + +#[test] +fn try_hash_map_try_clone() -> Result<()> { + OomTest::new().test(|| { + let mut map = TryHashMap::new(); + for i in 0..10 { + map.insert(i, i * 2)?; + } + let map2 = map.try_clone()?; + assert_eq!(map, map2); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/hash_set.rs b/crates/fuzzing/tests/oom/hash_set.rs new file mode 100644 index 000000000000..18d25084f877 --- /dev/null +++ b/crates/fuzzing/tests/oom/hash_set.rs @@ -0,0 +1,34 @@ +use wasmtime::Result; +use wasmtime_environ::collections::TryHashSet; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn try_hash_set_with_capacity() -> Result<()> { + OomTest::new().test(|| { + let _s = TryHashSet::::with_capacity(100)?; + Ok(()) + }) +} + +#[test] +fn try_hash_set_reserve() -> Result<()> { + OomTest::new().test(|| { + let mut set = TryHashSet::::new(); + set.reserve(100)?; + Ok(()) + }) +} + +#[test] +fn try_hash_set_insert() -> Result<()> { + OomTest::new().test(|| { + let mut set = TryHashSet::::new(); + for i in 0..1024 { + set.insert(i)?; + } + for i in 0..1024 { + set.insert(i)?; + } + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/index_map.rs b/crates/fuzzing/tests/oom/index_map.rs new file mode 100644 index 000000000000..00e30177eeb7 --- /dev/null +++ b/crates/fuzzing/tests/oom/index_map.rs @@ -0,0 +1,178 @@ +use wasmtime::Result; +use wasmtime_environ::collections::{TryClone, TryIndexMap, try_new}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn try_index_map_try_clone() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map1 = TryIndexMap::new(); + map1.insert("a", try_new::>(42)?)?; + map1.insert("b", try_new::>(36)?)?; + let map2 = map1.try_clone()?; + assert_eq!(map1, map2); + Ok(()) + }) +} + +#[test] +fn try_index_map_with_capacity() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let _map = TryIndexMap::<&str, usize>::with_capacity(100)?; + Ok(()) + }) +} + +#[test] +fn try_index_map_split_off() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map1 = TryIndexMap::new(); + map1.insert("a", 42)?; + map1.insert("b", 36)?; + + let map2 = map1.split_off(1)?; + + assert_eq!(map1.len(), 1); + assert_eq!(map2.len(), 1); + assert_eq!(map1[&"a"], 42); + assert_eq!(map1[0], 42); + assert_eq!(map2[&"b"], 36); + assert_eq!(map2[0], 36); + + Ok(()) + }) +} + +#[test] +fn try_index_map_reserve() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map = TryIndexMap::::new(); + map.reserve(100)?; + Ok(()) + }) +} + +#[test] +fn try_index_map_reserve_exact() -> Result<()> { + OomTest::new().test(|| { + let mut map = TryIndexMap::::new(); + map.reserve_exact(100)?; + Ok(()) + }) +} + +#[test] +fn try_index_map_insert() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map = TryIndexMap::new(); + map.insert(10, 20)?; + Ok(()) + }) +} + +#[test] +fn try_index_map_insert_full() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map = TryIndexMap::new(); + map.insert_full(10, 20)?; + Ok(()) + }) +} + +#[test] +fn try_index_map_insert_sorted() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map = TryIndexMap::new(); + map.insert_sorted(10, 20)?; + Ok(()) + }) +} + +#[test] +fn try_index_map_insert_sorted_by() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map = TryIndexMap::new(); + map.insert_sorted_by(10, 20, |_k, _v, _k2, _v2| core::cmp::Ordering::Less)?; + Ok(()) + }) +} + +#[test] +fn try_index_map_insert_sorted_by_key() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map = TryIndexMap::new(); + map.insert_sorted_by_key(10, 20, |_k, v| *v)?; + Ok(()) + }) +} + +#[test] +fn try_index_map_insert_before() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map = TryIndexMap::new(); + map.insert("a", 20)?; + map.insert("b", 30)?; + map.insert_before(1, "c", 40)?; + assert_eq!(map[0], 20); + assert_eq!(map[1], 40); + assert_eq!(map[2], 30); + Ok(()) + }) +} + +#[test] +fn try_index_map_shift_insert() -> Result<()> { + OomTest::new() + // `indexmap` will first try to double its capacity, and, if that fails, + // will then try to allocate only as much as it absolutely must. + .allow_alloc_after_oom(true) + .test(|| { + let mut map = TryIndexMap::new(); + map.insert("a", 20)?; + map.insert("b", 30)?; + map.shift_insert(1, "c", 40)?; + assert_eq!(map[0], 20); + assert_eq!(map[1], 40); + assert_eq!(map[2], 30); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/instance.rs b/crates/fuzzing/tests/oom/instance.rs new file mode 100644 index 000000000000..698612b55b05 --- /dev/null +++ b/crates/fuzzing/tests/oom/instance.rs @@ -0,0 +1,257 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Func, Instance, Linker, Module, Result, Store}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn call_exported_func() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = Module::new( + &engine, + r#" + (module + (func (export "add") (param i32 i32) (result i32) + (i32.add (local.get 0) (local.get 1)) + ) + ) + "#, + )?; + module.serialize()? + }; + + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let add = instance.get_typed_func::<(i32, i32), i32>(&mut store, "add")?; + let result = add.call(&mut store, (1, 2))?; + assert_eq!(result, 3); + Ok(()) + }) +} + +#[test] +fn instance_new() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + "(module (import \"\" \"f\" (func)) (func (export \"g\") (call 0)))", + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let func = Func::try_wrap(&mut store, || {})?; + let _instance = Instance::new(&mut store, &module, &[func.into()])?; + Ok(()) + }) +} + +#[test] +fn instance_get_export() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new(&engine, "(module (func (export \"f\")))")?.serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let _export = instance.get_export(&mut store, "f"); + Ok(()) + }) +} + +#[test] +fn instance_exports() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + "(module (func (export \"f\")) (memory (export \"m\") 1) (global (export \"g\") i32 (i32.const 0)))", + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let count = instance.exports(&mut store).count(); + assert_eq!(count, 3); + Ok(()) + }) +} + +#[test] +fn instance_get_func() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + "(module (func (export \"f\") (param i32) (result i32) (local.get 0)))", + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let f = instance.get_func(&mut store, "f"); + assert!(f.is_some()); + Ok(()) + }) +} + +#[test] +fn instance_get_typed_func() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + "(module (func (export \"f\") (param i32) (result i32) (local.get 0)))", + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let _f = instance.get_typed_func::(&mut store, "f")?; + Ok(()) + }) +} + +#[test] +fn instance_get_table() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new(&engine, "(module (table (export \"t\") 1 funcref))")?.serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let t = instance.get_table(&mut store, "t"); + assert!(t.is_some()); + Ok(()) + }) +} + +#[test] +fn instance_get_memory() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new(&engine, "(module (memory (export \"m\") 1))")?.serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let m = instance.get_memory(&mut store, "m"); + assert!(m.is_some()); + Ok(()) + }) +} + +#[test] +fn instance_get_global() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + "(module (global (export \"g\") i32 (i32.const 42)))", + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let g = instance.get_global(&mut store, "g"); + assert!(g.is_some()); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/instance_pre.rs b/crates/fuzzing/tests/oom/instance_pre.rs new file mode 100644 index 000000000000..572c61dddbe9 --- /dev/null +++ b/crates/fuzzing/tests/oom/instance_pre.rs @@ -0,0 +1,50 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Linker, Module, Result, Store}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn instance_pre_instantiate() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = Module::new( + &engine, + r#" + (module + (import "module" "func" (func (param i32) (result i32))) + + (memory (export "memory") 1) + (data (i32.const 0) "a") + + (table (export "table") 1 funcref) + (elem (i32.const 0) func 1) + + (func (export "func") (param i32) (result i32) + (call 0 (local.get 0)) + ) + ) + "#, + )?; + module.serialize()? + }; + + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + + let engine = Engine::new(&config)?; + + let mut linker = Linker::<()>::new(&engine); + linker.func_wrap("module", "func", |x: i32| x * 2)?; + + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let _ = instance_pre.instantiate(&mut store)?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/linker.rs b/crates/fuzzing/tests/oom/linker.rs new file mode 100644 index 000000000000..84c718298239 --- /dev/null +++ b/crates/fuzzing/tests/oom/linker.rs @@ -0,0 +1,257 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Func, FuncType, Linker, Module, Result, Store}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn linker_new() -> Result<()> { + OomTest::new().test(|| { + let mut config = Config::new(); + config.enable_compiler(false); + let engine = Engine::new(&config)?; + let _linker = Linker::<()>::new(&engine); + Ok(()) + }) +} + +#[test] +fn linker_func_wrap() -> Result<()> { + OomTest::new().test(|| { + let mut config = Config::new(); + config.enable_compiler(false); + let engine = Engine::new(&config)?; + let mut linker = Linker::<()>::new(&engine); + linker.func_wrap("module", "func", |x: i32| x * 2)?; + Ok(()) + }) +} + +#[test] +fn linker_instantiate_pre() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = Module::new( + &engine, + r#" + (module + (import "module" "func" (func (param i32) (result i32))) + + (memory (export "memory") 1) + (data (i32.const 0) "a") + + (table (export "table") 1 funcref) + (elem (i32.const 0) func 1) + + (func (export "func") (param i32) (result i32) + (call 0 (local.get 0)) + ) + ) + "#, + )?; + module.serialize()? + }; + + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + + let engine = Engine::new(&config)?; + + let mut linker = Linker::<()>::new(&engine); + linker.func_wrap("module", "func", |x: i32| x * 2)?; + + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let _ = linker.instantiate_pre(&module)?; + Ok(()) + }) +} + +#[test] +fn linker_define() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let mut linker = Linker::<()>::new(&engine); + let func = Func::try_wrap(&mut store, || {})?; + linker.define(&store, "mod", "func", func)?; + Ok(()) + }) +} + +#[test] +fn linker_func_new() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut linker = Linker::<()>::new(&engine); + linker.func_new( + "mod", + "func", + FuncType::try_new(&engine, [], [])?, + |_caller, _params, _results| Ok(()), + )?; + Ok(()) + }) +} + +#[test] +fn linker_instance() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + r#"(module (func (export "f")) (memory (export "m") 1))"#, + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let pre_linker = Linker::<()>::new(&engine); + let instance_pre = pre_linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let mut linker = Linker::<()>::new(&engine); + linker.instance(&mut store, "inst", instance)?; + Ok(()) + }) +} + +#[test] +fn linker_get() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + let mut linker = Linker::<()>::new(&engine); + linker.func_wrap("mod", "func", || {})?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let _item = linker.get(&mut store, "mod", "func")?; + Ok(()) + }) +} + +#[test] +fn linker_get_by_import() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new(&engine, r#"(module (import "mod" "func" (func)))"#)?.serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + let mut linker = Linker::<()>::new(&engine); + linker.func_wrap("mod", "func", || {})?; + + let import = module.imports().next().unwrap(); + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let ext = linker.try_get_by_import(&mut store, &import)?; + assert!(ext.is_some()); + Ok(()) + }) +} + +#[test] +fn linker_get_default() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new(&engine, r#"(module (func (export "_start")))"#)?.serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + let linker = Linker::<()>::new(&engine); + let instance_pre = linker.instantiate_pre(&module)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let instance = instance_pre.instantiate(&mut store)?; + let mut linker = Linker::<()>::new(&engine); + linker.instance(&mut store, "inst", instance)?; + let _default = linker.get_default(&mut store, "inst")?; + Ok(()) + }) +} + +#[test] +fn linker_define_name() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + let mut linker = Linker::<()>::new(&engine); + let func = Func::try_wrap(&mut store, || {})?; + linker.define_name(&store, "func", func)?; + Ok(()) + }) +} + +// Note: linker_define_unknown_imports_as_traps and +// linker_define_unknown_imports_as_default_values are not tested under OOM +// because UnknownImportError::new uses infallible String allocations +// (to_string) that cannot be made fallible without changing the public API. + +#[test] +fn linker_alias() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut linker = Linker::<()>::new(&engine); + linker.func_wrap("mod", "func", || {})?; + linker.alias("mod", "func", "mod2", "func2")?; + Ok(()) + }) +} + +#[test] +fn linker_alias_module() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let mut linker = Linker::<()>::new(&engine); + linker.func_wrap("mod", "func1", || {})?; + linker.func_wrap("mod", "func2", || {})?; + linker.alias_module("mod", "mod2")?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/main.rs b/crates/fuzzing/tests/oom/main.rs new file mode 100644 index 000000000000..df0a13425595 --- /dev/null +++ b/crates/fuzzing/tests/oom/main.rs @@ -0,0 +1,44 @@ +mod arc; +mod bforest_map; +mod bforest_set; +mod bit_set; +mod boxed; +mod btree_map; +mod caller; +mod config; +mod engine; +mod entity_set; +mod error; +mod func; +mod func_type; +mod global; +mod hash_map; +mod hash_set; +mod index_map; +mod instance; +mod instance_pre; +mod linker; +mod memory; +mod module; +mod module_read; +mod primary_map; +mod secondary_map; +mod shared_memory; +mod smoke; +mod store; +mod string; +mod table; +mod tag; +mod types; +mod val; +mod vec; + +use wasmtime_fuzzing::oom::OomTestAllocator; + +#[global_allocator] +static GLOBAL_ALLOCATOR: OomTestAllocator = OomTestAllocator::new(); + +/// Entity key for testing fallible `PrimaryMap`s and such. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Key(u32); +wasmtime_environ::entity_impl!(Key); diff --git a/crates/fuzzing/tests/oom/memory.rs b/crates/fuzzing/tests/oom/memory.rs new file mode 100644 index 000000000000..5f5f884133c6 --- /dev/null +++ b/crates/fuzzing/tests/oom/memory.rs @@ -0,0 +1,133 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Memory, MemoryType, Result, Store}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn memory_new() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new() + // `IndexMap::reserve` will try to allocate double space, but if that + // fails, will attempt to allocate the minimal space necessary. + .allow_alloc_after_oom(true) + .test(|| { + let mut store = Store::try_new(&engine, ())?; + let _memory = Memory::new(&mut store, MemoryType::new(1, None))?; + Ok(()) + }) +} + +#[test] +fn memory_grow() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let memory = Memory::new(&mut store, MemoryType::new(1, None))?; + let _old_size = memory.grow(&mut store, 1)?; + Ok(()) + }) +} + +#[test] +fn memory_ty() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let memory = Memory::new(&mut store, MemoryType::new(1, None))?; + let _ty = memory.ty(&store); + Ok(()) + }) +} + +#[test] +fn memory_size() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let memory = Memory::new(&mut store, MemoryType::new(1, None))?; + assert_eq!(memory.size(&store), 1); + Ok(()) + }) +} + +#[test] +fn memory_data_size() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let memory = Memory::new(&mut store, MemoryType::new(1, None))?; + assert_eq!(memory.data_size(&store), 65536); + Ok(()) + }) +} + +#[test] +fn memory_read_write() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let memory = Memory::new(&mut store, MemoryType::new(1, None))?; + memory.write(&mut store, 0, &[1, 2, 3, 4])?; + let mut buf = [0u8; 4]; + memory.read(&store, 0, &mut buf)?; + assert_eq!(buf, [1, 2, 3, 4]); + Ok(()) + }) +} + +#[test] +fn memory_data() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let memory = Memory::new(&mut store, MemoryType::new(1, None))?; + let data = memory.data(&store); + assert_eq!(data.len(), 65536); + Ok(()) + }) +} + +#[test] +fn memory_data_mut() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let memory = Memory::new(&mut store, MemoryType::new(1, None))?; + let data = memory.data_mut(&mut store); + data[0] = 42; + assert_eq!(data[0], 42); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/module.rs b/crates/fuzzing/tests/oom/module.rs new file mode 100644 index 000000000000..e63186e30b53 --- /dev/null +++ b/crates/fuzzing/tests/oom/module.rs @@ -0,0 +1,50 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Module, Result}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn module_deserialize() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = Module::new( + &engine, + r#" + (module + (import "module" "func" (func (param i32) (result i32))) + + (memory (export "memory") 1) + (data (i32.const 0) "a") + + (table (export "table") 1 funcref) + (elem (i32.const 0) func 1) + + (func (export "func") (param i32) (result i32) + (call 0 (local.get 0)) + ) + ) + "#, + )?; + module.serialize()? + }; + + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new() + // NB: We use `postcard` to deserialize module metadata, and it will + // return a `postcard::Error::SerdeDeCustom` when we generate an + // `OutOfMemory` error during deserialization. That is then converted + // into a `wasmtime::Error`, and in the process we will attempt to box + // that into an `Error` trait object. There is no good way to avoid all + // this, so just allow allocation attempts after OOM here. + .allow_alloc_after_oom(true) + .test(|| unsafe { + let _ = Module::deserialize(&engine, &module_bytes)?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/module_read.rs b/crates/fuzzing/tests/oom/module_read.rs new file mode 100644 index 000000000000..60cd3f96aec8 --- /dev/null +++ b/crates/fuzzing/tests/oom/module_read.rs @@ -0,0 +1,117 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Module, Result}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn module_name() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new(&engine, r#"(module $my_module (func (export "f")))"#)?.serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let _name = module.name(); + Ok(()) + }) +} + +#[test] +fn module_imports() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + r#"(module (import "mod" "func" (func)) (func (export "f")))"#, + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let count = module.imports().count(); + assert_eq!(count, 1); + Ok(()) + }) +} + +#[test] +fn module_exports() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new( + &engine, + r#"(module (func (export "f")) (memory (export "m") 1))"#, + )? + .serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let count = module.exports().count(); + assert_eq!(count, 2); + Ok(()) + }) +} + +#[test] +fn module_get_export() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new(&engine, r#"(module (func (export "f")))"#)?.serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let export = module.get_export("f"); + assert!(export.is_some()); + let missing = module.get_export("nonexistent"); + assert!(missing.is_none()); + Ok(()) + }) +} + +#[test] +fn module_engine() -> Result<()> { + let module_bytes = { + let mut config = Config::new(); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + Module::new(&engine, "(module)")?.serialize()? + }; + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + let module = unsafe { Module::deserialize(&engine, &module_bytes)? }; + + OomTest::new().test(|| { + let _engine = module.engine(); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/primary_map.rs b/crates/fuzzing/tests/oom/primary_map.rs new file mode 100644 index 000000000000..3d9cfc1f79c6 --- /dev/null +++ b/crates/fuzzing/tests/oom/primary_map.rs @@ -0,0 +1,30 @@ +use super::Key; +use wasmtime::Result; +use wasmtime_environ::PrimaryMap; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn primary_map_try_with_capacity() -> Result<()> { + OomTest::new().test(|| { + let _map = PrimaryMap::::try_with_capacity(32)?; + Ok(()) + }) +} + +#[test] +fn primary_map_try_reserve() -> Result<()> { + OomTest::new().test(|| { + let mut map = PrimaryMap::::new(); + map.try_reserve(100)?; + Ok(()) + }) +} + +#[test] +fn primary_map_try_reserve_exact() -> Result<()> { + OomTest::new().test(|| { + let mut map = PrimaryMap::::new(); + map.try_reserve_exact(13)?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/secondary_map.rs b/crates/fuzzing/tests/oom/secondary_map.rs new file mode 100644 index 000000000000..4413ec0bd29c --- /dev/null +++ b/crates/fuzzing/tests/oom/secondary_map.rs @@ -0,0 +1,30 @@ +use super::Key; +use wasmtime::Result; +use wasmtime_environ::SecondaryMap; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn secondary_map_try_with_capacity() -> Result<()> { + OomTest::new().test(|| { + let _map = SecondaryMap::::try_with_capacity(32)?; + Ok(()) + }) +} + +#[test] +fn secondary_map_try_resize() -> Result<()> { + OomTest::new().test(|| { + let mut map = SecondaryMap::::new(); + map.try_resize(100)?; + Ok(()) + }) +} + +#[test] +fn secondary_map_try_insert() -> Result<()> { + OomTest::new().test(|| { + let mut map = SecondaryMap::::new(); + map.try_insert(Key::from_u32(42), 100)?; + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/shared_memory.rs b/crates/fuzzing/tests/oom/shared_memory.rs new file mode 100644 index 000000000000..c8dcf84d4108 --- /dev/null +++ b/crates/fuzzing/tests/oom/shared_memory.rs @@ -0,0 +1,90 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, MemoryTypeBuilder, Result, SharedMemory}; +use wasmtime_fuzzing::oom::OomTest; + +fn shared_memory_engine() -> Result { + let mut config = Config::new(); + config.enable_compiler(false); + config.shared_memory(true); + Engine::new(&config) +} + +#[test] +fn shared_memory_new() -> Result<()> { + let engine = shared_memory_engine()?; + + OomTest::new().test(|| { + let ty = MemoryTypeBuilder::new() + .min(1) + .max(Some(2)) + .shared(true) + .build()?; + let _mem = SharedMemory::new(&engine, ty)?; + Ok(()) + }) +} + +#[test] +fn shared_memory_ty() -> Result<()> { + let engine = shared_memory_engine()?; + + OomTest::new().test(|| { + let ty = MemoryTypeBuilder::new() + .min(1) + .max(Some(2)) + .shared(true) + .build()?; + let mem = SharedMemory::new(&engine, ty)?; + let _ty = mem.ty(); + Ok(()) + }) +} + +#[test] +fn shared_memory_size() -> Result<()> { + let engine = shared_memory_engine()?; + + OomTest::new().test(|| { + let ty = MemoryTypeBuilder::new() + .min(1) + .max(Some(2)) + .shared(true) + .build()?; + let mem = SharedMemory::new(&engine, ty)?; + assert_eq!(mem.size(), 1); + Ok(()) + }) +} + +#[test] +fn shared_memory_grow() -> Result<()> { + let engine = shared_memory_engine()?; + + OomTest::new().test(|| { + let ty = MemoryTypeBuilder::new() + .min(1) + .max(Some(4)) + .shared(true) + .build()?; + let mem = SharedMemory::new(&engine, ty)?; + let _old = mem.grow(1)?; + Ok(()) + }) +} + +#[test] +fn shared_memory_data_size() -> Result<()> { + let engine = shared_memory_engine()?; + + OomTest::new().test(|| { + let ty = MemoryTypeBuilder::new() + .min(1) + .max(Some(2)) + .shared(true) + .build()?; + let mem = SharedMemory::new(&engine, ty)?; + assert_eq!(mem.data_size(), 65536); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/smoke.rs b/crates/fuzzing/tests/oom/smoke.rs new file mode 100644 index 000000000000..aef769908b99 --- /dev/null +++ b/crates/fuzzing/tests/oom/smoke.rs @@ -0,0 +1,91 @@ +use std::{ + alloc::{Layout, alloc, dealloc}, + ops::Deref, +}; +use wasmtime::{Result, error::OutOfMemory}; +use wasmtime_fuzzing::oom::OomTest; + +/// RAII wrapper around a raw allocation to deallocate it on drop. +struct Alloc { + layout: Layout, + ptr: *mut u8, +} + +impl Drop for Alloc { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { + dealloc(self.ptr, self.layout); + } + } + } +} + +impl Deref for Alloc { + type Target = *mut u8; + + fn deref(&self) -> &Self::Target { + &self.ptr + } +} + +impl Alloc { + /// Safety: same as `std::alloc::alloc`. + unsafe fn new(layout: Layout) -> Self { + let ptr = unsafe { alloc(layout) }; + Alloc { layout, ptr } + } +} + +#[test] +pub(crate) fn smoke_test_ok() -> Result<()> { + OomTest::new().test(|| Ok(())) +} + +#[test] +pub(crate) fn smoke_test_missed_oom() -> Result<()> { + let err = OomTest::new() + .test(|| unsafe { + let _ = Alloc::new(Layout::new::()); + Ok(()) + }) + .unwrap_err(); + let err = format!("{err:?}"); + assert!( + err.contains("OOM test function missed an OOM"), + "should have missed an OOM, got: {err}" + ); + Ok(()) +} + +#[test] +pub(crate) fn smoke_test_disallow_alloc_after_oom() -> Result<()> { + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = OomTest::new().test(|| unsafe { + let layout = Layout::new::(); + let p = Alloc::new(layout); + let _q = Alloc::new(layout); + if p.is_null() { + Err(OutOfMemory::new(layout.size()).into()) + } else { + Ok(()) + } + }); + })); + assert!(result.is_err()); + Ok(()) +} + +#[test] +pub(crate) fn smoke_test_allow_alloc_after_oom() -> Result<()> { + OomTest::new().allow_alloc_after_oom(true).test(|| unsafe { + let layout = Layout::new::(); + let p = Alloc::new(layout); + let q = Alloc::new(layout); + if p.is_null() || q.is_null() { + Err(OutOfMemory::new(layout.size()).into()) + } else { + Ok(()) + } + }) +} diff --git a/crates/fuzzing/tests/oom/store.rs b/crates/fuzzing/tests/oom/store.rs new file mode 100644 index 000000000000..f4805a06d7f9 --- /dev/null +++ b/crates/fuzzing/tests/oom/store.rs @@ -0,0 +1,99 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Result, Store, StoreLimitsBuilder}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn store_try_new() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + OomTest::new().test(|| { + let _ = Store::try_new(&engine, ())?; + Ok(()) + }) +} + +#[test] +fn store_data() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + OomTest::new().test(|| { + let store = Store::try_new(&engine, 42u32)?; + assert_eq!(*store.data(), 42); + Ok(()) + }) +} + +#[test] +fn store_data_mut() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, 42u32)?; + *store.data_mut() = 99; + assert_eq!(*store.data(), 99); + Ok(()) + }) +} + +#[test] +fn store_engine() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + OomTest::new().test(|| { + let store = Store::try_new(&engine, ())?; + let _engine = store.engine(); + Ok(()) + }) +} + +#[test] +fn store_limiter() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, StoreLimitsBuilder::new().build())?; + store.limiter(|limits| limits); + Ok(()) + }) +} + +#[test] +fn store_fuel() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + config.consume_fuel(true); + let engine = Engine::new(&config)?; + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + store.set_fuel(100)?; + let fuel = store.get_fuel()?; + assert_eq!(fuel, 100); + Ok(()) + }) +} + +#[test] +fn store_epoch_deadline() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + config.epoch_interruption(true); + let engine = Engine::new(&config)?; + OomTest::new().test(|| { + let mut store = Store::try_new(&engine, ())?; + store.set_epoch_deadline(10); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/string.rs b/crates/fuzzing/tests/oom/string.rs new file mode 100644 index 000000000000..8eb6293c2666 --- /dev/null +++ b/crates/fuzzing/tests/oom/string.rs @@ -0,0 +1,101 @@ +use wasmtime::Result; +use wasmtime_environ::collections::TryString; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn try_string_with_capacity() -> Result<()> { + OomTest::new().test(|| { + let _s = TryString::with_capacity(100)?; + Ok(()) + }) +} + +#[test] +fn try_string_reserve() -> Result<()> { + OomTest::new().test(|| { + let mut s = TryString::new(); + s.reserve(10)?; + Ok(()) + }) +} + +#[test] +fn try_string_reserve_exact() -> Result<()> { + OomTest::new().test(|| { + let mut s = TryString::new(); + s.reserve_exact(3)?; + Ok(()) + }) +} + +#[test] +fn try_string_push() -> Result<()> { + OomTest::new().test(|| { + let mut s = TryString::new(); + s.push('c')?; + Ok(()) + }) +} + +#[test] +fn try_string_push_str() -> Result<()> { + OomTest::new().test(|| { + let mut s = TryString::new(); + s.push_str("hello")?; + Ok(()) + }) +} + +#[test] +fn try_string_shrink_to_fit() -> Result<()> { + OomTest::new().test(|| { + // len == cap == 0 + let mut s = TryString::new(); + s.shrink_to_fit()?; + + // len == 0 < cap + let mut s = TryString::with_capacity(4)?; + s.shrink_to_fit()?; + + // 0 < len < cap + let mut s = TryString::with_capacity(4)?; + s.push('a')?; + s.shrink_to_fit()?; + + // 0 < len == cap + let mut s = TryString::new(); + s.reserve_exact(2)?; + s.push('a')?; + s.push('a')?; + s.shrink_to_fit()?; + + Ok(()) + }) +} + +#[test] +fn try_string_into_boxed_str() -> Result<()> { + OomTest::new().test(|| { + // len == cap == 0 + let s = TryString::new(); + let _ = s.into_boxed_str()?; + + // len == 0 < cap + let s = TryString::with_capacity(4)?; + let _ = s.into_boxed_str()?; + + // 0 < len < cap + let mut s = TryString::with_capacity(4)?; + s.push('a')?; + let _ = s.into_boxed_str()?; + + // 0 < len == cap + let mut s = TryString::new(); + s.reserve_exact(2)?; + s.push('a')?; + s.push('a')?; + let _ = s.into_boxed_str()?; + + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/table.rs b/crates/fuzzing/tests/oom/table.rs new file mode 100644 index 000000000000..5e783aeb9552 --- /dev/null +++ b/crates/fuzzing/tests/oom/table.rs @@ -0,0 +1,136 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Ref, RefType, Result, Store, Table, TableType}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn table_new() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new() + // `IndexMap::reserve` will try to allocate double space, but if that + // fails, will attempt to allocate the minimal space necessary. + .allow_alloc_after_oom(true) + .test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = TableType::new(RefType::FUNCREF, 1, None); + let _table = wasmtime::Table::new(&mut store, ty, Ref::Func(None))?; + Ok(()) + }) +} + +#[test] +fn table_grow() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = TableType::new(RefType::FUNCREF, 1, None); + let table = Table::new(&mut store, ty, Ref::Func(None))?; + let _old_size = table.grow(&mut store, 4, Ref::Func(None))?; + Ok(()) + }) +} + +#[test] +fn table_set() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = TableType::new(RefType::FUNCREF, 1, None); + let table = Table::new(&mut store, ty, Ref::Func(None))?; + table.set(&mut store, 0, Ref::Func(None))?; + Ok(()) + }) +} + +#[test] +fn table_copy() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = TableType::new(RefType::FUNCREF, 4, None); + let table = Table::new(&mut store, ty, Ref::Func(None))?; + Table::copy(&mut store, &table, 0, &table, 2, 2)?; + Ok(()) + }) +} + +#[test] +fn table_fill() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = TableType::new(RefType::FUNCREF, 4, None); + let table = Table::new(&mut store, ty, Ref::Func(None))?; + table.fill(&mut store, 0, Ref::Func(None), 4)?; + Ok(()) + }) +} + +#[test] +fn table_get() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = TableType::new(RefType::FUNCREF, 1, None); + let table = Table::new(&mut store, ty, Ref::Func(None))?; + let val = table.get(&mut store, 0); + assert!(val.is_some()); + Ok(()) + }) +} + +#[test] +fn table_ty() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = TableType::new(RefType::FUNCREF, 1, None); + let table = Table::new(&mut store, ty, Ref::Func(None))?; + let _ty = table.ty(&store); + Ok(()) + }) +} + +#[test] +fn table_size() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let ty = TableType::new(RefType::FUNCREF, 2, None); + let table = Table::new(&mut store, ty, Ref::Func(None))?; + assert_eq!(table.size(&store), 2); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/tag.rs b/crates/fuzzing/tests/oom/tag.rs new file mode 100644 index 000000000000..416c8e122dce --- /dev/null +++ b/crates/fuzzing/tests/oom/tag.rs @@ -0,0 +1,56 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, FuncType, Result, Store, Tag, TagType}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn tag_new() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let func_ty = FuncType::try_new(&engine, [], [])?; + let tag_ty = TagType::new(func_ty); + let _tag = Tag::new(&mut store, &tag_ty)?; + Ok(()) + }) +} + +#[test] +fn tag_ty() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let func_ty = FuncType::try_new(&engine, [], [])?; + let tag_ty = TagType::new(func_ty); + let tag = Tag::new(&mut store, &tag_ty)?; + let _ty = tag.ty(&store); + Ok(()) + }) +} + +#[test] +fn tag_eq() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().allow_alloc_after_oom(true).test(|| { + let mut store = Store::try_new(&engine, ())?; + let func_ty = FuncType::try_new(&engine, [], [])?; + let tag_ty = TagType::new(func_ty); + let tag1 = Tag::new(&mut store, &tag_ty)?; + let tag2 = Tag::new(&mut store, &tag_ty)?; + assert!(Tag::eq(&tag1, &tag1, &store)); + assert!(!Tag::eq(&tag1, &tag2, &store)); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/types.rs b/crates/fuzzing/tests/oom/types.rs new file mode 100644 index 000000000000..ff5a156bbcd0 --- /dev/null +++ b/crates/fuzzing/tests/oom/types.rs @@ -0,0 +1,57 @@ +#![cfg(arc_try_new)] + +use wasmtime::{ + Config, Engine, FuncType, GlobalType, MemoryType, Mutability, RefType, Result, TableType, + ValType, +}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn func_type_params_results() -> Result<()> { + OomTest::new().test(|| { + let mut config = Config::new(); + config.enable_compiler(false); + let engine = Engine::new(&config)?; + let ty = FuncType::try_new(&engine, [ValType::I32, ValType::I64], [ValType::F32])?; + assert_eq!(ty.params().len(), 2); + assert_eq!(ty.results().len(), 1); + Ok(()) + }) +} + +#[test] +fn table_type_accessors() -> Result<()> { + OomTest::new().test(|| { + let ty = TableType::new(RefType::FUNCREF, 1, Some(10)); + assert_eq!(ty.minimum(), 1); + assert_eq!(ty.maximum(), Some(10)); + Ok(()) + }) +} + +#[test] +fn memory_type_accessors() -> Result<()> { + OomTest::new().test(|| { + let ty = MemoryType::new(1, Some(10)); + assert_eq!(ty.minimum(), 1); + assert_eq!(ty.maximum(), Some(10)); + assert!(!ty.is_64()); + assert!(!ty.is_shared()); + Ok(()) + }) +} + +#[test] +fn global_type_accessors() -> Result<()> { + OomTest::new().test(|| { + let ty = GlobalType::new(ValType::I32, Mutability::Var); + assert!(ty.content().is_i32()); + assert_eq!(ty.mutability(), Mutability::Var); + Ok(()) + }) +} + +// Note: ExnType::new, StructType::new, and ArrayType::new are not tested under +// OOM yet because their construction goes through the type registry and GC +// layout computation which has additional .panic_on_oom() calls deep in +// crates/environ/src/gc.rs that need to be addressed first. diff --git a/crates/fuzzing/tests/oom/val.rs b/crates/fuzzing/tests/oom/val.rs new file mode 100644 index 000000000000..fb8fceb7b89f --- /dev/null +++ b/crates/fuzzing/tests/oom/val.rs @@ -0,0 +1,38 @@ +#![cfg(arc_try_new)] + +use wasmtime::{Config, Engine, Result, Store, Val}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn val_i32_conversions() -> Result<()> { + OomTest::new().test(|| { + let val = Val::I32(42); + assert_eq!(val.unwrap_i32(), 42); + Ok(()) + }) +} + +#[test] +fn val_i64_conversions() -> Result<()> { + OomTest::new().test(|| { + let val = Val::I64(42); + assert_eq!(val.unwrap_i64(), 42); + Ok(()) + }) +} + +#[test] +fn val_null_func_ref() -> Result<()> { + let mut config = Config::new(); + config.enable_compiler(false); + config.concurrency_support(false); + let engine = Engine::new(&config)?; + + OomTest::new().test(|| { + let store = Store::try_new(&engine, ())?; + let val = Val::null_func_ref(); + let _ = &store; + assert!(val.ref_().is_some()); + Ok(()) + }) +} diff --git a/crates/fuzzing/tests/oom/vec.rs b/crates/fuzzing/tests/oom/vec.rs new file mode 100644 index 000000000000..a80893a0bde2 --- /dev/null +++ b/crates/fuzzing/tests/oom/vec.rs @@ -0,0 +1,185 @@ +use std::iter; +use wasmtime::Result; +use wasmtime_environ::collections::{TryCollect, TryVec, try_vec}; +use wasmtime_fuzzing::oom::OomTest; + +#[test] +fn try_vec_with_capacity() -> Result<()> { + OomTest::new().test(|| { + let _v = wasmtime_environ::collections::TryVec::::with_capacity(100)?; + Ok(()) + }) +} + +#[test] +fn try_vec_reserve() -> Result<()> { + OomTest::new().test(|| { + let mut v = wasmtime_environ::collections::TryVec::::new(); + v.reserve(10)?; + Ok(()) + }) +} + +#[test] +fn try_vec_reserve_exact() -> Result<()> { + OomTest::new().test(|| { + let mut v = wasmtime_environ::collections::TryVec::::new(); + v.reserve_exact(3)?; + Ok(()) + }) +} + +#[test] +fn try_vec_push() -> Result<()> { + OomTest::new().test(|| { + let mut v = wasmtime_environ::collections::TryVec::new(); + v.push(42)?; + Ok(()) + }) +} + +#[test] +fn try_vec_and_boxed_slice() -> Result<()> { + use wasmtime_core::alloc::TryVec; + + OomTest::new().test(|| { + // Nonzero-sized type. + let mut vec = TryVec::new(); + vec.push(1)?; + let slice = vec.into_boxed_slice()?; // len > 0, cap > 0 + + let mut vec = TryVec::from(slice); + vec.pop(); + let slice = vec.into_boxed_slice()?; // len = 0, cap > 0 + + let vec = TryVec::from(slice); + let _slice = vec.into_boxed_slice()?; // len = 0, cap = 0 + + let mut vec = TryVec::new(); + vec.reserve_exact(3)?; + vec.push(2)?; + vec.push(2)?; + vec.push(2)?; + let _slice = vec.into_boxed_slice()?; // len = cap, len > 0 + + for i in 0..12 { + let mut vec = TryVec::new(); + for j in 0..i { + vec.push(j)?; + } + let _slice = vec.into_boxed_slice()?; // len ?= cap + } + + // Zero-sized type. + let mut vec = TryVec::new(); + vec.push(())?; + let slice = vec.into_boxed_slice()?; // len > 0, cap > 0 + let mut vec = TryVec::from(slice); + vec.pop(); + let slice = vec.into_boxed_slice()?; // len = 0, cap > 0 + let vec = TryVec::from(slice); + let _ = vec.into_boxed_slice()?; // len = 0, cap = 0 + + Ok(()) + }) +} + +#[test] +fn try_vec_shrink_to_fit() -> Result<()> { + use wasmtime_core::alloc::TryVec; + + #[derive(Default)] + struct ZeroSized; + + #[derive(Default)] + struct NonZeroSized { + _unused: usize, + } + + fn do_test() -> Result<()> { + // len == cap == 0 + let mut v = TryVec::::new(); + v.shrink_to_fit()?; + + // len == 0 < cap + let mut v = TryVec::::with_capacity(4)?; + v.shrink_to_fit()?; + + // 0 < len < cap + let mut v = TryVec::with_capacity(4)?; + v.push(T::default())?; + v.shrink_to_fit()?; + + // 0 < len == cap + let mut v = TryVec::new(); + v.reserve_exact(2)?; + v.push(T::default())?; + v.push(T::default())?; + v.shrink_to_fit()?; + + Ok(()) + } + + OomTest::new().test(|| do_test::())?; + OomTest::new().test(|| do_test::())?; + Ok(()) +} + +#[test] +fn try_vec_resize() -> Result<()> { + OomTest::new().test(|| { + let mut v = TryVec::new(); + v.resize(10, 'a')?; // Grow. + v.resize(1, 'b')?; // Truncate. + v.resize(1, 'c')?; // Same length. + v.resize(3, 'd')?; // Grow again. + assert_eq!(&*v, &['a', 'd', 'd']); + Ok(()) + }) +} + +#[test] +fn try_vec_try_collect() -> Result<()> { + OomTest::new().test(|| { + iter::repeat(1).take(0).try_collect::, _>()?; + iter::repeat(1).take(1).try_collect::, _>()?; + iter::repeat(1).take(100).try_collect::, _>()?; + iter::repeat(()).take(100).try_collect::, _>()?; + Ok(()) + }) +} + +#[test] +fn try_vec_extend() -> Result<()> { + use wasmtime_core::alloc::{TryExtend, TryVec}; + OomTest::new().test(|| { + let mut vec = TryVec::new(); + vec.try_extend([])?; + vec.try_extend([1])?; + vec.try_extend([1, 2, 3, 4])?; + + let mut vec = TryVec::new(); + vec.try_extend([])?; + vec.try_extend([()])?; + vec.try_extend([(), (), ()])?; + Ok(()) + }) +} + +#[test] +fn try_vec_macro_elems() -> Result<()> { + OomTest::new().test(|| { + let v = try_vec![100, 200, 300, 400]?; + assert_eq!(&*v, &[100, 200, 300, 400]); + Ok(()) + }) +} + +#[test] +fn try_vec_macro_elem_len() -> Result<()> { + OomTest::new().test(|| { + let v = try_vec![100; 3]?; + assert_eq!(&*v, &[100, 100, 100]); + Ok(()) + }) +} diff --git a/crates/gdbstub-component/Cargo.toml b/crates/gdbstub-component/Cargo.toml new file mode 100644 index 000000000000..913fceada0bc --- /dev/null +++ b/crates/gdbstub-component/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "wasmtime-internal-gdbstub-component" +version.workspace = true +authors.workspace = true +edition.workspace = true +license = "Apache-2.0 WITH LLVM-exception" +description = "gdbstub debug-component adapter" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = { workspace = true, features = ["macros"] } +anyhow = { workspace = true } +clap = { workspace = true } +wstd = { workspace = true } +wasip2 = { workspace = true } +futures = { workspace = true, default-features = true } +gdbstub = { workspace = true } +gdbstub_arch = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } + +[lints] +workspace = true diff --git a/crates/gdbstub-component/artifact/Cargo.toml b/crates/gdbstub-component/artifact/Cargo.toml new file mode 100644 index 000000000000..b1db95f1d022 --- /dev/null +++ b/crates/gdbstub-component/artifact/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "wasmtime-internal-gdbstub-component-artifact" +version.workspace = true +authors.workspace = true +edition.workspace = true +license = "Apache-2.0 WITH LLVM-exception" +description = "gdbstub debug-component adapter" + +[lints] +workspace = true diff --git a/crates/gdbstub-component/artifact/build.rs b/crates/gdbstub-component/artifact/build.rs new file mode 100644 index 000000000000..c39b43f1168d --- /dev/null +++ b/crates/gdbstub-component/artifact/build.rs @@ -0,0 +1,77 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + // If we're not able to build our sibling crate because this is an + // isolated published copy of the crate (e.g. to crates.io), fail + // cleanly. The gdbstub component is included only in the published + // wasmtime build artifacts or builds from the source tree, not + // crates.io. + if !PathBuf::from("../Cargo.toml").exists() { + std::fs::write( + out_dir.join("gen.rs"), + "pub const GDBSTUB_COMPONENT: &[u8] = &[];\n", + ) + .unwrap(); + return; + } + + let mut cmd = cargo(); + cmd.arg("build") + .arg("--release") + .arg("--target=wasm32-wasip2") + .arg("--package=wasmtime-internal-gdbstub-component") + .env("CARGO_TARGET_DIR", &out_dir) + .env("RUSTFLAGS", rustflags()) + .env_remove("CARGO_ENCODED_RUSTFLAGS"); + eprintln!("running: {cmd:?}"); + let status = cmd.status().unwrap(); + assert!(status.success()); + + let wasm = out_dir + .join("wasm32-wasip2") + .join("release") + .join("wasmtime_internal_gdbstub_component.wasm"); + + // Read dep-info to get proper rerun-if-changed directives. + let deps_file = wasm.with_extension("d"); + if let Ok(contents) = std::fs::read_to_string(&deps_file) { + for line in contents.lines() { + let Some(pos) = line.find(": ") else { + continue; + }; + let line = &line[pos + 2..]; + let mut parts = line.split_whitespace(); + while let Some(part) = parts.next() { + let mut file = part.to_string(); + while file.ends_with('\\') { + file.pop(); + file.push(' '); + file.push_str(parts.next().unwrap()); + } + println!("cargo:rerun-if-changed={file}"); + } + } + } + + let generated = format!("pub const GDBSTUB_COMPONENT: &[u8] = include_bytes!({wasm:?});\n"); + std::fs::write(out_dir.join("gen.rs"), generated).unwrap(); +} + +fn cargo() -> Command { + let mut cargo = Command::new("cargo"); + if std::env::var("CARGO_CFG_MIRI").is_ok() { + cargo.env_remove("RUSTC").env_remove("RUSTC_WRAPPER"); + } + cargo +} + +fn rustflags() -> &'static str { + match option_env!("RUSTFLAGS") { + Some(s) if s.contains("-D warnings") => "-D warnings", + _ => "", + } +} diff --git a/crates/gdbstub-component/artifact/src/lib.rs b/crates/gdbstub-component/artifact/src/lib.rs new file mode 100644 index 000000000000..26a930a60ee6 --- /dev/null +++ b/crates/gdbstub-component/artifact/src/lib.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/gen.rs")); diff --git a/crates/gdbstub-component/src/addr.rs b/crates/gdbstub-component/src/addr.rs new file mode 100644 index 000000000000..92d1b0f9754d --- /dev/null +++ b/crates/gdbstub-component/src/addr.rs @@ -0,0 +1,186 @@ +//! Synthetic Wasm address space expected by the gdbstub Wasm +//! extensions. + +use crate::api::{Debuggee, Frame, Memory, Module}; +use anyhow::Result; +use gdbstub_arch::wasm::addr::{WasmAddr, WasmAddrType}; +use std::collections::{HashMap, hash_map::Entry}; + +/// Representation of the synthesized Wasm address space. +pub struct AddrSpace { + module_ids: HashMap, + memory_ids: HashMap, + modules: Vec, + module_bytecode: Vec>, + memories: Vec, +} + +/// The result of a lookup in the address space. +pub enum AddrSpaceLookup<'a> { + Module { + module: &'a Module, + bytecode: &'a [u8], + offset: u32, + }, + Memory { + memory: &'a Memory, + offset: u32, + }, + Empty, +} + +impl AddrSpace { + pub fn new() -> Self { + AddrSpace { + module_ids: HashMap::new(), + modules: vec![], + module_bytecode: vec![], + memory_ids: HashMap::new(), + memories: vec![], + } + } + + fn module_id(&mut self, m: &Module) -> u32 { + match self.module_ids.entry(m.unique_id()) { + Entry::Occupied(o) => *o.get(), + Entry::Vacant(v) => { + let id = u32::try_from(self.modules.len()).unwrap(); + let bytecode = m.bytecode().unwrap_or(vec![]); + self.module_bytecode.push(bytecode); + self.modules.push(m.clone()); + *v.insert(id) + } + } + } + + fn memory_id(&mut self, m: &Memory) -> u32 { + match self.memory_ids.entry(m.unique_id()) { + Entry::Occupied(o) => *o.get(), + Entry::Vacant(v) => { + let id = u32::try_from(self.memories.len()).unwrap(); + self.memories.push(m.clone()); + *v.insert(id) + } + } + } + + /// Update/create new mappings so that all modules and instances' + /// memories in the debuggee have mappings. + pub fn update(&mut self, d: &Debuggee) -> Result<()> { + for module in d.all_modules() { + let _ = self.module_id(&module); + } + for instance in d.all_instances() { + let mut idx = 0; + loop { + if let Ok(m) = instance.get_memory(d, idx) { + let _ = self.memory_id(&m); + idx += 1; + } else { + break; + } + } + } + Ok(()) + } + + /// Iterate over the base `WasmAddr` of every registered module. + pub fn module_base_addrs(&self) -> impl Iterator + '_ { + (0..self.modules.len()) + .map(|idx| WasmAddr::new(WasmAddrType::Object, u32::try_from(idx).unwrap(), 0).unwrap()) + } + + /// Build the GDB memory-map XML describing all known regions. + /// + /// Module bytecode regions are reported as `rom` (read-only), and + /// linear memories as `ram` (read-write). + pub fn memory_map_xml(&self, debuggee: &Debuggee) -> String { + use std::fmt::Write; + let mut xml = String::from( + "", + ); + for (idx, bc) in self.module_bytecode.iter().enumerate() { + let start = + WasmAddr::new(WasmAddrType::Object, u32::try_from(idx).unwrap(), 0).unwrap(); + let len = bc.len(); + if len > 0 { + write!( + xml, + "", + start.as_raw(), + len + ) + .unwrap(); + } + } + for (idx, mem) in self.memories.iter().enumerate() { + let start = + WasmAddr::new(WasmAddrType::Memory, u32::try_from(idx).unwrap(), 0).unwrap(); + let len = mem.size_bytes(debuggee); + if len > 0 { + write!( + xml, + "", + start.as_raw(), + len + ) + .unwrap(); + } + } + xml.push_str(""); + xml + } + + pub fn frame_to_pc(&self, frame: &Frame, debuggee: &Debuggee) -> WasmAddr { + let module = frame.get_instance(debuggee).unwrap().get_module(debuggee); + let &module_id = self + .module_ids + .get(&module.unique_id()) + .expect("module not found in addr space"); + let pc = frame.get_pc(debuggee).unwrap(); + WasmAddr::new(WasmAddrType::Object, module_id, pc).unwrap() + } + + pub fn frame_to_return_addr(&self, frame: &Frame, debuggee: &Debuggee) -> Option { + let module = frame.get_instance(debuggee).unwrap().get_module(debuggee); + let &module_id = self + .module_ids + .get(&module.unique_id()) + .expect("module not found in addr space"); + let ret_pc = frame.get_pc(debuggee).ok()?; + Some(WasmAddr::new(WasmAddrType::Object, module_id, ret_pc).unwrap()) + } + + pub fn lookup(&self, addr: WasmAddr, d: &Debuggee) -> AddrSpaceLookup<'_> { + let index = usize::try_from(addr.module_index()).unwrap(); + match addr.addr_type() { + WasmAddrType::Object => { + if index >= self.modules.len() { + return AddrSpaceLookup::Empty; + } + let bytecode = &self.module_bytecode[index]; + if addr.offset() >= u32::try_from(bytecode.len()).unwrap() { + return AddrSpaceLookup::Empty; + } + AddrSpaceLookup::Module { + module: &self.modules[index], + bytecode, + offset: addr.offset(), + } + } + WasmAddrType::Memory => { + if index >= self.memories.len() { + return AddrSpaceLookup::Empty; + } + let size = self.memories[index].size_bytes(d); + if u64::from(addr.offset()) >= size { + return AddrSpaceLookup::Empty; + } + AddrSpaceLookup::Memory { + memory: &self.memories[index], + offset: addr.offset(), + } + } + } + } +} diff --git a/crates/gdbstub-component/src/api.rs b/crates/gdbstub-component/src/api.rs new file mode 100644 index 000000000000..a33e3057a923 --- /dev/null +++ b/crates/gdbstub-component/src/api.rs @@ -0,0 +1,44 @@ +//! Bindings for Wasmtime's debugger API. + +use wstd::runtime::AsyncPollable; + +wit_bindgen::generate!({ + world: "bytecodealliance:wasmtime/debug-main", + path: "../debugger/wit", + with: { + "wasi:io/poll@0.2.6": wasip2::io::poll, + } +}); +pub(crate) use bytecodealliance::wasmtime::debuggee::*; + +/// One "resumption", or period of execution, in the debuggee. +pub struct Resumption { + future: EventFuture, + pollable: Option, +} + +impl Resumption { + pub fn continue_(d: &Debuggee, r: ResumptionValue) -> Self { + let future = d.continue_(r); + let pollable = Some(AsyncPollable::new(future.subscribe())); + Resumption { future, pollable } + } + + pub fn single_step(d: &Debuggee, r: ResumptionValue) -> Self { + let future = d.single_step(r); + let pollable = Some(AsyncPollable::new(future.subscribe())); + Resumption { future, pollable } + } + + pub async fn wait(&mut self) { + if let Some(pollable) = self.pollable.as_mut() { + pollable.wait_for().await; + } + } + + pub fn result(mut self, d: &Debuggee) -> std::result::Result { + // Drop the pollable first, since it's a child resource. + let _ = self.pollable.take(); + EventFuture::finish(self.future, d) + } +} diff --git a/crates/gdbstub-component/src/lib.rs b/crates/gdbstub-component/src/lib.rs new file mode 100644 index 000000000000..bcc929b2bba9 --- /dev/null +++ b/crates/gdbstub-component/src/lib.rs @@ -0,0 +1,361 @@ +//! gdbstub protocol implementation in Wasmtime's debug-main world. + +mod addr; +mod api; +mod target; + +use crate::{ + addr::AddrSpace, + api::{WasmType, WasmValue}, +}; +use anyhow::Result; +use clap::Parser; +use futures::{FutureExt, select}; +use gdbstub::{ + common::{Signal, Tid}, + conn::Connection, + stub::{ + MultiThreadStopReason, + state_machine::{GdbStubStateMachine, GdbStubStateMachineInner, state::Running}, + }, +}; +use gdbstub_arch::wasm::addr::WasmAddr; +use log::trace; +use wstd::{ + io::{AsyncRead, AsyncWrite}, + iter::AsyncIterator, + net::{TcpListener, TcpStream}, +}; + +/// Command-line options. +#[derive(Parser)] +struct Options { + /// The TCP address to listen on, in `:` format. + tcp_address: String, + /// Verbose logging. + #[clap(short = 'v')] + verbose: bool, +} + +struct Component; +api::export!(Component with_types_in api); + +impl api::exports::bytecodealliance::wasmtime::debugger::Guest for Component { + fn debug(d: &api::Debuggee, args: Vec) { + let options = Options::parse_from(args); + if options.verbose { + env_logger::Builder::new() + .filter_level(log::LevelFilter::Trace) + .init(); + } + let mut debugger = Debugger { + debuggee: d, + tid: Tid::new(1).unwrap(), + options, + running: None, + current_pc: WasmAddr::from_raw(0).unwrap(), + interrupt: false, + single_stepping: false, + frame_cache: vec![], + addr_space: AddrSpace::new(), + }; + wstd::runtime::block_on(async { + if let Err(e) = debugger.run().await { + trace!("debugger exited with error: {e}"); + } + }); + } +} + +struct Debugger<'a> { + debuggee: &'a api::Debuggee, + tid: Tid, + options: Options, + running: Option, + addr_space: AddrSpace, + interrupt: bool, + single_stepping: bool, + current_pc: WasmAddr, + frame_cache: Vec, +} + +impl<'a> Debugger<'a> { + async fn run(&mut self) -> Result<()> { + // Load module info from the debuggee. Modules are + // pre-registered on the debuggee store before the Debuggee is + // created, so they are visible here without needing to + // execute any Wasm. + self.update_on_stop(); + + let listener = TcpListener::bind(&self.options.tcp_address) + .await + .expect("Could not bind to TCP port"); + + api::print_debugger_info(&format!( + "Debugger listening on {}", + self.options.tcp_address, + )); + api::print_debugger_info(&format!( + "In LLDB, attach with: process connect --plugin wasm connect://{}", + self.options.tcp_address, + )); + + // Only accept one connection for the run; once the debugger + // disconnects, we'll just continue. + let Some(connection) = listener.incoming().next().await else { + return Ok(()); + }; + + let gdbconn = Conn::new(connection?); + let mut stub = gdbstub::stub::GdbStub::new(gdbconn).run_state_machine(&mut *self)?; + + // Main loop. + 'mainloop: loop { + match stub { + GdbStubStateMachine::Idle(mut inner) => { + if inner.borrow_conn().flush().await.is_err() { + // Connection closed or other outbound error. + break 'mainloop; + } + + // Wait for an inbound network byte. + let Some(byte) = inner.borrow_conn().read_byte().await? else { + inner.borrow_conn().flush().await?; + log::info!("Connection closed; debugger detached."); + break 'mainloop; + }; + + stub = inner.incoming_data(self, byte)?; + } + + GdbStubStateMachine::Running(mut inner) => { + if inner.borrow_conn().flush().await.is_err() { + // Connection closed or other outbound error. + break 'mainloop; + } + + // Wait for either a resumption or a byte from the + // connection. + let resumption = self + .running + .as_mut() + .expect("In Running state, we must have a resumption future"); + select! { + _ = resumption.wait().fuse() => { + let resumption = self.running.take().unwrap(); + let event = resumption.result(self.debuggee)?; + stub = self.handle_event(event, inner).await?; + } + byte = inner.borrow_conn().read_byte().fuse() => { + let Some(byte) = byte? else { + // Eat any connection-closed errors on + // the outbound flush. + let _ = inner.borrow_conn().flush().await; + // Connection closed. + log::info!("Connection closed; debugger detached."); + break 'mainloop; + }; + stub = inner.incoming_data(&mut *self, byte)?; + } + } + } + GdbStubStateMachine::CtrlCInterrupt(mut inner) => { + if inner.borrow_conn().flush().await.is_err() { + // Connection error: break. + break 'mainloop; + } + stub = inner.interrupt_handled(self, None::>)?; + } + GdbStubStateMachine::Disconnected(mut inner) => { + // Eat any connection-closed errors -- we are + // already in Disconnected state. + let _ = inner.borrow_conn().flush().await; + break 'mainloop; + } + } + } + + Ok(()) + } + + fn start_continue(&mut self, resumption: api::ResumptionValue) { + assert!(self.running.is_none()); + trace!("continuing"); + self.single_stepping = false; + self.running = Some(api::Resumption::continue_(self.debuggee, resumption)); + } + + fn start_single_step(&mut self, resumption: api::ResumptionValue) { + assert!(self.running.is_none()); + trace!("single-stepping"); + self.single_stepping = true; + self.running = Some(api::Resumption::single_step(self.debuggee, resumption)); + } + + fn update_on_stop(&mut self) { + self.addr_space.update(self.debuggee).unwrap(); + + // Cache all frame handles for the duration of this stop. + // The Wasm trait methods take `&self` and need access to + // frames by depth, so we eagerly walk the full stack here. + self.frame_cache.clear(); + let mut next = self.debuggee.exit_frames().into_iter().next(); + while let Some(f) = next { + next = f.parent_frame(self.debuggee).unwrap(); + self.frame_cache.push(f); + } + + if let Some(f) = self.frame_cache.first() { + self.current_pc = self.addr_space.frame_to_pc(f, self.debuggee); + } else { + self.current_pc = WasmAddr::from_raw(0).unwrap(); + } + } + + async fn handle_event<'b>( + &mut self, + event: api::Event, + inner: GdbStubStateMachineInner<'b, Running, Self, Conn>, + ) -> Result> { + match event { + api::Event::Complete => { + trace!("Event::Complete"); + let pc_bytes = self.current_pc.as_raw().to_le_bytes(); + let mut regs = core::iter::once(( + gdbstub_arch::wasm::reg::id::WasmRegId::Pc, + pc_bytes.as_slice(), + )); + Ok(inner.report_stop_with_regs( + self, + MultiThreadStopReason::Exited(0), + &mut regs, + )?) + } + api::Event::Breakpoint => { + trace!( + "Event::Breakpoint; single_stepping = {}", + self.single_stepping + ); + self.update_on_stop(); + let stop_reason = if self.single_stepping { + MultiThreadStopReason::SignalWithThread { + tid: self.tid, + signal: Signal::SIGTRAP, + } + } else { + MultiThreadStopReason::SwBreak(self.tid) + }; + let pc_bytes = self.current_pc.as_raw().to_le_bytes(); + let mut regs = core::iter::once(( + gdbstub_arch::wasm::reg::id::WasmRegId::Pc, + pc_bytes.as_slice(), + )); + Ok(inner.report_stop_with_regs(self, stop_reason, &mut regs)?) + } + api::Event::Trap => { + trace!("Event::Trap"); + self.update_on_stop(); + let pc_bytes = self.current_pc.as_raw().to_le_bytes(); + let mut regs = core::iter::once(( + gdbstub_arch::wasm::reg::id::WasmRegId::Pc, + pc_bytes.as_slice(), + )); + Ok(inner.report_stop_with_regs( + self, + // We report all traps as SIGSEGV for now. + MultiThreadStopReason::SignalWithThread { + tid: self.tid, + signal: Signal::SIGSEGV, + }, + &mut regs, + )?) + } + _ => { + trace!("other event: {event:?}"); + if self.interrupt { + self.interrupt = false; + self.update_on_stop(); + let pc_bytes = self.current_pc.as_raw().to_le_bytes(); + let mut regs = core::iter::once(( + gdbstub_arch::wasm::reg::id::WasmRegId::Pc, + pc_bytes.as_slice(), + )); + Ok(inner.report_stop_with_regs( + self, + MultiThreadStopReason::Signal(Signal::SIGINT), + &mut regs, + )?) + } else { + if self.single_stepping { + self.start_single_step(api::ResumptionValue::Normal); + } else { + self.start_continue(api::ResumptionValue::Normal); + } + Ok(GdbStubStateMachine::Running(inner)) + } + } + } + } + + fn value_to_bytes(&self, value: WasmValue) -> Vec { + match value.get_type() { + WasmType::WasmI32 => value.unwrap_i32().to_le_bytes().to_vec(), + WasmType::WasmI64 => value.unwrap_i64().to_le_bytes().to_vec(), + WasmType::WasmF32 => value.unwrap_f32().to_le_bytes().to_vec(), + WasmType::WasmF64 => value.unwrap_f64().to_le_bytes().to_vec(), + WasmType::WasmV128 => value.unwrap_v128(), + WasmType::WasmFuncref => 0u32.to_le_bytes().to_vec(), + WasmType::WasmExnref => 0u32.to_le_bytes().to_vec(), + } + } +} + +struct Conn { + buf: Vec, + conn: TcpStream, +} + +impl Conn { + fn new(conn: TcpStream) -> Self { + Conn { buf: vec![], conn } + } + + async fn flush(&mut self) -> anyhow::Result<()> { + self.conn.write_all(&self.buf).await?; + self.buf.clear(); + Ok(()) + } + + async fn read_byte(&mut self) -> Result> { + let mut buf = [0u8]; + let len = self.conn.read(&mut buf).await?; + if len == 1 { Ok(Some(buf[0])) } else { Ok(None) } + } +} + +impl Drop for Conn { + fn drop(&mut self) { + assert!( + self.buf.is_empty(), + "failed to async-flush before dropping connection write buffer" + ); + } +} + +impl Connection for Conn { + type Error = anyhow::Error; + + fn write(&mut self, byte: u8) -> std::result::Result<(), Self::Error> { + self.buf.push(byte); + Ok(()) + } + + fn flush(&mut self) -> std::result::Result<(), Self::Error> { + // We cannot flush synchronously; we leave this to the `async + // fn flush` method called within the main loop. Fortunately + // the gdbstub cannot wait for a response before returning to + // the main loop, so we cannot introduce any deadlocks by + // failing to flush synchronously here. + Ok(()) + } +} diff --git a/crates/gdbstub-component/src/target.rs b/crates/gdbstub-component/src/target.rs new file mode 100644 index 000000000000..c54cf9e58e3a --- /dev/null +++ b/crates/gdbstub-component/src/target.rs @@ -0,0 +1,464 @@ +//! gdbstub `Target` implementation. + +use crate::Debugger; +use crate::addr::AddrSpaceLookup; +use crate::api; +use gdbstub::arch::lldb::{Encoding, Format, Generic, Register}; +use gdbstub::common::{Endianness, Pid, Signal, Tid}; +use gdbstub::target::Target; +use gdbstub::target::TargetError; +use gdbstub::target::TargetResult; +use gdbstub::target::ext::base::BaseOps; +use gdbstub::target::ext::base::multithread::{ + MultiThreadBase, MultiThreadResume, MultiThreadResumeOps, MultiThreadSchedulerLocking, + MultiThreadSchedulerLockingOps, MultiThreadSingleStep, MultiThreadSingleStepOps, +}; +use gdbstub::target::ext::base::single_register_access::{ + SingleRegisterAccess, SingleRegisterAccessOps, +}; +use gdbstub::target::ext::breakpoints::{ + Breakpoints, BreakpointsOps, SwBreakpoint, SwBreakpointOps, +}; +use gdbstub::target::ext::host_info::{HostInfo, HostInfoOps, HostInfoResponse}; +use gdbstub::target::ext::libraries::{Libraries, LibrariesOps}; +use gdbstub::target::ext::lldb_register_info_override::{ + Callback, CallbackToken, LldbRegisterInfoOverride, LldbRegisterInfoOverrideOps, +}; +use gdbstub::target::ext::memory_map::{MemoryMap, MemoryMapOps}; +use gdbstub::target::ext::process_info::{ProcessInfo, ProcessInfoOps, ProcessInfoResponse}; +use gdbstub::target::ext::wasm::{Wasm, WasmOps}; +use gdbstub_arch::wasm::Wasm as WasmArch; +use gdbstub_arch::wasm::addr::WasmAddr; +use gdbstub_arch::wasm::reg::WasmRegisters; +use gdbstub_arch::wasm::reg::id::WasmRegId; + +impl<'a> Target for Debugger<'a> { + type Arch = WasmArch; + type Error = anyhow::Error; + + fn base_ops(&mut self) -> BaseOps<'_, Self::Arch, Self::Error> { + BaseOps::MultiThread(self) + } + + fn support_wasm(&mut self) -> Option> { + Some(self) + } + + fn use_lldb_register_info(&self) -> bool { + true + } + + fn support_lldb_register_info_override( + &mut self, + ) -> Option> { + Some(self) + } + + fn support_breakpoints(&mut self) -> Option> { + Some(self) + } + + fn support_libraries(&mut self) -> Option> { + Some(self) + } + + fn support_memory_map(&mut self) -> Option> { + Some(self) + } + + fn support_process_info(&mut self) -> Option> { + Some(self) + } + + fn support_host_info(&mut self) -> Option> { + Some(self) + } +} + +impl<'a> MultiThreadBase for Debugger<'a> { + fn read_registers(&mut self, regs: &mut WasmRegisters, _tid: Tid) -> TargetResult<(), Self> { + regs.pc = self.current_pc.as_raw(); + Ok(()) + } + + fn write_registers(&mut self, regs: &WasmRegisters, _tid: Tid) -> TargetResult<(), Self> { + self.current_pc = WasmAddr::from_raw(regs.pc).ok_or(TargetError::NonFatal)?; + Ok(()) + } + + fn read_addrs( + &mut self, + start_addr: u64, + data: &mut [u8], + _tid: Tid, + ) -> TargetResult { + let addr = WasmAddr::from_raw(start_addr).ok_or(TargetError::NonFatal)?; + let debuggee = self.debuggee; + match self.addr_space.lookup(addr, debuggee) { + AddrSpaceLookup::Module { + bytecode, offset, .. + } => { + let offset = usize::try_from(offset).unwrap(); + let avail = bytecode.len() - offset; + let n = avail.min(data.len()); + data[..n].copy_from_slice(&bytecode[offset..offset + n]); + Ok(n) + } + AddrSpaceLookup::Memory { memory, offset } => { + match memory.get_bytes(debuggee, offset.into(), u64::try_from(data.len()).unwrap()) + { + Ok(bytes) => { + assert_eq!(bytes.len(), data.len()); + data.copy_from_slice(&bytes); + Ok(data.len()) + } + Err(_) => Err(TargetError::NonFatal), + } + } + AddrSpaceLookup::Empty => Err(TargetError::NonFatal), + } + } + + fn write_addrs(&mut self, _start_addr: u64, _data: &[u8], _tid: Tid) -> TargetResult<(), Self> { + Err(TargetError::NonFatal) + } + + fn list_active_threads( + &mut self, + thread_is_active: &mut dyn FnMut(Tid), + ) -> Result<(), Self::Error> { + thread_is_active(self.tid); + Ok(()) + } + + fn support_single_register_access(&mut self) -> Option> { + Some(self) + } + + fn support_resume(&mut self) -> Option> { + Some(self) + } +} + +impl<'a> SingleRegisterAccess for Debugger<'a> { + fn read_register( + &mut self, + _tid: Tid, + reg_id: WasmRegId, + buf: &mut [u8], + ) -> TargetResult { + match reg_id { + WasmRegId::Pc => { + let bytes = self.current_pc.as_raw().to_le_bytes(); + let n = bytes.len().min(buf.len()); + buf[..n].copy_from_slice(&bytes[..n]); + Ok(n) + } + _ => Err(TargetError::NonFatal), + } + } + + fn write_register( + &mut self, + _tid: Tid, + reg_id: WasmRegId, + val: &[u8], + ) -> TargetResult<(), Self> { + match reg_id { + WasmRegId::Pc => { + if val.len() < 8 { + return Err(TargetError::NonFatal); + } + let raw = u64::from_le_bytes(val[..8].try_into().unwrap()); + self.current_pc = WasmAddr::from_raw(raw).ok_or(TargetError::NonFatal)?; + Ok(()) + } + _ => Err(TargetError::NonFatal), + } + } +} + +impl<'a> MultiThreadResume for Debugger<'a> { + fn resume(&mut self) -> Result<(), Self::Error> { + self.frame_cache.clear(); + log::trace!("resume() -> single_stepping = {}", self.single_stepping); + if self.single_stepping { + self.start_single_step(api::ResumptionValue::Normal); + } else { + self.start_continue(api::ResumptionValue::Normal); + } + Ok(()) + } + + fn clear_resume_actions(&mut self) -> Result<(), Self::Error> { + self.single_stepping = false; + Ok(()) + } + + fn set_resume_action_continue( + &mut self, + _tid: Tid, + _signal: Option, + ) -> Result<(), Self::Error> { + self.single_stepping = false; + Ok(()) + } + + fn support_single_step(&mut self) -> Option> { + Some(self) + } + + fn support_scheduler_locking(&mut self) -> Option> { + Some(self) + } +} + +impl<'a> MultiThreadSingleStep for Debugger<'a> { + fn set_resume_action_step( + &mut self, + _tid: Tid, + _signal: Option, + ) -> Result<(), Self::Error> { + self.single_stepping = true; + Ok(()) + } +} + +impl<'a> MultiThreadSchedulerLocking for Debugger<'a> { + fn set_resume_action_scheduler_lock(&mut self) -> Result<(), Self::Error> { + // We have a single thread, so scheduler locking is a no-op. + Ok(()) + } +} + +impl<'a> Breakpoints for Debugger<'a> { + #[inline(always)] + fn support_sw_breakpoint(&mut self) -> Option> { + Some(self) + } +} + +impl<'a> SwBreakpoint for Debugger<'a> { + fn add_sw_breakpoint(&mut self, addr: u64, _kind: usize) -> TargetResult { + let Some(wasm_addr) = WasmAddr::from_raw(addr) else { + return Ok(false); + }; + let debuggee = self.debuggee; + if let AddrSpaceLookup::Module { module, .. } = self.addr_space.lookup(wasm_addr, debuggee) + { + module + .add_breakpoint(debuggee, wasm_addr.offset()) + .map_err(|_| TargetError::NonFatal)?; + Ok(true) + } else { + Ok(false) + } + } + + fn remove_sw_breakpoint(&mut self, addr: u64, _kind: usize) -> TargetResult { + let Some(wasm_addr) = WasmAddr::from_raw(addr) else { + return Ok(false); + }; + let debuggee = self.debuggee; + if let AddrSpaceLookup::Module { module, .. } = self.addr_space.lookup(wasm_addr, debuggee) + { + module + .remove_breakpoint(debuggee, wasm_addr.offset()) + .map_err(|_| TargetError::NonFatal)?; + Ok(true) + } else { + Ok(false) + } + } +} + +impl<'a> LldbRegisterInfoOverride for Debugger<'a> { + fn lldb_register_info<'b>( + &mut self, + reg_id: usize, + reg_info: Callback<'b>, + ) -> Result, Self::Error> { + Ok(match reg_id { + 0 => reg_info.write(Register { + name: "pc", + alt_name: Some("pc"), + bitsize: 64, + offset: 0, + encoding: Encoding::Uint, + format: Format::Hex, + set: "PC", + gcc: Some(16), + dwarf: Some(16), + generic: Some(Generic::Pc), + container_regs: None, + invalidate_regs: None, + }), + _ => reg_info.done(), + }) + } +} + +impl<'a> Libraries for Debugger<'a> { + fn get_libraries( + &self, + offset: u64, + length: usize, + buf: &mut [u8], + ) -> TargetResult { + let mut xml = String::from(""); + for (i, addr) in self.addr_space.module_base_addrs().enumerate() { + // Each library entry needs a unique name; LLDB's + // DynamicLoader deduplicates by name, so using the same + // name for all modules causes only the first to be loaded. + xml.push_str(&format!( + "
", + addr.as_raw() + )); + } + xml.push_str(""); + + let xml_bytes = xml.as_bytes(); + let offset = usize::try_from(offset).unwrap(); + if offset >= xml_bytes.len() { + return Ok(0); + } + let avail = xml_bytes.len() - offset; + let n = avail.min(length).min(buf.len()); + buf[..n].copy_from_slice(&xml_bytes[offset..offset + n]); + Ok(n) + } +} + +impl<'a> MemoryMap for Debugger<'a> { + fn memory_map_xml( + &self, + offset: u64, + length: usize, + buf: &mut [u8], + ) -> TargetResult { + let xml = self.addr_space.memory_map_xml(self.debuggee); + let xml_bytes = xml.as_bytes(); + let offset = usize::try_from(offset).unwrap(); + if offset >= xml_bytes.len() { + return Ok(0); + } + let avail = xml_bytes.len() - offset; + let n = avail.min(length).min(buf.len()); + buf[..n].copy_from_slice(&xml_bytes[offset..offset + n]); + Ok(n) + } +} + +impl<'a> Wasm for Debugger<'a> { + fn wasm_call_stack(&self, _tid: Tid, callback: &mut dyn FnMut(u64)) -> Result<(), Self::Error> { + let debuggee = self.debuggee; + for (i, f) in self.frame_cache.iter().enumerate() { + // For non-innermost frames, report the return address + // (the instruction after the call) rather than the call + // instruction's PC. This matches the standard debugger + // convention and is needed for LLDB's `finish` command + // to set a breakpoint at the right address. + let pc = if i > 0 { + self.addr_space + .frame_to_return_addr(f, debuggee) + .unwrap_or_else(|| self.addr_space.frame_to_pc(f, debuggee)) + } else { + self.addr_space.frame_to_pc(f, debuggee) + }; + callback(pc.as_raw()); + } + Ok(()) + } + + fn read_wasm_local( + &self, + _tid: Tid, + frame_depth: usize, + index: usize, + buf: &mut [u8], + ) -> Result { + let Some(f) = self.frame_cache.get(frame_depth) else { + return Ok(0); + }; + let Ok(locals) = f.get_locals(self.debuggee) else { + return Ok(0); + }; + let Some(val) = locals.get(index) else { + return Ok(0); + }; + let bytes = self.value_to_bytes(val.clone()); + buf[..bytes.len()].copy_from_slice(&bytes); + Ok(bytes.len()) + } + + fn read_wasm_global( + &self, + _tid: Tid, + frame_depth: usize, + index: usize, + buf: &mut [u8], + ) -> Result { + let Some(f) = self.frame_cache.get(frame_depth) else { + return Ok(0); + }; + let debuggee = self.debuggee; + let Ok(instance) = f.get_instance(debuggee) else { + return Ok(0); + }; + let Ok(global) = instance.get_global(debuggee, u32::try_from(index).unwrap()) else { + return Ok(0); + }; + let Ok(val) = global.get(debuggee) else { + return Ok(0); + }; + let bytes = self.value_to_bytes(val); + buf[..bytes.len()].copy_from_slice(&bytes); + Ok(bytes.len()) + } + + fn read_wasm_stack( + &self, + _tid: Tid, + frame_depth: usize, + index: usize, + buf: &mut [u8], + ) -> Result { + let Some(f) = self.frame_cache.get(frame_depth) else { + return Ok(0); + }; + let Ok(stack) = f.get_stack(self.debuggee) else { + return Ok(0); + }; + let Some(val) = stack.get(index) else { + return Ok(0); + }; + let bytes = self.value_to_bytes(val.clone()); + buf[..bytes.len()].copy_from_slice(&bytes); + Ok(bytes.len()) + } +} + +impl<'a> HostInfo for Debugger<'a> { + fn host_info( + &self, + write_item: &mut dyn FnMut(&HostInfoResponse<'_>), + ) -> Result<(), Self::Error> { + write_item(&HostInfoResponse::Triple("wasm32-unknown-unknown-wasm")); + write_item(&HostInfoResponse::Endianness(Endianness::Little)); + write_item(&HostInfoResponse::PointerSize(4)); + Ok(()) + } +} + +impl<'a> ProcessInfo for Debugger<'a> { + fn process_info( + &self, + write_item: &mut dyn FnMut(&ProcessInfoResponse<'_>), + ) -> Result<(), Self::Error> { + write_item(&ProcessInfoResponse::Pid(Pid::new(1).unwrap())); + write_item(&ProcessInfoResponse::Triple("wasm32-unknown-unknown-wasm")); + write_item(&ProcessInfoResponse::Endianness(Endianness::Little)); + write_item(&ProcessInfoResponse::PointerSize(4)); + Ok(()) + } +} diff --git a/crates/test-macros/src/wasmtime_test.rs b/crates/test-macros/src/wasmtime_test.rs index baeb695810eb..7ca3726811c9 100644 --- a/crates/test-macros/src/wasmtime_test.rs +++ b/crates/test-macros/src/wasmtime_test.rs @@ -228,7 +228,7 @@ fn expand(test_config: &TestConfig, func: Fn) -> Result { let func_name = &func.sig.ident; match &func.sig.output { ReturnType::Default => { - return Err(syn::Error::new(func_name.span(), "Expected `Restult<()>`")); + return Err(syn::Error::new(func_name.span(), "Expected `Result<()>`")); } ReturnType::Type(..) => {} }; diff --git a/crates/test-programs/Cargo.toml b/crates/test-programs/Cargo.toml index cd52866eab63..f79059d4cf22 100644 --- a/crates/test-programs/Cargo.toml +++ b/crates/test-programs/Cargo.toml @@ -26,3 +26,4 @@ flate2 = "1.0.28" log = { workspace = true } env_logger = { workspace = true } pin-project-lite = { workspace = true } + diff --git a/crates/test-programs/artifacts/build.rs b/crates/test-programs/artifacts/build.rs index 4b20673fb474..cb9467aa882f 100644 --- a/crates/test-programs/artifacts/build.rs +++ b/crates/test-programs/artifacts/build.rs @@ -87,16 +87,19 @@ impl Artifacts { s if s.starts_with("p1_") => "p1", s if s.starts_with("p2_http_") => "p2_http", s if s.starts_with("p2_api_") => "p2_api", + s if s.starts_with("p2_tls_") => "p2_tls", s if s.starts_with("p2_") => "p2", s if s.starts_with("p3_http_") => "p3_http", s if s.starts_with("p3_api_") => "p3_api", + s if s.starts_with("p3_tls_") => "p3_tls", s if s.starts_with("p3_") => "p3", s if s.starts_with("nn_") => "nn", s if s.starts_with("piped_") => "piped", + s if s.starts_with("debugger_") => "debugger", + s if s.starts_with("guest_debug_") => "guest_debug", s if s.starts_with("dwarf_") => "dwarf", s if s.starts_with("config_") => "config", s if s.starts_with("keyvalue_") => "keyvalue", - s if s.starts_with("tls_") => "tls", s if s.starts_with("async_") => "async", s if s.starts_with("fuzz_") => "fuzz", // If you're reading this because you hit this panic, either add diff --git a/crates/test-programs/src/bin/async_cancel_caller.rs b/crates/test-programs/src/bin/async_cancel_caller.rs index e9b63bb1c26c..057cb1a067e4 100644 --- a/crates/test-programs/src/bin/async_cancel_caller.rs +++ b/crates/test-programs/src/bin/async_cancel_caller.rs @@ -139,7 +139,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 } => { assert_eq!(event0, EVENT_NONE); - // First, call and cancel `yield_with_options::yield_tiems` + // First, call and cancel `yield_with_options::yield_times` // with backpressure enabled. Cancelling should not block since // the call will not even have started. diff --git a/crates/test-programs/src/bin/debugger_component.rs b/crates/test-programs/src/bin/debugger_component.rs new file mode 100644 index 000000000000..482d6d1f1518 --- /dev/null +++ b/crates/test-programs/src/bin/debugger_component.rs @@ -0,0 +1,128 @@ +//! Debug-main guest to run debugger tests. +//! +//! Invoked by tests/all/debug_component.rs with particular debuggees +//! loaded for each test (selected by argv) below. We print "OK" to +//! stderr to communicate success. + +use std::time::Duration; + +mod api { + wit_bindgen::generate!({ + world: "bytecodealliance:wasmtime/debug-main", + path: "../../crates/debugger/wit", + with: { + "wasi:io/poll@0.2.6": wasip2::io::poll, + } + }); +} +use api::bytecodealliance::wasmtime::debuggee::*; + +struct Component; +api::export!(Component with_types_in api); + +impl api::exports::bytecodealliance::wasmtime::debugger::Guest for Component { + fn debug(d: &Debuggee, args: Vec) { + match args.get(1).map(|s| s.as_str()) { + Some("simple") => { + test_simple(d); + } + Some("loop") => { + test_loop(d); + } + other => panic!("unknown test mode: {other:?}"), + } + } +} + +struct Resumption { + future: EventFuture, +} + +impl Resumption { + fn single_step(d: &Debuggee) -> Self { + let future = d.single_step(ResumptionValue::Normal); + Self { future } + } + + fn continue_(d: &Debuggee) -> Self { + let future = d.continue_(ResumptionValue::Normal); + Self { future } + } + + fn result(self, d: &Debuggee) -> Result { + EventFuture::finish(self.future, d) + } +} + +/// Tests single-stepping. +/// +/// Tests against `debugger_debuggee_simple.wat`. +fn test_simple(d: &Debuggee) { + // Step once to reach the first instruction. + let r = Resumption::single_step(d); + let _event = r.result(d).unwrap(); + + let mut pcs = vec![]; + + for _ in 0..5 { + let frames = d.exit_frames(); + let pc = frames[0].get_pc(d).unwrap(); + pcs.push(pc); + + let r = Resumption::single_step(d); + match r.result(d).unwrap() { + Event::Breakpoint => {} + other => panic!("unexpected event: {other:?}"), + } + } + + // There should be five PCs and they should each be distinct from the previous. + assert_eq!(pcs.len(), 5); + assert!(pcs.windows(2).all(|p| p[0] != p[1])); + + eprintln!("OK"); +} + +/// Interrupt test: continue an infinite-loop debuggee, interrupt it, +/// verify the interrupt, then set the exit flag in memory and continue +/// to completion. +/// +/// Tests against `debugger_debuggee_loop.wat`. +fn test_loop(d: &Debuggee) { + // Continue execution (the debuggee should loop). + let r = Resumption::continue_(d); + + // Yield to the event loop and let it run for a bit. + std::thread::sleep(Duration::from_millis(100)); + + // Request interrupt. + d.interrupt(); + + // Wait for the interrupt event. + let event = r.result(d).unwrap(); + assert!( + matches!(event, Event::Interrupted), + "expected Interrupted, got {event:?}" + ); + + // Set the exit-flag to kill the infinite loop in the guest (the + // debugger environment will not otherwise end until the guest + // ends; we have no way of forcing an early exit yet). + for inst in &d.all_instances() { + if let Ok(mem) = inst.get_memory(d, 0) { + mem.set_bytes(d, 0, &[1]).unwrap(); + } + } + + // Continue; the debuggee should exit normally now. + let r = Resumption::continue_(d); + let event = r.result(d).unwrap(); + assert!( + matches!(event, Event::Complete), + "expected Complete, got {event:?}" + ); + + eprintln!("OK"); +} + +fn main() {} diff --git a/crates/test-programs/src/bin/debugger_debuggee_loop.wat b/crates/test-programs/src/bin/debugger_debuggee_loop.wat new file mode 100644 index 000000000000..96a90d417396 --- /dev/null +++ b/crates/test-programs/src/bin/debugger_debuggee_loop.wat @@ -0,0 +1,11 @@ +(module + (memory (export "memory") 1) + (func (export "_start") + (block $exit + (loop $loop + (br_if $exit (i32.load8_u (i32.const 0))) + (br $loop) + ) + ) + ) +) diff --git a/crates/test-programs/src/bin/debugger_debuggee_simple.wat b/crates/test-programs/src/bin/debugger_debuggee_simple.wat new file mode 100644 index 000000000000..cd24b4815610 --- /dev/null +++ b/crates/test-programs/src/bin/debugger_debuggee_simple.wat @@ -0,0 +1,9 @@ +(module + (memory (export "memory") 1) + (func (export "_start") + (local $x i32) + (local.set $x (i32.const 1)) + (local.set $x (i32.const 2)) + (local.set $x (i32.const 3)) + ) +) diff --git a/crates/test-programs/src/bin/guest_debug_fib.c b/crates/test-programs/src/bin/guest_debug_fib.c new file mode 100644 index 000000000000..986842eaa62e --- /dev/null +++ b/crates/test-programs/src/bin/guest_debug_fib.c @@ -0,0 +1,19 @@ +//! flags = [] + +#include + +int fib(int n) { + int t, a = 0, b = 1; + for (int i = 0; i < n; i++) { + t = a; + a = b; + b += t; + } + return b; +} + +int main() { + int result = fib(5); + printf("fib(5) = %d\n", result); + return 0; +} diff --git a/crates/test-programs/src/bin/p1_file_truncation_readonly.rs b/crates/test-programs/src/bin/p1_file_truncation_readonly.rs new file mode 100644 index 000000000000..1f9c3da3c872 --- /dev/null +++ b/crates/test-programs/src/bin/p1_file_truncation_readonly.rs @@ -0,0 +1,75 @@ +#![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] + +use std::process; +use test_programs::preview1::open_scratch_directory; + +const FILENAME: &str = "test.txt"; +unsafe fn test_file_has_expected_contents(dir_fd: wasip1::Fd) { + // Open a file for reading + let file_fd = wasip1::path_open(dir_fd, 0, FILENAME, 0, wasip1::RIGHTS_FD_READ, 0, 0) + .expect("opening test.txt for reading"); + + // Read the file's contents + let buffer = &mut [0u8; 100]; + let nread = wasip1::fd_read( + file_fd, + &[wasip1::Iovec { + buf: buffer.as_mut_ptr(), + buf_len: buffer.len(), + }], + ) + .expect("reading file content"); + + const EXPECTED_CONTENTS: &[u8] = b"truncation test file\n"; + // The file should be as created by the test harness, not truncated. + assert_eq!(nread, EXPECTED_CONTENTS.len(), "expected untouched file"); + assert_eq!( + &buffer[..nread], + EXPECTED_CONTENTS, + "expected untouched file contents" + ); + + wasip1::fd_close(file_fd).expect("closing the file"); +} + +unsafe fn test_file_truncation_readonly(dir_fd: wasip1::Fd) { + // Check test preconditions. + test_file_has_expected_contents(dir_fd); + + // Opening the file for truncation should fail. + let err = wasip1::path_open( + dir_fd, + 0, + FILENAME, + wasip1::OFLAGS_TRUNC, + wasip1::RIGHTS_FD_READ, + 0, + 0, + ); + assert!(err.is_err(), "opening file for truncation should fail"); + assert_eq!( + err.err().unwrap(), + wasip1::ERRNO_PERM, + "opening file for truncation should fail with PERM" + ); + + // Check that truncation did not occur. + test_file_has_expected_contents(dir_fd); +} + +fn main() { + // This test program requires a special preopen at the path "readonly", + // which the host enforces as read-only. Unlike other test programs, this + // directory's path not passed in as an argument, because modifications to + // the testing harness would be too invasive. + let dir_fd = match open_scratch_directory("readonly") { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{err}"); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_file_truncation_readonly(dir_fd) } +} diff --git a/crates/test-programs/src/bin/p2_cli_http_headers.rs b/crates/test-programs/src/bin/p2_cli_http_headers.rs index 1d0939264344..a5fd29bffa73 100644 --- a/crates/test-programs/src/bin/p2_cli_http_headers.rs +++ b/crates/test-programs/src/bin/p2_cli_http_headers.rs @@ -1,33 +1,78 @@ -fn main() { - let fields = wasip2::http::types::Fields::new(); +use test_programs::p3::wasi as wasip3; +fn main() { match std::env::args().nth(1).as_deref() { - Some("append") => { + Some("p2-append") => { + let fields = wasip2::http::types::Fields::new(); for i in 0.. { if fields.append(&format!("a{i}"), b"a").is_err() { break; } } } - Some("append-empty") => { + Some("p2-append-empty") => { + let fields = wasip2::http::types::Fields::new(); for i in 0.. { if fields.append(&format!("a{i}"), b"").is_err() { break; } } } - Some("append-same") => loop { - if fields.append("a", b"b").is_err() { - break; + Some("p2-append-same") => { + let fields = wasip2::http::types::Fields::new(); + loop { + if fields.append("a", b"b").is_err() { + break; + } + } + } + Some("p2-append-same-empty") => { + let fields = wasip2::http::types::Fields::new(); + loop { + if fields.append("a", b"").is_err() { + break; + } + } + } + Some("p3-append") => { + let fields = wasip3::http::types::Fields::new(); + for i in 0.. { + if fields.append(&format!("a{i}"), b"a").is_err() { + println!("error received"); + return; + } + } + } + Some("p3-append-empty") => { + let fields = wasip3::http::types::Fields::new(); + for i in 0.. { + if fields.append(&format!("a{i}"), b"").is_err() { + println!("error received"); + return; + } + } + } + Some("p3-append-same") => { + let fields = wasip3::http::types::Fields::new(); + loop { + if fields.append("a", b"b").is_err() { + println!("error received"); + return; + } } - }, - Some("append-same-empty") => loop { - if fields.append("a", b"").is_err() { - break; + } + Some("p3-append-same-empty") => { + let fields = wasip3::http::types::Fields::new(); + loop { + if fields.append("a", b"").is_err() { + println!("error received"); + return; + } } - }, + } other => panic!("unknown test {other:?}"), } + // p2 cases trap before reaching here unreachable!(); } diff --git a/crates/test-programs/src/bin/p2_cli_many_resources.rs b/crates/test-programs/src/bin/p2_cli_many_resources.rs index fd3d995e06d8..c4d207427c16 100644 --- a/crates/test-programs/src/bin/p2_cli_many_resources.rs +++ b/crates/test-programs/src/bin/p2_cli_many_resources.rs @@ -1,7 +1,8 @@ use test_programs::wasi::clocks::monotonic_clock::subscribe_duration; fn main() { - loop { + for _ in 0..1000 { std::mem::forget(subscribe_duration(1_000_000)); } + panic!("should have trapped before now"); } diff --git a/crates/test-programs/src/bin/p2_file_truncation_readonly.rs b/crates/test-programs/src/bin/p2_file_truncation_readonly.rs new file mode 100644 index 000000000000..d51a16edeb42 --- /dev/null +++ b/crates/test-programs/src/bin/p2_file_truncation_readonly.rs @@ -0,0 +1,64 @@ +use test_programs::wasi::filesystem::preopens; +use test_programs::wasi::filesystem::types::{ + Descriptor, DescriptorFlags, ErrorCode, OpenFlags, PathFlags, +}; + +const FILENAME: &str = "test.txt"; +fn test_file_has_expected_contents(dir: &Descriptor) { + // Open a file for reading + let file = dir + .open_at( + PathFlags::empty(), + FILENAME, + OpenFlags::empty(), + DescriptorFlags::READ, + ) + .expect("open test.txt for reading"); + + // Read the file's contents + let stream = file.read_via_stream(0).unwrap(); + let read = stream.blocking_read(100).expect("reading test.txt content"); + drop(stream); + drop(file); + + const EXPECTED_CONTENTS: &[u8] = b"truncation test file\n"; + // The file should not be empty due to truncation + assert_eq!(read, EXPECTED_CONTENTS, "expected untouched file contents"); +} + +fn test_file_truncation_readonly(dir: &Descriptor) { + // Check test preconditions. + test_file_has_expected_contents(dir); + + // Opening the file for truncation should fail. + let err = dir.open_at( + PathFlags::empty(), + FILENAME, + OpenFlags::TRUNCATE, + DescriptorFlags::READ, + ); + assert!(err.is_err(), "opening file for truncation should fail"); + assert_eq!( + err.err().unwrap(), + ErrorCode::NotPermitted, + "opening file for truncation should fail with ErrorCode::NotPermitted" + ); + + // Check that truncation did not occur. + test_file_has_expected_contents(dir); +} + +fn main() { + // This test program requires a special preopen at the path "readonly", + // which the host enforces as read-only. Unlike other test programs, this + // directory's path not passed in as an argument, because modifications to + // the testing harness would be too invasive. + let preopens = preopens::get_directories(); + let (dir, _) = preopens + .iter() + .find(|(_, path)| path == "readonly") + .expect("find preopen named readonly"); + + // Run the test + test_file_truncation_readonly(dir); +} diff --git a/crates/test-programs/src/bin/p2_ip_name_lookup.rs b/crates/test-programs/src/bin/p2_ip_name_lookup.rs index 9e8e6eb531e9..3930d8e6083b 100644 --- a/crates/test-programs/src/bin/p2_ip_name_lookup.rs +++ b/crates/test-programs/src/bin/p2_ip_name_lookup.rs @@ -5,7 +5,13 @@ fn main() { // Valid domains resolve("localhost").unwrap(); - resolve_at_least_one_of(&["example.com", "api.github.com"]); + resolve_at_least_one_of(&[ + "example.com", + "api.github.com", + "docs.wasmtime.dev", + "bytecodealliance.org", + "www.rust-lang.org", + ]); // NB: this is an actual real resolution, so it might time out, might cause // issues, etc. This result is ignored to prevent flaky failures in CI. diff --git a/crates/test-programs/src/bin/tls_sample_application.rs b/crates/test-programs/src/bin/p2_tls_sample_application.rs similarity index 100% rename from crates/test-programs/src/bin/tls_sample_application.rs rename to crates/test-programs/src/bin/p2_tls_sample_application.rs diff --git a/crates/test-programs/src/bin/p3_big_random_buf.rs b/crates/test-programs/src/bin/p3_big_random_buf.rs new file mode 100644 index 000000000000..e59fa9247c58 --- /dev/null +++ b/crates/test-programs/src/bin/p3_big_random_buf.rs @@ -0,0 +1,23 @@ +use test_programs::p3::wasi::random; + +struct Component; + +test_programs::p3::export!(Component); + +impl test_programs::p3::exports::wasi::cli::run::Guest for Component { + async fn run() -> Result<(), ()> { + test_big_random_buf().await; + Ok(()) + } +} + +fn main() { + unreachable!() +} + +async fn test_big_random_buf() { + let buf = random::random::get_random_bytes(1024); + // Chances are pretty good that at least *one* byte will be non-zero in + // any meaningful random function producing 1024 u8 values. + assert!(buf.iter().any(|x| *x != 0), "random_get returned all zeros"); +} diff --git a/crates/test-programs/src/bin/p3_cli_many_tasks.rs b/crates/test-programs/src/bin/p3_cli_many_tasks.rs new file mode 100644 index 000000000000..f7b161ac6bb1 --- /dev/null +++ b/crates/test-programs/src/bin/p3_cli_many_tasks.rs @@ -0,0 +1,32 @@ +use test_programs::p3::wasi as wasip3; + +#[link(wasm_import_module = "wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15")] +unsafe extern "C" { + #[link_name = "[async-lower]wait-for"] + fn wait_for(dur: u64) -> u32; +} + +struct Component; + +test_programs::p3::export!(Component); + +impl test_programs::p3::exports::wasi::cli::run::Guest for Component { + async fn run() -> Result<(), ()> { + // Execute it once to ensure we get type information pulled in. + wasip3::clocks::monotonic_clock::wait_for(0).await; + + // Execute the raw function without Rust bindings to stress invoking it + // many times. + for _ in 0..1000 { + unsafe { + wait_for(1 << 60); + } + } + + panic!("should have trapped before now"); + } +} + +fn main() { + unreachable!(); +} diff --git a/crates/test-programs/src/bin/p3_cli_random_limits.rs b/crates/test-programs/src/bin/p3_cli_random_limits.rs new file mode 100644 index 000000000000..b4212dceee87 --- /dev/null +++ b/crates/test-programs/src/bin/p3_cli_random_limits.rs @@ -0,0 +1,26 @@ +use test_programs::p3::wasi::random; + +struct Component; + +test_programs::p3::export!(Component); + +impl test_programs::p3::exports::wasi::cli::run::Guest for Component { + async fn run() -> Result<(), ()> { + let args = std::env::args().collect::>(); + let args = args.iter().map(|s| s.as_str()).collect::>(); + match &args[1..] { + ["random", n] => { + random::random::get_random_bytes(n.parse().unwrap()); + } + ["insecure", n] => { + random::insecure::get_insecure_random_bytes(n.parse().unwrap()); + } + other => { + panic!("unexpected args: {other:?}"); + } + } + Ok(()) + } +} + +fn main() {} diff --git a/crates/test-programs/src/bin/p3_cli_read_stdin.rs b/crates/test-programs/src/bin/p3_cli_read_stdin.rs new file mode 100644 index 000000000000..4518450f5d33 --- /dev/null +++ b/crates/test-programs/src/bin/p3_cli_read_stdin.rs @@ -0,0 +1,25 @@ +use test_programs::p3::wasi; + +struct Component; + +test_programs::p3::export!(Component); + +impl test_programs::p3::exports::wasi::cli::run::Guest for Component { + async fn run() -> Result<(), ()> { + let (mut stream, result) = wasi::cli::stdin::read_via_stream(); + let (sresult, buf) = stream.read(Vec::with_capacity(100)).await; + assert_eq!(buf, b"hello!".to_vec()); + assert_eq!(sresult, wit_bindgen::StreamResult::Complete(6)); + + let (sresult, buf) = stream.read(Vec::with_capacity(100)).await; + assert!(buf.is_empty()); + assert_eq!(sresult, wit_bindgen::StreamResult::Dropped); + + result.await.unwrap(); + Ok(()) + } +} + +fn main() { + unreachable!(); +} diff --git a/crates/test-programs/src/bin/p3_http_middleware.rs b/crates/test-programs/src/bin/p3_http_middleware.rs index 1381bc9a8334..54fb11cb9b9f 100644 --- a/crates/test-programs/src/bin/p3_http_middleware.rs +++ b/crates/test-programs/src/bin/p3_http_middleware.rs @@ -18,19 +18,19 @@ wit_bindgen::generate!({ path: "../wasi-http/src/p3/wit", world: "wasi:http/middleware", with: { - "wasi:http/handler@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::handler, - "wasi:http/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::types, - "wasi:http/client@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::client, - "wasi:random/random@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::random, - "wasi:random/insecure@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure, - "wasi:random/insecure-seed@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure_seed, - "wasi:cli/stdout@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdout, - "wasi:cli/stderr@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stderr, - "wasi:cli/stdin@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdin, - "wasi:cli/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::types, - "wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::monotonic_clock, - "wasi:clocks/system-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::system_clock, - "wasi:clocks/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::types, + "wasi:http/handler@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::handler, + "wasi:http/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::types, + "wasi:http/client@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::client, + "wasi:random/random@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::random, + "wasi:random/insecure@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure, + "wasi:random/insecure-seed@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure_seed, + "wasi:cli/stdout@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdout, + "wasi:cli/stderr@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stderr, + "wasi:cli/stdin@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdin, + "wasi:cli/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::types, + "wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::monotonic_clock, + "wasi:clocks/system-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::system_clock, + "wasi:clocks/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::types, }, }); diff --git a/crates/test-programs/src/bin/p3_http_middleware_with_chain.rs b/crates/test-programs/src/bin/p3_http_middleware_with_chain.rs index 7b66ac714dc8..cf14e3532b99 100644 --- a/crates/test-programs/src/bin/p3_http_middleware_with_chain.rs +++ b/crates/test-programs/src/bin/p3_http_middleware_with_chain.rs @@ -6,13 +6,13 @@ mod bindings { package local:local; world middleware-with-chain { - include wasi:http/service@0.3.0-rc-2026-02-09; + include wasi:http/service@0.3.0-rc-2026-03-15; import chain-http; } interface chain-http { - use wasi:http/types@0.3.0-rc-2026-02-09.{request, response, error-code}; + use wasi:http/types@0.3.0-rc-2026-03-15.{request, response, error-code}; handle: async func(request: request) -> result; } @@ -20,18 +20,18 @@ interface chain-http { // workaround https://github.com/bytecodealliance/wit-bindgen/issues/1544 // generate_all with: { - "wasi:http/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::types, - "wasi:http/client@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::client, - "wasi:random/random@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::random, - "wasi:random/insecure@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure, - "wasi:random/insecure-seed@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure_seed, - "wasi:cli/stdout@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdout, - "wasi:cli/stderr@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stderr, - "wasi:cli/stdin@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdin, - "wasi:cli/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::types, - "wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::monotonic_clock, - "wasi:clocks/system-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::system_clock, - "wasi:clocks/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::types, + "wasi:http/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::types, + "wasi:http/client@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::client, + "wasi:random/random@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::random, + "wasi:random/insecure@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure, + "wasi:random/insecure-seed@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure_seed, + "wasi:cli/stdout@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdout, + "wasi:cli/stderr@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stderr, + "wasi:cli/stdin@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdin, + "wasi:cli/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::types, + "wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::monotonic_clock, + "wasi:clocks/system-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::system_clock, + "wasi:clocks/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::types, }, }); diff --git a/crates/test-programs/src/bin/p3_readdir.rs b/crates/test-programs/src/bin/p3_readdir.rs index dcb9e759a8d7..44b4ffbd2943 100644 --- a/crates/test-programs/src/bin/p3_readdir.rs +++ b/crates/test-programs/src/bin/p3_readdir.rs @@ -62,9 +62,9 @@ async fn test_readdir(dir: &Descriptor) { let entries = read_dir(dir).await; assert_eq!(entries.len(), 2); assert_eq!(entries[0].name, "file"); - assert_eq!(entries[0].type_, DescriptorType::RegularFile); + assert!(matches!(entries[0].type_, DescriptorType::RegularFile)); assert_eq!(entries[1].name, "nested"); - assert_eq!(entries[1].type_, DescriptorType::Directory); + assert!(matches!(entries[1].type_, DescriptorType::Directory)); assert_empty_dir(&nested).await; drop(nested); diff --git a/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs b/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs index 94548c2191f1..6437c09b01d9 100644 --- a/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs +++ b/crates/test-programs/src/bin/p3_sockets_ip_name_lookup.rs @@ -1,4 +1,3 @@ -use futures::join; use test_programs::p3::wasi::sockets::ip_name_lookup::{ErrorCode, resolve_addresses}; use test_programs::p3::wasi::sockets::types::IpAddress; @@ -14,16 +13,33 @@ async fn resolve_one(name: &str) -> Result { .to_owned()) } +/// Attempts to resolve at least one of `domains`. Allows failure so long as one +/// succeeds. Intended to help make this test less flaky while still also +/// testing live services. +async fn resolve_at_least_one_of(domains: &[&str]) { + for domain in domains { + match resolve_one(domain).await { + Ok(_) => return, + Err(e) => eprintln!("failed to resolve `{domain}`: {e}"), + } + } + + panic!("should have been able to resolve at least one domain"); +} + impl test_programs::p3::exports::wasi::cli::run::Guest for Component { async fn run() -> Result<(), ()> { // Valid domains - let (res0, res1) = join!( - resolve_addresses("localhost".into()), - resolve_addresses("example.com".into()) - ); - if res0.is_err() && res1.is_err() { - panic!("should have been able to resolve at least one domain"); - } + resolve_one("localhost").await.unwrap(); + + resolve_at_least_one_of(&[ + "example.com", + "api.github.com", + "docs.wasmtime.dev", + "bytecodealliance.org", + "www.rust-lang.org", + ]) + .await; // NB: this is an actual real resolution, so it might time out, might cause // issues, etc. This result is ignored to prevent flaky failures in CI. @@ -69,32 +85,32 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { ); // Invalid inputs - assert_eq!( + assert!(matches!( resolve_addresses("".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses(" ".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses("a.b<&>".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses("127.0.0.1:80".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses("[::]:80".into()).await.unwrap_err(), ErrorCode::InvalidArgument - ); - assert_eq!( + )); + assert!(matches!( resolve_addresses("http://example.com/".into()) .await .unwrap_err(), ErrorCode::InvalidArgument - ); + )); Ok(()) } } diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs b/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs index b7b4cf05f098..276064797a2b 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs @@ -46,7 +46,10 @@ fn test_tcp_bind_addrinuse(ip: IpAddress) { let bound_addr = sock1.get_local_address().unwrap(); let sock2 = TcpSocket::create(ip.family()).unwrap(); - assert_eq!(sock2.bind(bound_addr), Err(ErrorCode::AddressInUse)); + assert!(matches!( + sock2.bind(bound_addr), + Err(ErrorCode::AddressInUse) + )); } // The WASI runtime should set SO_REUSEADDR for us @@ -111,7 +114,10 @@ fn test_tcp_bind_addrnotavail(ip: IpAddress) { let sock = TcpSocket::create(ip.family()).unwrap(); - assert_eq!(sock.bind(bind_addr), Err(ErrorCode::AddressNotBindable)); + assert!(matches!( + sock.bind(bind_addr), + Err(ErrorCode::AddressNotBindable) + )); } /// Bind should validate the address family. diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_connect.rs b/crates/test-programs/src/bin/p3_sockets_tcp_connect.rs index cb8d0306e886..960de022d3c4 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_connect.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_connect.rs @@ -15,7 +15,10 @@ async fn test_tcp_connect_unspec(family: IpAddressFamily) { let addr = IpSocketAddress::new(IpAddress::new_unspecified(family), SOME_PORT); let sock = TcpSocket::create(family).unwrap(); - assert_eq!(sock.connect(addr).await, Err(ErrorCode::InvalidArgument)); + assert!(matches!( + sock.connect(addr).await, + Err(ErrorCode::InvalidArgument) + )); } /// 0 is not a valid remote port. @@ -23,7 +26,10 @@ async fn test_tcp_connect_port_0(family: IpAddressFamily) { let addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0); let sock = TcpSocket::create(family).unwrap(); - assert_eq!(sock.connect(addr).await, Err(ErrorCode::InvalidArgument)); + assert!(matches!( + sock.connect(addr).await, + Err(ErrorCode::InvalidArgument) + )); } /// Connect should validate the address family. @@ -36,10 +42,10 @@ async fn test_tcp_connect_wrong_family(family: IpAddressFamily) { let sock = TcpSocket::create(family).unwrap(); - assert_eq!( + assert!(matches!( sock.connect(remote_addr).await, Err(ErrorCode::InvalidArgument) - ); + )); } /// Can only connect to unicast addresses. @@ -52,18 +58,18 @@ async fn test_tcp_connect_non_unicast() { let sock_v4 = TcpSocket::create(IpAddressFamily::Ipv4).unwrap(); let sock_v6 = TcpSocket::create(IpAddressFamily::Ipv6).unwrap(); - assert_eq!( + assert!(matches!( sock_v4.connect(ipv4_broadcast).await, Err(ErrorCode::InvalidArgument) - ); - assert_eq!( + )); + assert!(matches!( sock_v4.connect(ipv4_multicast).await, Err(ErrorCode::InvalidArgument) - ); - assert_eq!( + )); + assert!(matches!( sock_v6.connect(ipv6_multicast).await, Err(ErrorCode::InvalidArgument) - ); + )); } async fn test_tcp_connect_dual_stack() { @@ -83,15 +89,15 @@ async fn test_tcp_connect_dual_stack() { // Tests: // Connecting to an IPv4 address on an IPv6 socket should fail: - assert_eq!( + assert!(matches!( v6_client.connect(v4_listener_addr).await, Err(ErrorCode::InvalidArgument) - ); + )); // Connecting to an IPv4-mapped-IPv6 address on an IPv6 socket should fail: - assert_eq!( + assert!(matches!( v6_client.connect(v6_listener_addr).await, Err(ErrorCode::InvalidArgument) - ); + )); } /// Client sockets can be explicitly bound. diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_listen.rs b/crates/test-programs/src/bin/p3_sockets_tcp_listen.rs index 3cd22f3ad6bb..a2f61f56098c 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_listen.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_listen.rs @@ -25,7 +25,7 @@ fn test_tcp_listen_with_bind(family: IpAddressFamily) { let local_addr = sock.get_local_address().unwrap(); assert!(matches!(sock.listen(), Ok(_))); - assert_eq!(sock.get_local_address(), Ok(local_addr)); + assert_eq!(sock.get_local_address().unwrap(), local_addr); } impl test_programs::p3::exports::wasi::cli::run::Guest for Component { diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_states.rs b/crates/test-programs/src/bin/p3_sockets_tcp_states.rs index 97d3d3b10fa5..c99d112b4d9d 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_states.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_states.rs @@ -16,35 +16,41 @@ fn test_tcp_unbound_state_invariants(family: IpAddressFamily) { // sock.shutdown(ShutdownType::Both), // Err(ErrorCode::InvalidState) //)); - assert_eq!(sock.get_local_address(), Err(ErrorCode::InvalidState)); - assert_eq!(sock.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!( + sock.get_local_address(), + Err(ErrorCode::InvalidState) + )); + assert!(matches!( + sock.get_remote_address(), + Err(ErrorCode::InvalidState) + )); assert!(!sock.get_is_listening()); assert_eq!(sock.get_address_family(), family); - assert_eq!(sock.set_listen_backlog_size(32), Ok(())); + sock.set_listen_backlog_size(32).unwrap(); assert!(sock.get_keep_alive_enabled().is_ok()); - assert_eq!(sock.set_keep_alive_enabled(false), Ok(())); - assert_eq!(sock.get_keep_alive_enabled(), Ok(false)); + sock.set_keep_alive_enabled(false).unwrap(); + assert_eq!(sock.get_keep_alive_enabled().unwrap(), false); assert!(sock.get_keep_alive_idle_time().is_ok()); - assert_eq!(sock.set_keep_alive_idle_time(1), Ok(())); + sock.set_keep_alive_idle_time(1).unwrap(); assert!(sock.get_keep_alive_interval().is_ok()); - assert_eq!(sock.set_keep_alive_interval(1), Ok(())); + sock.set_keep_alive_interval(1).unwrap(); assert!(sock.get_keep_alive_count().is_ok()); - assert_eq!(sock.set_keep_alive_count(1), Ok(())); + sock.set_keep_alive_count(1).unwrap(); assert!(sock.get_hop_limit().is_ok()); - assert_eq!(sock.set_hop_limit(255), Ok(())); - assert_eq!(sock.get_hop_limit(), Ok(255)); + sock.set_hop_limit(255).unwrap(); + assert_eq!(sock.get_hop_limit().unwrap(), 255); assert!(sock.get_receive_buffer_size().is_ok()); - assert_eq!(sock.set_receive_buffer_size(16000), Ok(())); + sock.set_receive_buffer_size(16000).unwrap(); assert!(sock.get_send_buffer_size().is_ok()); - assert_eq!(sock.set_send_buffer_size(16000), Ok(())); + sock.set_send_buffer_size(16000).unwrap(); } fn test_tcp_bound_state_invariants(family: IpAddressFamily) { @@ -52,7 +58,10 @@ fn test_tcp_bound_state_invariants(family: IpAddressFamily) { let sock = TcpSocket::create(family).unwrap(); sock.bind(bind_address).unwrap(); - assert_eq!(sock.bind(bind_address), Err(ErrorCode::InvalidState)); + assert!(matches!( + sock.bind(bind_address), + Err(ErrorCode::InvalidState) + )); // TODO: Test send and receive //assert!(matches!( // sock.shutdown(ShutdownType::Both), @@ -60,34 +69,37 @@ fn test_tcp_bound_state_invariants(family: IpAddressFamily) { //)); assert!(sock.get_local_address().is_ok()); - assert_eq!(sock.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!( + sock.get_remote_address(), + Err(ErrorCode::InvalidState) + )); assert!(!sock.get_is_listening()); assert_eq!(sock.get_address_family(), family); - assert_eq!(sock.set_listen_backlog_size(32), Ok(())); + sock.set_listen_backlog_size(32).unwrap(); assert!(sock.get_keep_alive_enabled().is_ok()); - assert_eq!(sock.set_keep_alive_enabled(false), Ok(())); - assert_eq!(sock.get_keep_alive_enabled(), Ok(false)); + sock.set_keep_alive_enabled(false).unwrap(); + assert_eq!(sock.get_keep_alive_enabled().unwrap(), false); assert!(sock.get_keep_alive_idle_time().is_ok()); - assert_eq!(sock.set_keep_alive_idle_time(1), Ok(())); + sock.set_keep_alive_idle_time(1).unwrap(); assert!(sock.get_keep_alive_interval().is_ok()); - assert_eq!(sock.set_keep_alive_interval(1), Ok(())); + sock.set_keep_alive_interval(1).unwrap(); assert!(sock.get_keep_alive_count().is_ok()); - assert_eq!(sock.set_keep_alive_count(1), Ok(())); + sock.set_keep_alive_count(1).unwrap(); assert!(sock.get_hop_limit().is_ok()); - assert_eq!(sock.set_hop_limit(255), Ok(())); - assert_eq!(sock.get_hop_limit(), Ok(255)); + sock.set_hop_limit(255).unwrap(); + assert_eq!(sock.get_hop_limit().unwrap(), 255); assert!(sock.get_receive_buffer_size().is_ok()); - assert_eq!(sock.set_receive_buffer_size(16000), Ok(())); + sock.set_receive_buffer_size(16000).unwrap(); assert!(sock.get_send_buffer_size().is_ok()); - assert_eq!(sock.set_send_buffer_size(16000), Ok(())); + sock.set_send_buffer_size(16000).unwrap(); } async fn test_tcp_listening_state_invariants(family: IpAddressFamily) { @@ -96,12 +108,15 @@ async fn test_tcp_listening_state_invariants(family: IpAddressFamily) { sock.bind(bind_address).unwrap(); sock.listen().unwrap(); - assert_eq!(sock.bind(bind_address), Err(ErrorCode::InvalidState)); - assert_eq!( + assert!(matches!( + sock.bind(bind_address), + Err(ErrorCode::InvalidState) + )); + assert!(matches!( sock.connect(IpSocketAddress::new(IpAddress::new_loopback(family), 1)) .await, Err(ErrorCode::InvalidState) - ); + )); assert!(matches!(sock.listen(), Err(ErrorCode::InvalidState))); // Skipping: tcp::accept // TODO: Test send and receive @@ -111,7 +126,10 @@ async fn test_tcp_listening_state_invariants(family: IpAddressFamily) { //)); assert!(sock.get_local_address().is_ok()); - assert_eq!(sock.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!( + sock.get_remote_address(), + Err(ErrorCode::InvalidState) + )); assert!(sock.get_is_listening()); assert_eq!(sock.get_address_family(), family); @@ -121,27 +139,27 @@ async fn test_tcp_listening_state_invariants(family: IpAddressFamily) { )); assert!(sock.get_keep_alive_enabled().is_ok()); - assert_eq!(sock.set_keep_alive_enabled(false), Ok(())); - assert_eq!(sock.get_keep_alive_enabled(), Ok(false)); + sock.set_keep_alive_enabled(false).unwrap(); + assert_eq!(sock.get_keep_alive_enabled().unwrap(), false); assert!(sock.get_keep_alive_idle_time().is_ok()); - assert_eq!(sock.set_keep_alive_idle_time(1), Ok(())); + sock.set_keep_alive_idle_time(1).unwrap(); assert!(sock.get_keep_alive_interval().is_ok()); - assert_eq!(sock.set_keep_alive_interval(1), Ok(())); + sock.set_keep_alive_interval(1).unwrap(); assert!(sock.get_keep_alive_count().is_ok()); - assert_eq!(sock.set_keep_alive_count(1), Ok(())); + sock.set_keep_alive_count(1).unwrap(); assert!(sock.get_hop_limit().is_ok()); - assert_eq!(sock.set_hop_limit(255), Ok(())); - assert_eq!(sock.get_hop_limit(), Ok(255)); + sock.set_hop_limit(255).unwrap(); + assert_eq!(sock.get_hop_limit().unwrap(), 255); assert!(sock.get_receive_buffer_size().is_ok()); - assert_eq!(sock.set_receive_buffer_size(16000), Ok(())); + sock.set_receive_buffer_size(16000).unwrap(); assert!(sock.get_send_buffer_size().is_ok()); - assert_eq!(sock.set_send_buffer_size(16000), Ok(())); + sock.set_send_buffer_size(16000).unwrap(); } async fn test_tcp_connected_state_invariants(family: IpAddressFamily) { @@ -160,11 +178,14 @@ async fn test_tcp_connected_state_invariants(family: IpAddressFamily) { } ); - assert_eq!(sock.bind(bind_address), Err(ErrorCode::InvalidState)); - assert_eq!( + assert!(matches!( + sock.bind(bind_address), + Err(ErrorCode::InvalidState) + )); + assert!(matches!( sock.connect(addr_listener).await, Err(ErrorCode::InvalidState) - ); + )); assert!(matches!(sock.listen(), Err(ErrorCode::InvalidState))); // Skipping: tcp::shutdown @@ -174,27 +195,27 @@ async fn test_tcp_connected_state_invariants(family: IpAddressFamily) { assert_eq!(sock.get_address_family(), family); assert!(sock.get_keep_alive_enabled().is_ok()); - assert_eq!(sock.set_keep_alive_enabled(false), Ok(())); - assert_eq!(sock.get_keep_alive_enabled(), Ok(false)); + sock.set_keep_alive_enabled(false).unwrap(); + assert_eq!(sock.get_keep_alive_enabled().unwrap(), false); assert!(sock.get_keep_alive_idle_time().is_ok()); - assert_eq!(sock.set_keep_alive_idle_time(1), Ok(())); + sock.set_keep_alive_idle_time(1).unwrap(); assert!(sock.get_keep_alive_interval().is_ok()); - assert_eq!(sock.set_keep_alive_interval(1), Ok(())); + sock.set_keep_alive_interval(1).unwrap(); assert!(sock.get_keep_alive_count().is_ok()); - assert_eq!(sock.set_keep_alive_count(1), Ok(())); + sock.set_keep_alive_count(1).unwrap(); assert!(sock.get_hop_limit().is_ok()); - assert_eq!(sock.set_hop_limit(255), Ok(())); - assert_eq!(sock.get_hop_limit(), Ok(255)); + sock.set_hop_limit(255).unwrap(); + assert_eq!(sock.get_hop_limit().unwrap(), 255); assert!(sock.get_receive_buffer_size().is_ok()); - assert_eq!(sock.set_receive_buffer_size(16000), Ok(())); + sock.set_receive_buffer_size(16000).unwrap(); assert!(sock.get_send_buffer_size().is_ok()); - assert_eq!(sock.set_send_buffer_size(16000), Ok(())); + sock.set_send_buffer_size(16000).unwrap(); } impl test_programs::p3::exports::wasi::cli::run::Guest for Component { diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs index 9b6b40157572..9bb87bc6ea46 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs @@ -47,7 +47,7 @@ async fn test_tcp_receive_stream_should_be_dropped_by_remote_shutdown(family: Ip let (stream_result, data) = client.receive_stream.read(Vec::with_capacity(1)).await; assert_eq!(data.len(), 0); assert_eq!(stream_result, StreamResult::Dropped); - assert_eq!(client.receive_result.await, Ok(())); + client.receive_result.await.unwrap(); }) .await; } @@ -69,7 +69,7 @@ async fn test_tcp_receive_future_should_resolve_when_stream_dropped(family: IpAd // Dropping the stream should've caused the future to resolve even // though there was still data pending: - assert_eq!(receive_result.await, Ok(())); + receive_result.await.unwrap(); } }).await; } @@ -83,7 +83,7 @@ async fn test_tcp_send_future_should_resolve_when_stream_dropped(family: IpAddre .. } = client; drop(send_stream); - assert_eq!(send_result.await, Ok(())); + send_result.await.unwrap(); }) .await; } @@ -101,7 +101,14 @@ async fn test_tcp_send_drops_stream_when_remote_shutdown(family: IpAddressFamily } } - _ = client.send_result.await; + let result = client.send_result.await; + assert!( + matches!( + result, + Err(ErrorCode::ConnectionBroken | ErrorCode::ConnectionReset) + ), + "unexpected error {result:?}", + ); }) .await; } @@ -123,7 +130,7 @@ async fn test_tcp_receive_once(family: IpAddressFamily) { let (stream_result, data) = reader.read(Vec::with_capacity(10)).await; assert_eq!(data.len(), 0); assert_eq!(stream_result, StreamResult::Dropped); - assert_eq!(future.await, Err(ErrorCode::InvalidState)); + assert!(matches!(future.await, Err(ErrorCode::InvalidState))); } }) .await; @@ -142,7 +149,7 @@ async fn test_tcp_send_once(family: IpAddressFamily) { let (stream_result, rest) = writer.write(DATA.into()).await; assert_eq!(rest.into_vec(), DATA); assert_eq!(stream_result, StreamResult::Dropped); - assert_eq!(future.await, Err(ErrorCode::InvalidState)); + assert!(matches!(future.await, Err(ErrorCode::InvalidState))); } }) .await; @@ -175,23 +182,23 @@ async fn test_tcp_stream_lifetimes(family: IpAddressFamily) { let rest = server_send_stream.write_all(b"ping".into()).await; assert!(rest.is_empty()); drop(server_send_stream); - assert_eq!(server_send_result.await, Ok(())); + server_send_result.await.unwrap(); } { let data = client_receive_stream.collect().await; assert_eq!(data, b"ping"); - assert_eq!(client_receive_result.await, Ok(())); + client_receive_result.await.unwrap(); } { let rest = client_send_stream.write_all(b"pong".into()).await; assert!(rest.is_empty()); drop(client_send_stream); - assert_eq!(client_send_result.await, Ok(())); + client_send_result.await.unwrap(); } { let data = server_receive_stream.collect().await; assert_eq!(data, b"pong"); - assert_eq!(server_receive_result.await, Ok(())); + server_receive_result.await.unwrap(); } }) .await; diff --git a/crates/test-programs/src/bin/p3_sockets_udp_connect.rs b/crates/test-programs/src/bin/p3_sockets_udp_connect.rs index 723068cb1c19..11273c230af5 100644 --- a/crates/test-programs/src/bin/p3_sockets_udp_connect.rs +++ b/crates/test-programs/src/bin/p3_sockets_udp_connect.rs @@ -20,26 +20,35 @@ fn test_udp_connect_disconnect_reconnect(family: IpAddressFamily) { let client = UdpSocket::create(family).unwrap(); - assert_eq!(client.disconnect(), Err(ErrorCode::InvalidState)); - assert_eq!(client.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!(client.disconnect(), Err(ErrorCode::InvalidState))); + assert!(matches!( + client.get_remote_address(), + Err(ErrorCode::InvalidState) + )); - assert_eq!(client.disconnect(), Err(ErrorCode::InvalidState)); - assert_eq!(client.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!(client.disconnect(), Err(ErrorCode::InvalidState))); + assert!(matches!( + client.get_remote_address(), + Err(ErrorCode::InvalidState) + )); _ = client.connect(remote1).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote1)); + assert_eq!(client.get_remote_address().unwrap(), remote1); _ = client.connect(remote1).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote1)); + assert_eq!(client.get_remote_address().unwrap(), remote1); _ = client.connect(remote2).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote2)); + assert_eq!(client.get_remote_address().unwrap(), remote2); _ = client.disconnect().unwrap(); - assert_eq!(client.get_remote_address(), Err(ErrorCode::InvalidState)); + assert!(matches!( + client.get_remote_address(), + Err(ErrorCode::InvalidState) + )); _ = client.connect(remote1).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote1)); + assert_eq!(client.get_remote_address().unwrap(), remote1); } /// `0.0.0.0` / `::` is not a valid remote address in WASI. @@ -175,9 +184,9 @@ async fn test_udp_connect_and_send(family: IpAddressFamily) { client.bind(unspecified_port).unwrap(); client.connect(remote).unwrap(); - assert_eq!(client.get_remote_address(), Ok(remote)); + assert_eq!(client.get_remote_address().unwrap(), remote); - assert_eq!(client.send(b"hello".into(), None).await, Ok(())); + client.send(b"hello".into(), None).await.unwrap(); } impl test_programs::p3::exports::wasi::cli::run::Guest for Component { diff --git a/crates/test-programs/src/bin/p3_sockets_udp_states.rs b/crates/test-programs/src/bin/p3_sockets_udp_states.rs index 370acda8d738..13afa925cb87 100644 --- a/crates/test-programs/src/bin/p3_sockets_udp_states.rs +++ b/crates/test-programs/src/bin/p3_sockets_udp_states.rs @@ -12,13 +12,19 @@ async fn test_udp_unbound_state_invariants(family: IpAddressFamily) { // Skipping: udp::bind - assert_eq!( + assert!(matches!( sock.send(b"test".into(), None).await, Err(ErrorCode::InvalidArgument) - ); - assert_eq!(sock.disconnect(), Err(ErrorCode::InvalidState)); - assert_eq!(sock.get_local_address(), Err(ErrorCode::InvalidState)); - assert_eq!(sock.get_remote_address(), Err(ErrorCode::InvalidState)); + )); + assert!(matches!(sock.disconnect(), Err(ErrorCode::InvalidState))); + assert!(matches!( + sock.get_local_address(), + Err(ErrorCode::InvalidState) + )); + assert!(matches!( + sock.get_remote_address(), + Err(ErrorCode::InvalidState) + )); assert_eq!(sock.get_address_family(), family); assert!(matches!(sock.get_unicast_hop_limit(), Ok(_))); diff --git a/crates/test-programs/src/bin/p3_tls_sample_application.rs b/crates/test-programs/src/bin/p3_tls_sample_application.rs new file mode 100644 index 000000000000..b5063cb86b1d --- /dev/null +++ b/crates/test-programs/src/bin/p3_tls_sample_application.rs @@ -0,0 +1,130 @@ +use anyhow::{Context as _, Result, anyhow}; +use core::future::Future; +use test_programs::p3::wasi::sockets::ip_name_lookup::resolve_addresses; +use test_programs::p3::wasi::sockets::types::{IpAddress, IpSocketAddress, TcpSocket}; +use test_programs::p3::wasi::tls::client::Connector; +use test_programs::p3::wit_stream; + +struct Component; + +test_programs::p3::export!(Component); + +const PORT: u16 = 443; + +async fn test_tls_sample_application(domain: &str, ip: IpAddress) -> Result<()> { + let request = format!( + "GET / HTTP/1.1\r\nHost: {domain}\r\nUser-Agent: wasmtime-wasi-rust\r\nConnection: close\r\n\r\n" + ); + + let sock = TcpSocket::create(ip.family()).unwrap(); + sock.connect(IpSocketAddress::new(ip, PORT)) + .await + .context("tcp connect failed")?; + + let conn = Connector::new(); + + let (sock_rx, sock_rx_fut) = sock.receive(); + let (tls_rx, tls_rx_fut) = conn.receive(sock_rx); + + let (mut data_tx, data_rx) = wit_stream::new(); + let (tls_tx, tls_tx_err_fut) = conn.send(data_rx); + let sock_tx_fut = sock.send(tls_tx); + + Connector::connect(conn, domain.into()) + .await + .context("tls handshake failed")?; + let buf = data_tx.write_all(request.into()).await; + assert!(buf.is_empty()); + + let response = tls_rx.collect().await; + let response = String::from_utf8(response)?; + if !response.contains("HTTP/1.1 200 OK") { + return Err(anyhow!("server did not respond with 200 OK: {response}")); + } + drop(data_tx); + sock_rx_fut.await.context("tcp recv")?; + sock_tx_fut.await.context("tcp send")?; + tls_rx_fut.await.context("tls recv")?; + tls_tx_err_fut.await.context("tls send")?; + + Ok(()) +} + +/// This test sets up a TCP connection using one domain, and then attempts to +/// perform a TLS handshake using another unrelated domain. This should result +/// in a handshake error. +async fn test_tls_invalid_certificate(_domain: &str, ip: IpAddress) -> Result<()> { + const BAD_DOMAIN: &str = "wrongdomain.localhost"; + + let sock = TcpSocket::create(ip.family()).unwrap(); + sock.connect(IpSocketAddress::new(ip, PORT)) + .await + .context("tcp connect failed")?; + + let (_, data_rx) = wit_stream::new(); + let conn = Connector::new(); + + conn.receive(sock.receive().0); + sock.send(conn.send(data_rx).0); + + match Connector::connect(conn, BAD_DOMAIN.into()).await { + Err(e) => { + let debug_string = e.to_debug_string(); + // We're expecting an error regarding certificates in some form or + // another. When we add more TLS backends this naive check will + // likely need to be revisited/expanded: + if debug_string.contains("certificate") || debug_string.contains("HandshakeFailure") { + return Ok(()); + } + Err(anyhow!(debug_string)) + } + Ok(_) => panic!("expecting server name mismatch"), + } +} + +async fn try_live_endpoints<'a, Fut>(test: impl Fn(&'a str, IpAddress) -> Fut) +where + Fut: Future> + 'a, +{ + // since this is testing remote endpoints to ensure system cert store works + // the test uses a couple different endpoints to reduce the number of flakes + const DOMAINS: &[&str] = &[ + "example.com", + "api.github.com", + "docs.wasmtime.dev", + "bytecodealliance.org", + "www.rust-lang.org", + ]; + + for &domain in DOMAINS { + let result = (|| async { + let ip = resolve_addresses(domain.into()) + .await? + .first() + .map(|a| a.to_owned()) + .ok_or_else(|| anyhow!("DNS lookup failed."))?; + test(domain, ip).await + })(); + + match result.await { + Ok(()) => return, + Err(e) => { + eprintln!("test for {domain} failed: {e:#}"); + } + } + } + + panic!("all tests failed"); +} + +impl test_programs::p3::exports::wasi::cli::run::Guest for Component { + async fn run() -> Result<(), ()> { + println!("sample app"); + try_live_endpoints(test_tls_sample_application).await; + println!("invalid cert"); + try_live_endpoints(test_tls_invalid_certificate).await; + Ok(()) + } +} + +fn main() {} diff --git a/crates/test-programs/src/p3/mod.rs b/crates/test-programs/src/p3/mod.rs index d5dea4f18439..9e49bb1c1952 100644 --- a/crates/test-programs/src/p3/mod.rs +++ b/crates/test-programs/src/p3/mod.rs @@ -6,15 +6,19 @@ wit_bindgen::generate!({ package wasmtime:test; world testp3 { - include wasi:cli/imports@0.3.0-rc-2026-02-09; - import wasi:http/types@0.3.0-rc-2026-02-09; - import wasi:http/client@0.3.0-rc-2026-02-09; - import wasi:http/handler@0.3.0-rc-2026-02-09; + include wasi:cli/imports@0.3.0-rc-2026-03-15; + include wasi:tls/imports@0.3.0-draft; + import wasi:http/types@0.3.0-rc-2026-03-15; + import wasi:http/client@0.3.0-rc-2026-03-15; + import wasi:http/handler@0.3.0-rc-2026-03-15; - export wasi:cli/run@0.3.0-rc-2026-02-09; + export wasi:cli/run@0.3.0-rc-2026-03-15; } ", - path: "../wasi-http/src/p3/wit", + path: [ + "../wasi-http/src/p3/wit", + "../wasi-tls/src/p3/wit", + ], world: "wasmtime:test/testp3", default_bindings_module: "test_programs::p3", pub_export_macro: true, @@ -28,19 +32,27 @@ pub mod service { default_bindings_module: "test_programs::p3::service", pub_export_macro: true, with: { - "wasi:http/handler@0.3.0-rc-2026-02-09": crate::p3::wasi::http::handler, - "wasi:http/types@0.3.0-rc-2026-02-09": crate::p3::wasi::http::types, - "wasi:http/client@0.3.0-rc-2026-02-09": crate::p3::wasi::http::client, - "wasi:random/random@0.3.0-rc-2026-02-09": crate::p3::wasi::random::random, - "wasi:random/insecure@0.3.0-rc-2026-02-09": crate::p3::wasi::random::insecure, - "wasi:random/insecure-seed@0.3.0-rc-2026-02-09": crate::p3::wasi::random::insecure_seed, - "wasi:cli/stdout@0.3.0-rc-2026-02-09": crate::p3::wasi::cli::stdout, - "wasi:cli/stderr@0.3.0-rc-2026-02-09": crate::p3::wasi::cli::stderr, - "wasi:cli/stdin@0.3.0-rc-2026-02-09": crate::p3::wasi::cli::stdin, - "wasi:cli/types@0.3.0-rc-2026-02-09": crate::p3::wasi::cli::types, - "wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09": crate::p3::wasi::clocks::monotonic_clock, - "wasi:clocks/system-clock@0.3.0-rc-2026-02-09": crate::p3::wasi::clocks::system_clock, - "wasi:clocks/types@0.3.0-rc-2026-02-09": crate::p3::wasi::clocks::types, + "wasi:http/handler@0.3.0-rc-2026-03-15": crate::p3::wasi::http::handler, + "wasi:http/types@0.3.0-rc-2026-03-15": crate::p3::wasi::http::types, + "wasi:http/client@0.3.0-rc-2026-03-15": crate::p3::wasi::http::client, + "wasi:random/random@0.3.0-rc-2026-03-15": crate::p3::wasi::random::random, + "wasi:random/insecure@0.3.0-rc-2026-03-15": crate::p3::wasi::random::insecure, + "wasi:random/insecure-seed@0.3.0-rc-2026-03-15": crate::p3::wasi::random::insecure_seed, + "wasi:cli/stdout@0.3.0-rc-2026-03-15": crate::p3::wasi::cli::stdout, + "wasi:cli/stderr@0.3.0-rc-2026-03-15": crate::p3::wasi::cli::stderr, + "wasi:cli/stdin@0.3.0-rc-2026-03-15": crate::p3::wasi::cli::stdin, + "wasi:cli/types@0.3.0-rc-2026-03-15": crate::p3::wasi::cli::types, + "wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15": crate::p3::wasi::clocks::monotonic_clock, + "wasi:clocks/system-clock@0.3.0-rc-2026-03-15": crate::p3::wasi::clocks::system_clock, + "wasi:clocks/types@0.3.0-rc-2026-03-15": crate::p3::wasi::clocks::types, }, }); } + +impl std::fmt::Display for wasi::tls::types::Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.to_debug_string()) + } +} + +impl std::error::Error for wasi::tls::types::Error {} diff --git a/crates/test-util/src/component.rs b/crates/test-util/src/component.rs index fb31b594a534..77ca3d64f5bc 100644 --- a/crates/test-util/src/component.rs +++ b/crates/test-util/src/component.rs @@ -27,6 +27,16 @@ pub fn engine() -> Engine { Engine::new(&config()).unwrap() } +pub fn map_config() -> Config { + let mut config = config(); + config.wasm_component_model_map(true); + config +} + +pub fn map_engine() -> Engine { + Engine::new(&map_config()).unwrap() +} + pub fn async_engine() -> Engine { Engine::default() } diff --git a/crates/test-util/src/component_fuzz.rs b/crates/test-util/src/component_fuzz.rs index 162b7075fbfe..98f625670e89 100644 --- a/crates/test-util/src/component_fuzz.rs +++ b/crates/test-util/src/component_fuzz.rs @@ -125,6 +125,7 @@ pub enum Type { Char, String, List(Box), + Map(Box, Box), // Give records the ability to generate a generous amount of fields but // don't let the fuzzer go too wild since `wasmparser`'s validator currently @@ -158,7 +159,7 @@ impl Type { fuel: &mut u32, ) -> arbitrary::Result { *fuel = fuel.saturating_sub(1); - let max = if depth == 0 || *fuel == 0 { 12 } else { 20 }; + let max = if depth == 0 || *fuel == 0 { 12 } else { 21 }; Ok(match u.int_in_range(0..=max)? { 0 => Type::Bool, 1 => Type::S8, @@ -195,11 +196,44 @@ impl Type { *fuel -= amt; Type::Flags(amt) } + 21 => Type::Map( + Box::new(Type::generate_hashable_key(u, fuel)?), + Box::new(Type::generate(u, depth - 1, fuel)?), + ), // ^-- if you add something here update the `depth != 0` case above _ => unreachable!(), }) } + /// Generate a type that can be used as a HashMap key (implements Hash + Eq). + /// This excludes floats and complex types that might contain floats. + fn generate_hashable_key(u: &mut Unstructured<'_>, fuel: &mut u32) -> arbitrary::Result { + *fuel = fuel.saturating_sub(1); + // Only generate types that implement Hash and Eq: + // - No Float32/Float64 (NaN comparison issues) + // - No complex types (Record, Tuple, Variant, etc.) as they might contain floats + // - String is allowed as it implements Hash + Eq + Ok(match u.int_in_range(0..=11)? { + 0 => Type::Bool, + 1 => Type::S8, + 2 => Type::U8, + 3 => Type::S16, + 4 => Type::U16, + 5 => Type::S32, + 6 => Type::U32, + 7 => Type::S64, + 8 => Type::U64, + 9 => Type::Char, + 10 => Type::String, + 11 => { + let amt = u.int_in_range(1..=(*fuel).max(1).min(257))?; + *fuel = fuel.saturating_sub(amt); + Type::Enum(amt) + } + _ => unreachable!(), + }) + } + fn generate_opt( u: &mut Unstructured<'_>, depth: u32, @@ -247,7 +281,7 @@ impl Type { Type::S64 | Type::U64 => Kind::Primitive("i64.store"), Type::Float32 => Kind::Primitive("f32.store"), Type::Float64 => Kind::Primitive("f64.store"), - Type::String | Type::List(_) => Kind::PointerPair, + Type::String | Type::List(_) | Type::Map(_, _) => Kind::PointerPair, Type::Enum(n) if *n <= (1 << 8) => Kind::Primitive("i32.store8"), Type::Enum(n) if *n <= (1 << 16) => Kind::Primitive("i32.store16"), Type::Enum(_) => Kind::Primitive("i32.store"), @@ -374,6 +408,7 @@ impl Type { | Type::Float64 | Type::String | Type::List(_) + | Type::Map(_, _) | Type::Flags(_) | Type::Enum(_) => unreachable!(), @@ -414,7 +449,7 @@ impl Type { Type::U64 | Type::S64 => Kind::Primitive("i64.load"), Type::Float32 => Kind::Primitive("f32.load"), Type::Float64 => Kind::Primitive("f64.load"), - Type::String | Type::List(_) => Kind::PointerPair, + Type::String | Type::List(_) | Type::Map(_, _) => Kind::PointerPair, Type::Enum(n) if *n <= (1 << 8) => Kind::Primitive("i32.load8_u"), Type::Enum(n) if *n <= (1 << 16) => Kind::Primitive("i32.load16_u"), Type::Enum(_) => Kind::Primitive("i32.load"), @@ -551,6 +586,7 @@ impl Type { | Type::Float64 | Type::String | Type::List(_) + | Type::Map(_, _) | Type::Flags(_) | Type::Enum(_) => unreachable!(), @@ -667,7 +703,7 @@ impl Type { Type::S64 | Type::U64 => vec.push(CoreType::I64), Type::Float32 => vec.push(CoreType::F32), Type::Float64 => vec.push(CoreType::F64), - Type::String | Type::List(_) => { + Type::String | Type::List(_) | Type::Map(_, _) => { vec.push(CoreType::I32); vec.push(CoreType::I32); } @@ -706,7 +742,7 @@ impl Type { alignment: 8, }, - Type::String | Type::List(_) => SizeAndAlignment { + Type::String | Type::List(_) | Type::Map(_, _) => SizeAndAlignment { size: 8, alignment: 4, }, @@ -894,7 +930,7 @@ fn make_import_and_export( wat.push_str("))\n"); // If the export is async, generate `task.return` as an import as well - // which is necesary to communicate the results. + // which is necessary to communicate the results. if export_results_loc.is_none() { wat.push_str(&format!("(type $task.return (func")); push_params(&mut wat, &result_lowered, MAX_FLAT_PARAMS); @@ -1161,6 +1197,11 @@ pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStre let ty = rust_type(ty, name_counter, declarations); quote!(Vec<#ty>) } + Type::Map(key_ty, value_ty) => { + let key_ty = rust_type(key_ty, name_counter, declarations); + let value_ty = rust_type(value_ty, name_counter, declarations); + quote!(std::collections::HashMap<#key_ty, #value_ty>) + } Type::Record(types) => { let fields = types .iter() @@ -1241,7 +1282,7 @@ pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStre }; declarations.extend(quote! { - #[derive(ComponentType, Lift, Lower, PartialEq, Debug, Copy, Clone, Arbitrary)] + #[derive(ComponentType, Lift, Lower, PartialEq, Eq, Hash, Debug, Copy, Clone, Arbitrary)] #[component(enum)] #[repr(#repr)] enum #name { @@ -1330,6 +1371,7 @@ impl<'a> TypesBuilder<'a> { // Otherwise emit a reference to the type and remember to generate // the corresponding type alias later. Type::List(_) + | Type::Map(_, _) | Type::Record(_) | Type::Tuple(_) | Type::Variant(_) @@ -1367,6 +1409,13 @@ impl<'a> TypesBuilder<'a> { self.write_ref(ty, &mut decl); decl.push_str(")"); } + Type::Map(key_ty, value_ty) => { + decl.push_str("(map "); + self.write_ref(key_ty, &mut decl); + decl.push_str(" "); + self.write_ref(value_ty, &mut decl); + decl.push_str(")"); + } Type::Record(types) => { decl.push_str("(record"); for (index, ty) in types.iter().enumerate() { diff --git a/crates/test-util/src/wasmtime_wast.rs b/crates/test-util/src/wasmtime_wast.rs index d9a22a8684e8..c64b193224f4 100644 --- a/crates/test-util/src/wasmtime_wast.rs +++ b/crates/test-util/src/wasmtime_wast.rs @@ -25,6 +25,7 @@ pub fn apply_wast_config(config: &mut Config, wast_config: &wast::WastConfig) { /// Helper method to apply `test_config` to `config`. pub fn apply_test_config(config: &mut Config, test_config: &wast::TestConfig) { let wast::TestConfig { + bulk_memory, memory64, custom_page_sizes, multi_memory, @@ -43,6 +44,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wast::TestConfig) { component_model_threading, component_model_error_context, component_model_gc, + component_model_map, component_model_fixed_length_lists, nan_canonicalization, simd, @@ -58,6 +60,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wast::TestConfig) { // Note that all of these proposals/features are currently default-off to // ensure that we annotate all tests accurately with what features they // need, even in the future when features are stabilized. + let bulk_memory = bulk_memory.unwrap_or(false); let memory64 = memory64.unwrap_or(false); let custom_page_sizes = custom_page_sizes.unwrap_or(false); let multi_memory = multi_memory.unwrap_or(false); @@ -73,6 +76,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wast::TestConfig) { let component_model_threading = component_model_threading.unwrap_or(false); let component_model_error_context = component_model_error_context.unwrap_or(false); let component_model_gc = component_model_gc.unwrap_or(false); + let component_model_map = component_model_map.unwrap_or(false); let component_model_fixed_length_lists = component_model_fixed_length_lists.unwrap_or(false); let nan_canonicalization = nan_canonicalization.unwrap_or(false); let relaxed_simd = relaxed_simd.unwrap_or(false); @@ -94,6 +98,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wast::TestConfig) { let _custom_descriptors = custom_descriptors.unwrap_or(false); config + .wasm_bulk_memory(bulk_memory) .wasm_multi_memory(multi_memory) .wasm_threads(threads) .wasm_shared_everything_threads(shared_everything_threads) @@ -113,6 +118,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wast::TestConfig) { .wasm_component_model_threading(component_model_threading) .wasm_component_model_error_context(component_model_error_context) .wasm_component_model_gc(component_model_gc) + .wasm_component_model_map(component_model_map) .wasm_component_model_fixed_length_lists(component_model_fixed_length_lists) .wasm_exceptions(exceptions) .wasm_stack_switching(stack_switching) diff --git a/crates/test-util/src/wast.rs b/crates/test-util/src/wast.rs index 4fdda8111b61..dc570ce43507 100644 --- a/crates/test-util/src/wast.rs +++ b/crates/test-util/src/wast.rs @@ -112,6 +112,7 @@ fn add_tests(tests: &mut Vec, path: &Path, config: &FindConfig) -> Res fn spec_test_config(test: &Path) -> TestConfig { let mut ret = TestConfig::default(); ret.spec_test = Some(true); + ret.bulk_memory = Some(true); match spec_proposal_from_path(test) { Some("wide-arithmetic") => { ret.wide_arithmetic = Some(true); @@ -248,6 +249,7 @@ impl fmt::Debug for WastTest { macro_rules! foreach_config_option { ($m:ident) => { $m! { + bulk_memory memory64 custom_page_sizes multi_memory @@ -268,6 +270,7 @@ macro_rules! foreach_config_option { component_model_threading component_model_error_context component_model_gc + component_model_map component_model_fixed_length_lists simd gc_types @@ -454,9 +457,11 @@ impl WastTest { // Some tests are known to fail with the pooling allocator if config.pooling { + // allocates too much memory for the pooling configuration here + if self.config.hogs_memory() { + return true; + } let unsupported = [ - // allocates too much memory for the pooling configuration here - "misc_testsuite/memory64/more-than-4gb.wast", // shared memories + pooling allocator aren't supported yet "misc_testsuite/memory-combos.wast", "misc_testsuite/threads/atomics-end-of-memory.wast", @@ -473,6 +478,7 @@ impl WastTest { "spec_testsuite/proposals/threads/atomic.wast", "spec_testsuite/proposals/threads/exports.wast", "spec_testsuite/proposals/threads/memory.wast", + "misc_testsuite/memory64/threads.wast", ]; if unsupported.iter().any(|part| self.path.ends_with(part)) { @@ -501,20 +507,6 @@ impl WastTest { "misc_testsuite/no-mixup-stack-maps.wast", "misc_testsuite/no-panic.wast", "misc_testsuite/simple_ref_is_null.wast", - "misc_testsuite/table_grow_with_funcref.wast", - "spec_testsuite/br_table.wast", - "spec_testsuite/global.wast", - "spec_testsuite/ref_func.wast", - "spec_testsuite/ref_is_null.wast", - "spec_testsuite/ref_null.wast", - "spec_testsuite/select.wast", - "spec_testsuite/table_fill.wast", - "spec_testsuite/table_get.wast", - "spec_testsuite/table_grow.wast", - "spec_testsuite/table_set.wast", - "spec_testsuite/table_size.wast", - "spec_testsuite/elem.wast", - "spec_testsuite/linking.wast", ]; if unsupported.iter().any(|part| self.path.ends_with(part)) { diff --git a/crates/unwinder/src/exception_table.rs b/crates/unwinder/src/exception_table.rs index fa87b94b947f..85ba28732ef5 100644 --- a/crates/unwinder/src/exception_table.rs +++ b/crates/unwinder/src/exception_table.rs @@ -10,7 +10,7 @@ //! and used without post-processing; this enables efficient //! module-loading in runtimes such as Wasmtime. -use object::{Bytes, LittleEndian, U32Bytes}; +use object::{Bytes, LittleEndian, U32}; #[cfg(feature = "cranelift")] use alloc::vec; @@ -108,12 +108,12 @@ use wasmtime_environ::prelude::*; #[cfg(feature = "cranelift")] #[derive(Clone, Debug, Default)] pub struct ExceptionTableBuilder { - pub callsites: Vec>, - pub frame_offsets: Vec>, - pub ranges: Vec>, - pub tags: Vec>, - pub contexts: Vec>, - pub handlers: Vec>, + pub callsites: Vec>, + pub frame_offsets: Vec>, + pub ranges: Vec>, + pub tags: Vec>, + pub contexts: Vec>, + pub handlers: Vec>, last_start_offset: CodeOffset, } @@ -145,17 +145,17 @@ impl ExceptionTableBuilder { for handler in call_site.exception_handlers { match handler { FinalizedMachExceptionHandler::Tag(tag, offset) => { - self.tags.push(U32Bytes::new(LittleEndian, tag.as_u32())); - self.contexts.push(U32Bytes::new(LittleEndian, context)); - self.handlers.push(U32Bytes::new( + self.tags.push(U32::new(LittleEndian, tag.as_u32())); + self.contexts.push(U32::new(LittleEndian, context)); + self.handlers.push(U32::new( LittleEndian, offset.checked_add(start_offset).unwrap(), )); } FinalizedMachExceptionHandler::Default(offset) => { - self.tags.push(U32Bytes::new(LittleEndian, u32::MAX)); - self.contexts.push(U32Bytes::new(LittleEndian, context)); - self.handlers.push(U32Bytes::new( + self.tags.push(U32::new(LittleEndian, u32::MAX)); + self.contexts.push(U32::new(LittleEndian, context)); + self.handlers.push(U32::new( LittleEndian, offset.checked_add(start_offset).unwrap(), )); @@ -176,12 +176,12 @@ impl ExceptionTableBuilder { // Omit empty callsites for compactness. if end_idx > start_idx { - self.ranges.push(U32Bytes::new(LittleEndian, end_idx)); - self.frame_offsets.push(U32Bytes::new( + self.ranges.push(U32::new(LittleEndian, end_idx)); + self.frame_offsets.push(U32::new( LittleEndian, call_site.frame_offset.unwrap_or(u32::MAX), )); - self.callsites.push(U32Bytes::new(LittleEndian, ret_addr)); + self.callsites.push(U32::new(LittleEndian, ret_addr)); } } @@ -224,12 +224,12 @@ impl ExceptionTableBuilder { /// [`ExceptionTableBuilder::serialize`]. #[derive(Clone, Debug)] pub struct ExceptionTable<'a> { - callsites: &'a [U32Bytes], - ranges: &'a [U32Bytes], - frame_offsets: &'a [U32Bytes], - tags: &'a [U32Bytes], - contexts: &'a [U32Bytes], - handlers: &'a [U32Bytes], + callsites: &'a [U32], + ranges: &'a [U32], + frame_offsets: &'a [U32], + tags: &'a [U32], + contexts: &'a [U32], + handlers: &'a [U32], } /// Wasmtime exception table item, after parsing. @@ -256,30 +256,27 @@ impl<'a> ExceptionTable<'a> { pub fn parse(data: &'a [u8]) -> Result> { let mut data = Bytes(data); let callsite_count = data - .read::>() + .read::>() .map_err(|_| format_err!("Unable to read callsite count prefix"))?; let callsite_count = usize::try_from(callsite_count.get(LittleEndian))?; let handler_count = data - .read::>() + .read::>() .map_err(|_| format_err!("Unable to read handler count prefix"))?; let handler_count = usize::try_from(handler_count.get(LittleEndian))?; let (callsites, data) = - object::slice_from_bytes::>(data.0, callsite_count) + object::slice_from_bytes::>(data.0, callsite_count) .map_err(|_| format_err!("Unable to read callsites slice"))?; let (frame_offsets, data) = - object::slice_from_bytes::>(data, callsite_count) + object::slice_from_bytes::>(data, callsite_count) .map_err(|_| format_err!("Unable to read frame_offsets slice"))?; - let (ranges, data) = - object::slice_from_bytes::>(data, callsite_count) - .map_err(|_| format_err!("Unable to read ranges slice"))?; - let (tags, data) = object::slice_from_bytes::>(data, handler_count) + let (ranges, data) = object::slice_from_bytes::>(data, callsite_count) + .map_err(|_| format_err!("Unable to read ranges slice"))?; + let (tags, data) = object::slice_from_bytes::>(data, handler_count) .map_err(|_| format_err!("Unable to read tags slice"))?; - let (contexts, data) = - object::slice_from_bytes::>(data, handler_count) - .map_err(|_| format_err!("Unable to read contexts slice"))?; - let (handlers, data) = - object::slice_from_bytes::>(data, handler_count) - .map_err(|_| format_err!("Unable to read handlers slice"))?; + let (contexts, data) = object::slice_from_bytes::>(data, handler_count) + .map_err(|_| format_err!("Unable to read contexts slice"))?; + let (handlers, data) = object::slice_from_bytes::>(data, handler_count) + .map_err(|_| format_err!("Unable to read handlers slice"))?; if !data.is_empty() { bail!("Unexpected data at end of serialized exception table"); @@ -362,9 +359,9 @@ impl<'a> ExceptionTable<'a> { &self, idx: usize, ) -> ( - &[U32Bytes], - &[U32Bytes], - &[U32Bytes], + &[U32], + &[U32], + &[U32], ) { let end_idx = self.ranges[idx].get(LittleEndian); let start_idx = if idx > 0 { diff --git a/crates/wasi-common/tests/all/async_.rs b/crates/wasi-common/tests/all/async_.rs index 794ca8a090a7..10dc1db6b72d 100644 --- a/crates/wasi-common/tests/all/async_.rs +++ b/crates/wasi-common/tests/all/async_.rs @@ -291,3 +291,9 @@ async fn p1_path_open_lots() { async fn p1_sleep_quickly_but_lots() { run(P1_SLEEP_QUICKLY_BUT_LOTS, true).await.unwrap() } +#[test] +fn p1_file_truncation_readonly() { + println!( + "blank placeholder test to satisfy assert_test_exists. This test exercises wasmtime-wasi functionality is not relevant to wasi-common" + ); +} diff --git a/crates/wasi-common/tests/all/sync.rs b/crates/wasi-common/tests/all/sync.rs index 53773e7e2d72..d230daf3252e 100644 --- a/crates/wasi-common/tests/all/sync.rs +++ b/crates/wasi-common/tests/all/sync.rs @@ -285,3 +285,9 @@ fn p1_path_open_lots() { fn p1_sleep_quickly_but_lots() { run(P1_SLEEP_QUICKLY_BUT_LOTS, true).unwrap() } +#[test] +fn p1_file_truncation_readonly() { + println!( + "blank placeholder test to satisfy assert_test_exists. This test exercises wasmtime-wasi functionality is not relevant to wasi-common" + ); +} diff --git a/crates/wasi-http/Cargo.toml b/crates/wasi-http/Cargo.toml index 042e04a2f3a9..7eb31ae2de4e 100644 --- a/crates/wasi-http/Cargo.toml +++ b/crates/wasi-http/Cargo.toml @@ -15,8 +15,9 @@ workspace = true all-features = true [features] -default = ["default-send-request"] +default = ["default-send-request", "p2"] default-send-request = ["dep:tokio-rustls", "dep:rustls", "dep:webpki-roots"] +p2 = ["wasmtime-wasi/p2"] p3 = ["wasmtime-wasi/p3", "dep:tokio-util"] component-model-async = ["futures/alloc", "wasmtime/component-model-async"] diff --git a/crates/wasi-http/src/ctx.rs b/crates/wasi-http/src/ctx.rs new file mode 100644 index 000000000000..0ae1a9678e22 --- /dev/null +++ b/crates/wasi-http/src/ctx.rs @@ -0,0 +1,41 @@ +/// Default maximum size for the contents of a fields resource. +/// +/// Typically, HTTP proxies limit headers to 8k. This number is higher than that +/// because it not only includes the wire-size of headers but it additionally +/// includes factors for the in-memory representation of `HeaderMap`. This is in +/// theory high enough that no one runs into it but low enough such that a +/// completely full `HeaderMap` doesn't break the bank in terms of memory +/// consumption. +const DEFAULT_FIELD_SIZE_LIMIT: usize = 128 * 1024; + +/// Capture the state necessary for use in the wasi-http API implementation. +#[derive(Debug, Clone)] +pub struct WasiHttpCtx { + pub(crate) field_size_limit: usize, +} + +impl WasiHttpCtx { + /// Create a new context. + pub fn new() -> Self { + Self { + field_size_limit: DEFAULT_FIELD_SIZE_LIMIT, + } + } + + /// Set the maximum size for any fields resources created by this context. + /// + /// The limit specified here is roughly a byte limit for the size of the + /// in-memory representation of headers. This means that the limit needs to + /// be larger than the literal representation of headers on the wire to + /// account for in-memory Rust-side data structures representing the header + /// names/values/etc. + pub fn set_field_size_limit(&mut self, limit: usize) { + self.field_size_limit = limit; + } +} + +impl Default for WasiHttpCtx { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/wasi-http/src/field_map.rs b/crates/wasi-http/src/field_map.rs new file mode 100644 index 000000000000..556e87b6d496 --- /dev/null +++ b/crates/wasi-http/src/field_map.rs @@ -0,0 +1,357 @@ +use http::header::Entry; +use http::{HeaderMap, HeaderName, HeaderValue}; +use std::fmt; +use std::ops::Deref; +use std::sync::Arc; +use wasmtime::Result; + +/// A wrapper around [`http::HeaderMap`] which implements `wasi:http` semantics. +/// +/// The main differences from [`http::HeaderMap`] and this type are: +/// +/// * A slimmed down mutability API to just what `wasi:http` needs. +/// * `FieldMap` is cheaply clone-able with the internal `HeaderMap` being +/// behind an `Arc`. +/// * `FieldMap` is either immutable or mutable. Mutations on immutable values +/// are rejected with an error. Mutations on mutable values will never panic +/// unlike `HeaderMap` and additionally require a limit to be set on the size +/// of the map. +/// +/// Overall the intention is that this is a slim wrapper around +/// [`http::HeaderMap`] with slightly different ownership, panic, and error +/// semantics. +#[derive(Debug, Clone)] +pub struct FieldMap { + map: Arc, + limit: Limit, + size: usize, +} + +#[derive(Debug, Clone)] +enum Limit { + Mutable(usize), + Immutable, +} + +impl Default for FieldMap { + fn default() -> Self { + Self::new_immutable(HeaderMap::default()) + } +} + +impl FieldMap { + /// Creates a new immutable `FieldMap` from the provided + /// [`http::HeaderMap`]. + /// + /// The returned value cannot be mutated and attempting to mutate it will + /// return an error. + pub fn new_immutable(map: HeaderMap) -> Self { + let size = Self::content_size(&map); + Self { + map: Arc::new(map), + size, + limit: Limit::Immutable, + } + } + + /// Creates a new, empty, mutable `FieldMap`. + /// + /// Mutations are allowed on the returned value and up to `limit` bytes of + /// memory (roughly) may be consumed by this map. + pub fn new_mutable(limit: usize) -> Self { + Self { + map: Arc::new(HeaderMap::new()), + size: 0, + limit: Limit::Mutable(limit), + } + } + + /// Calculate the content size of a `HeaderMap`. This is a sum of the size + /// of all of the keys and all of the values. + pub(crate) fn content_size(map: &HeaderMap) -> usize { + let mut sum = 0; + for key in map.keys() { + sum += header_name_size(key); + } + for value in map.values() { + sum += header_value_size(value); + } + sum + } + + /// Sets the header `key` to the `values` list provided. + /// + /// Removes the previous value, if any. + /// + /// If `values` is empty then this removes the header `key`. + // + // FIXME(WebAssembly/WASI#900): is this the right behavior? + pub fn set(&mut self, key: HeaderName, values: Vec) -> Result<(), FieldMapError> { + let (map, limit, size) = self.mutable()?; + let key_size = header_name_size(&key); + let values_size = values.iter().map(header_value_size).sum::(); + let mut values = values.into_iter(); + let mut entry = match map.try_entry(key)? { + Entry::Vacant(e) => match values.next() { + Some(v) => { + update_size(size, limit, *size + values_size + key_size)?; + e.try_insert_entry(v)? + } + None => return Ok(()), + }, + Entry::Occupied(mut e) => { + let prev_values_size = e.iter().map(header_value_size).sum::(); + let _prev = match values.next() { + Some(v) => { + update_size(size, limit, *size - prev_values_size + values_size)?; + e.insert(v); + } + None => { + update_size(size, limit, *size - prev_values_size - key_size)?; + e.remove(); + return Ok(()); + } + }; + e + } + }; + for value in values { + entry.append(value); + } + Ok(()) + } + + /// Remove all values associated with a key in a map. + /// + /// Returns an empty list if the key is not already present within the map. + pub fn remove_all(&mut self, key: HeaderName) -> Result, FieldMapError> { + let (map, _limit, size) = self.mutable()?; + match map.try_entry(key)? { + Entry::Vacant { .. } => Ok(Vec::new()), + Entry::Occupied(e) => { + let (name, value_drain) = e.remove_entry_mult(); + let mut removed = header_name_size(&name); + let values = value_drain.collect::>(); + for v in values.iter() { + removed += header_value_size(v); + } + *size -= removed; + Ok(values) + } + } + } + + fn mutable(&mut self) -> Result<(&mut HeaderMap, usize, &mut usize), FieldMapError> { + match self.limit { + Limit::Immutable => Err(FieldMapError::Immutable), + Limit::Mutable(limit) => Ok((Arc::make_mut(&mut self.map), limit, &mut self.size)), + } + } + + /// Add a value associated with a key to the map. + /// + /// If `key` is already present within the map then `value` is appended to + /// the list of values it already has. + pub fn append(&mut self, key: HeaderName, value: HeaderValue) -> Result { + let (map, limit, size) = self.mutable()?; + let key_size = header_name_size(&key); + let val_size = header_value_size(&value); + let new_size = if !map.contains_key(&key) { + *size + key_size + val_size + } else { + *size + val_size + }; + update_size(size, limit, new_size)?; + let already_present = map.try_append(key, value)?; + self.size = new_size; + Ok(already_present) + } + + /// Flags this map as mutable, allowing mutations which can allocate as much + /// as `limit` memory, in bytes, for this entire map (roughly). + pub fn set_mutable(&mut self, limit: usize) { + self.limit = Limit::Mutable(limit); + } + + /// Flags this map as immutable, forbidding all further mutations. + pub fn set_immutable(&mut self) { + self.limit = Limit::Immutable; + } +} + +/// Returns the size, in accounting cost, to consider for `name`. +/// +/// This includes both the byte length of the `name` itself as well as the size +/// of the data structure itself as it'll reside within a `HeaderMap`. +fn header_name_size(name: &HeaderName) -> usize { + name.as_str().len() + size_of::() +} + +/// Same as `header_name_size`, but for values. +/// +/// This notably includes the size of `HeaderValue` itself to ensure that all +/// headers have a nonzero size as otherwise this would never limit addition of +/// an empty header value. +fn header_value_size(value: &HeaderValue) -> usize { + value.len() + size_of::() +} + +fn update_size(size: &mut usize, limit: usize, new: usize) -> Result<(), FieldMapError> { + if new > limit { + Err(FieldMapError::TotalSizeTooBig) + } else { + *size = new; + Ok(()) + } +} + +// Note that `DerefMut` is specifically omitted here to force all mutations +// through the `FieldMap` wrapper. +impl Deref for FieldMap { + type Target = HeaderMap; + + fn deref(&self) -> &HeaderMap { + &self.map + } +} + +impl From for HeaderMap { + fn from(map: FieldMap) -> Self { + Arc::unwrap_or_clone(map.map) + } +} + +/// Errors that can happen when mutating/operating on a [`FieldMap`]. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum FieldMapError { + /// A mutation was attempted when the map is not mutable. + Immutable, + /// The map has too many fields and is not allowed to add more. + /// + /// Note that this is currently a limitation inherited from + /// [`http::HeaderMap`]. + TooManyFields, + /// The map's total size, of keys and values, is too large. + TotalSizeTooBig, + /// An invalid header name was attempted to be added. + InvalidHeaderName, +} + +impl fmt::Display for FieldMapError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + FieldMapError::Immutable => "cannot mutate an immutable field map", + FieldMapError::TooManyFields => "too many fields in the field map", + FieldMapError::TotalSizeTooBig => "total size of fields exceeds limit", + FieldMapError::InvalidHeaderName => "invalid header name", + }; + f.write_str(s) + } +} + +impl std::error::Error for FieldMapError {} + +impl From for FieldMapError { + fn from(_: http::header::MaxSizeReached) -> Self { + Self::TooManyFields + } +} + +impl From for FieldMapError { + fn from(_: http::header::InvalidHeaderName) -> Self { + Self::InvalidHeaderName + } +} + +#[cfg(test)] +mod tests { + use super::{FieldMap, FieldMapError}; + + #[test] + fn test_immutable() { + let mut map = FieldMap::default(); + assert_eq!( + map.set("foo".parse().unwrap(), vec!["bar".parse().unwrap()]), + Err(FieldMapError::Immutable) + ); + assert_eq!( + map.append("foo".parse().unwrap(), "bar".parse().unwrap()), + Err(FieldMapError::Immutable) + ); + assert_eq!( + map.remove_all("foo".parse().unwrap()), + Err(FieldMapError::Immutable) + ); + } + + #[test] + fn test_limits() { + let mut map = FieldMap::new_mutable(100); + loop { + match map.append("foo".parse().unwrap(), "bar".parse().unwrap()) { + Ok(_) => {} + Err(FieldMapError::TotalSizeTooBig) => break, + Err(e) => panic!("unexpected error: {e}"), + } + } + + map = FieldMap::new_mutable(100); + for i in 0.. { + match map.set( + "foo".parse().unwrap(), + (0..i).map(|j| format!("bar{j}").parse().unwrap()).collect(), + ) { + Ok(_) => {} + Err(FieldMapError::TotalSizeTooBig) => break, + Err(e) => panic!("unexpected error: {e}"), + } + } + + map = FieldMap::new_mutable(100); + for i in 0.. { + match map.set( + format!("foo{i}").parse().unwrap(), + vec!["bar".parse().unwrap()], + ) { + Ok(_) => {} + Err(FieldMapError::TotalSizeTooBig) => break, + Err(e) => panic!("unexpected error: {e}"), + } + } + } + + #[test] + fn test_size() -> Result<(), FieldMapError> { + let mut map = FieldMap::new_mutable(2000); + let name: http::HeaderName = "foo".parse().unwrap(); + + map.append(name.clone(), "bar".parse().unwrap())?; + assert!(map.size > 0); + map.remove_all(name.clone())?; + assert_eq!(map.size, 0); + + map.set(name.clone(), vec!["bar".parse().unwrap()])?; + assert!(map.size > 0); + map.remove_all(name.clone())?; + assert_eq!(map.size, 0); + + map.set(name.clone(), vec![])?; + assert_eq!(map.size, 0); + map.set(name.clone(), vec!["bar".parse().unwrap()])?; + assert!(map.size > 0); + map.set(name.clone(), vec![])?; + assert_eq!(map.size, 0); + + map.set(name.clone(), vec!["bar".parse().unwrap()])?; + assert!(map.size > 0); + map.set( + name.clone(), + vec!["bar".parse().unwrap(), "baz".parse().unwrap()], + )?; + assert!(map.size > 0); + map.remove_all(name.clone())?; + assert_eq!(map.size, 0); + + Ok(()) + } +} diff --git a/crates/wasi-http/src/handler.rs b/crates/wasi-http/src/handler.rs index c8496710d199..d198814428c7 100644 --- a/crates/wasi-http/src/handler.rs +++ b/crates/wasi-http/src/handler.rs @@ -24,6 +24,7 @@ use wasmtime::{Result, Store, StoreContextMut, format_err}; /// Alternative p2 bindings generated with `exports: { default: async | store }` /// so we can use `TypedFunc::call_concurrent` with both p2 and p3 instances. +#[cfg(feature = "p2")] pub mod p2 { #[expect(missing_docs, reason = "bindgen-generated code")] pub mod bindings { @@ -35,7 +36,7 @@ pub mod p2 { require_store_data_send: true, with: { // http is in this crate - "wasi:http": crate::bindings::http, + "wasi:http": crate::p2::bindings::http, // Upstream package dependencies "wasi:io": wasmtime_wasi::p2::bindings::io, } @@ -49,6 +50,7 @@ pub mod p2 { /// `wasi:http/handler@0.3.x` pre-instance. pub enum ProxyPre { /// A `wasi:http/incoming-handler@0.2.x` pre-instance. + #[cfg(feature = "p2")] P2(p2::bindings::ProxyPre), /// A `wasi:http/handler@0.3.x` pre-instance. #[cfg(feature = "p3")] @@ -61,6 +63,7 @@ impl ProxyPre { T: Send, { Ok(match self { + #[cfg(feature = "p2")] Self::P2(pre) => Proxy::P2(pre.instantiate_async(store).await?), #[cfg(feature = "p3")] Self::P3(pre) => Proxy::P3(pre.instantiate_async(store).await?), @@ -72,6 +75,7 @@ impl ProxyPre { /// `wasi:http/handler@0.3.x` instance. pub enum Proxy { /// A `wasi:http/incoming-handler@0.2.x` instance. + #[cfg(feature = "p2")] P2(p2::bindings::Proxy), /// A `wasi:http/handler@0.3.x` instance. #[cfg(feature = "p3")] @@ -370,10 +374,10 @@ where // new task from the queue. // // Note the the order of operations here is important. By - // polling `next_future` first, we'll disover any tasks that + // polling `next_future` first, we'll discover any tasks that // may have timed out, at which point we'll stop accepting // new tasks altogether (see below for details). This is - // especially imporant in the case where the task was + // especially important in the case where the task was // blocked on a synchronous call to a host function which // has exclusive access to the `Store`; once that call // finishes, the first think we need to do is time out the diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs index 358682ddc91b..b61c11c6466c 100644 --- a/crates/wasi-http/src/lib.rs +++ b/crates/wasi-http/src/lib.rs @@ -1,402 +1,52 @@ -//! # Wasmtime's WASI HTTP Implementation +//! Wasmtime's implementation of `wasi:http` //! -//! This crate is Wasmtime's host implementation of the `wasi:http` package as -//! part of WASIp2. This crate's implementation is primarily built on top of -//! [`hyper`] and [`tokio`]. -//! -//! # WASI HTTP Interfaces -//! -//! This crate contains implementations of the following interfaces: -//! -//! * [`wasi:http/incoming-handler`] -//! * [`wasi:http/outgoing-handler`] -//! * [`wasi:http/types`] -//! -//! The crate also contains an implementation of the [`wasi:http/proxy`] world. -//! -//! [`wasi:http/proxy`]: crate::bindings::Proxy -//! [`wasi:http/outgoing-handler`]: crate::bindings::http::outgoing_handler::Host -//! [`wasi:http/types`]: crate::bindings::http::types::Host -//! [`wasi:http/incoming-handler`]: crate::bindings::exports::wasi::http::incoming_handler::Guest -//! -//! This crate is very similar to [`wasmtime_wasi`] in the it uses the -//! `bindgen!` macro in Wasmtime to generate bindings to interfaces. Bindings -//! are located in the [`bindings`] module. -//! -//! # The `WasiHttpView` trait -//! -//! All `bindgen!`-generated `Host` traits are implemented in terms of a -//! [`WasiHttpView`] trait which provides basic access to [`WasiHttpCtx`], -//! configuration for WASI HTTP, and a [`wasmtime_wasi::ResourceTable`], the -//! state for all host-defined component model resources. -//! -//! The [`WasiHttpView`] trait additionally offers a few other configuration -//! methods such as [`WasiHttpView::send_request`] to customize how outgoing -//! HTTP requests are handled. -//! -//! # Async and Sync -//! -//! There are both asynchronous and synchronous bindings in this crate. For -//! example [`add_to_linker_async`] is for asynchronous embedders and -//! [`add_to_linker_sync`] is for synchronous embedders. Note that under the -//! hood both versions are implemented with `async` on top of [`tokio`]. -//! -//! # Examples -//! -//! Usage of this crate is done through a few steps to get everything hooked up: -//! -//! 1. First implement [`WasiHttpView`] for your type which is the `T` in -//! [`wasmtime::Store`]. -//! 2. Add WASI HTTP interfaces to a [`wasmtime::component::Linker`]. There -//! are a few options of how to do this: -//! * Use [`add_to_linker_async`] to bundle all interfaces in -//! `wasi:http/proxy` together -//! * Use [`add_only_http_to_linker_async`] to add only HTTP interfaces but -//! no others. This is useful when working with -//! [`wasmtime_wasi::p2::add_to_linker_async`] for example. -//! * Add individual interfaces such as with the -//! [`bindings::http::outgoing_handler::add_to_linker`] function. -//! 3. Use [`ProxyPre`](bindings::ProxyPre) to pre-instantiate a component -//! before serving requests. -//! 4. When serving requests use -//! [`ProxyPre::instantiate_async`](bindings::ProxyPre::instantiate_async) -//! to create instances and handle HTTP requests. -//! -//! A standalone example of doing all this looks like: -//! -//! ```no_run -//! use wasmtime::bail; -//! use hyper::server::conn::http1; -//! use std::sync::Arc; -//! use tokio::net::TcpListener; -//! use wasmtime::component::{Component, Linker, ResourceTable}; -//! use wasmtime::{Engine, Result, Store}; -//! use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; -//! use wasmtime_wasi_http::bindings::ProxyPre; -//! use wasmtime_wasi_http::bindings::http::types::Scheme; -//! use wasmtime_wasi_http::body::HyperOutgoingBody; -//! use wasmtime_wasi_http::io::TokioIo; -//! use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; -//! -//! #[tokio::main] -//! async fn main() -> Result<()> { -//! let component = std::env::args().nth(1).unwrap(); -//! -//! // Prepare the `Engine` for Wasmtime -//! let engine = Engine::default(); -//! -//! // Compile the component on the command line to machine code -//! let component = Component::from_file(&engine, &component)?; -//! -//! // Prepare the `ProxyPre` which is a pre-instantiated version of the -//! // component that we have. This will make per-request instantiation -//! // much quicker. -//! let mut linker = Linker::new(&engine); -//! wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; -//! wasmtime_wasi_http::add_only_http_to_linker_async(&mut linker)?; -//! let pre = ProxyPre::new(linker.instantiate_pre(&component)?)?; -//! -//! // Prepare our server state and start listening for connections. -//! let server = Arc::new(MyServer { pre }); -//! let listener = TcpListener::bind("127.0.0.1:8000").await?; -//! println!("Listening on {}", listener.local_addr()?); -//! -//! loop { -//! // Accept a TCP connection and serve all of its requests in a separate -//! // tokio task. Note that for now this only works with HTTP/1.1. -//! let (client, addr) = listener.accept().await?; -//! println!("serving new client from {addr}"); -//! -//! let server = server.clone(); -//! tokio::task::spawn(async move { -//! if let Err(e) = http1::Builder::new() -//! .keep_alive(true) -//! .serve_connection( -//! TokioIo::new(client), -//! hyper::service::service_fn(move |req| { -//! let server = server.clone(); -//! async move { server.handle_request(req).await } -//! }), -//! ) -//! .await -//! { -//! eprintln!("error serving client[{addr}]: {e:?}"); -//! } -//! }); -//! } -//! } -//! -//! struct MyServer { -//! pre: ProxyPre, -//! } -//! -//! impl MyServer { -//! async fn handle_request( -//! &self, -//! req: hyper::Request, -//! ) -> Result> { -//! // Create per-http-request state within a `Store` and prepare the -//! // initial resources passed to the `handle` function. -//! let mut store = Store::new( -//! self.pre.engine(), -//! MyClientState { -//! table: ResourceTable::new(), -//! wasi: WasiCtx::builder().inherit_stdio().build(), -//! http: WasiHttpCtx::new(), -//! }, -//! ); -//! let (sender, receiver) = tokio::sync::oneshot::channel(); -//! let req = store.data_mut().new_incoming_request(Scheme::Http, req)?; -//! let out = store.data_mut().new_response_outparam(sender)?; -//! let pre = self.pre.clone(); -//! -//! // Run the http request itself in a separate task so the task can -//! // optionally continue to execute beyond after the initial -//! // headers/response code are sent. -//! let task = tokio::task::spawn(async move { -//! let proxy = pre.instantiate_async(&mut store).await?; -//! -//! if let Err(e) = proxy -//! .wasi_http_incoming_handler() -//! .call_handle(store, req, out) -//! .await -//! { -//! return Err(e); -//! } -//! -//! Ok(()) -//! }); -//! -//! match receiver.await { -//! // If the client calls `response-outparam::set` then one of these -//! // methods will be called. -//! Ok(Ok(resp)) => Ok(resp), -//! Ok(Err(e)) => Err(e.into()), -//! -//! // Otherwise the `sender` will get dropped along with the `Store` -//! // meaning that the oneshot will get disconnected and here we can -//! // inspect the `task` result to see what happened -//! Err(_) => { -//! let e = match task.await { -//! Ok(Ok(())) => { -//! bail!("guest never invoked `response-outparam::set` method") -//! } -//! Ok(Err(e)) => e, -//! Err(e) => e.into(), -//! }; -//! return Err(e.context("guest never invoked `response-outparam::set` method")); -//! } -//! } -//! } -//! } -//! -//! struct MyClientState { -//! wasi: WasiCtx, -//! http: WasiHttpCtx, -//! table: ResourceTable, -//! } -//! -//! impl WasiView for MyClientState { -//! fn ctx(&mut self) -> WasiCtxView<'_> { -//! WasiCtxView { ctx: &mut self.wasi, table: &mut self.table } -//! } -//! } -//! -//! impl WasiHttpView for MyClientState { -//! fn ctx(&mut self) -> &mut WasiHttpCtx { -//! &mut self.http -//! } -//! -//! fn table(&mut self) -> &mut ResourceTable { -//! &mut self.table -//! } -//! } -//! ``` +//! This crate is organized similarly to [`wasmtime_wasi`] where there is a +//! top-level [`p2`] and [`p3`] module corresponding to the implementation for +//! WASIp2 and WASIp3. #![deny(missing_docs)] #![doc(test(attr(deny(warnings))))] #![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))] #![cfg_attr(docsrs, feature(doc_cfg))] -mod error; -mod http_impl; -mod types_impl; +use http::{HeaderName, header}; -pub mod body; +mod ctx; +mod field_map; #[cfg(feature = "component-model-async")] pub mod handler; pub mod io; -pub mod types; - -pub mod bindings; - +#[cfg(feature = "p2")] +pub mod p2; #[cfg(feature = "p3")] pub mod p3; -pub use crate::error::{ - HttpError, HttpResult, http_request_error, hyper_request_error, hyper_response_error, -}; -#[doc(inline)] -pub use crate::types::{ - DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS, DEFAULT_OUTGOING_BODY_CHUNK_SIZE, WasiHttpCtx, - WasiHttpImpl, WasiHttpView, -}; -use http::header::CONTENT_LENGTH; -use wasmtime::component::{HasData, Linker}; - -/// Add all of the `wasi:http/proxy` world's interfaces to a [`wasmtime::component::Linker`]. -/// -/// This function will add the `async` variant of all interfaces into the -/// `Linker` provided. For embeddings with async support disabled see -/// [`add_to_linker_sync`] instead. -/// -/// # Example -/// -/// ``` -/// use wasmtime::{Engine, Result}; -/// use wasmtime::component::{ResourceTable, Linker}; -/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; -/// use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; -/// -/// fn main() -> Result<()> { -/// let engine = Engine::default(); -/// -/// let mut linker = Linker::::new(&engine); -/// wasmtime_wasi_http::add_to_linker_async(&mut linker)?; -/// // ... add any further functionality to `linker` if desired ... -/// -/// Ok(()) -/// } -/// -/// struct MyState { -/// ctx: WasiCtx, -/// http_ctx: WasiHttpCtx, -/// table: ResourceTable, -/// } -/// -/// impl WasiHttpView for MyState { -/// fn ctx(&mut self) -> &mut WasiHttpCtx { &mut self.http_ctx } -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// -/// impl WasiView for MyState { -/// fn ctx(&mut self) -> WasiCtxView<'_> { -/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table } -/// } -/// } -/// ``` -pub fn add_to_linker_async(l: &mut wasmtime::component::Linker) -> wasmtime::Result<()> -where - T: WasiHttpView + wasmtime_wasi::WasiView + 'static, -{ - wasmtime_wasi::p2::add_to_linker_proxy_interfaces_async(l)?; - add_only_http_to_linker_async(l) -} - -/// A slimmed down version of [`add_to_linker_async`] which only adds -/// `wasi:http` interfaces to the linker. -/// -/// This is useful when using [`wasmtime_wasi::p2::add_to_linker_async`] for -/// example to avoid re-adding the same interfaces twice. -pub fn add_only_http_to_linker_async( - l: &mut wasmtime::component::Linker, -) -> wasmtime::Result<()> -where - T: WasiHttpView + 'static, -{ - let options = crate::bindings::LinkOptions::default(); // FIXME: Thread through to the CLI options. - crate::bindings::http::outgoing_handler::add_to_linker::<_, WasiHttp>(l, |x| { - WasiHttpImpl(x) - })?; - crate::bindings::http::types::add_to_linker::<_, WasiHttp>(l, &options.into(), |x| { - WasiHttpImpl(x) - })?; - - Ok(()) -} - -struct WasiHttp(T); - -impl HasData for WasiHttp { - type Data<'a> = WasiHttpImpl<&'a mut T>; -} - -/// Add all of the `wasi:http/proxy` world's interfaces to a [`wasmtime::component::Linker`]. -/// -/// This function will add the `sync` variant of all interfaces into the -/// `Linker` provided. For embeddings with async support see -/// [`add_to_linker_async`] instead. -/// -/// # Example -/// -/// ``` -/// use wasmtime::{Engine, Result, Config}; -/// use wasmtime::component::{ResourceTable, Linker}; -/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; -/// use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; -/// -/// fn main() -> Result<()> { -/// let config = Config::default(); -/// let engine = Engine::new(&config)?; -/// -/// let mut linker = Linker::::new(&engine); -/// wasmtime_wasi_http::add_to_linker_sync(&mut linker)?; -/// // ... add any further functionality to `linker` if desired ... -/// -/// Ok(()) -/// } -/// -/// struct MyState { -/// ctx: WasiCtx, -/// http_ctx: WasiHttpCtx, -/// table: ResourceTable, -/// } -/// impl WasiHttpView for MyState { -/// fn ctx(&mut self) -> &mut WasiHttpCtx { &mut self.http_ctx } -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// impl WasiView for MyState { -/// fn ctx(&mut self) -> WasiCtxView<'_> { -/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table } -/// } -/// } -/// ``` -pub fn add_to_linker_sync(l: &mut Linker) -> wasmtime::Result<()> -where - T: WasiHttpView + wasmtime_wasi::WasiView + 'static, -{ - wasmtime_wasi::p2::add_to_linker_proxy_interfaces_sync(l)?; - add_only_http_to_linker_sync(l) -} - -/// A slimmed down version of [`add_to_linker_sync`] which only adds -/// `wasi:http` interfaces to the linker. -/// -/// This is useful when using [`wasmtime_wasi::p2::add_to_linker_sync`] for -/// example to avoid re-adding the same interfaces twice. -pub fn add_only_http_to_linker_sync(l: &mut Linker) -> wasmtime::Result<()> -where - T: WasiHttpView + 'static, -{ - let options = crate::bindings::LinkOptions::default(); // FIXME: Thread through to the CLI options. - crate::bindings::sync::http::outgoing_handler::add_to_linker::<_, WasiHttp>(l, |x| { - WasiHttpImpl(x) - })?; - crate::bindings::sync::http::types::add_to_linker::<_, WasiHttp>(l, &options.into(), |x| { - WasiHttpImpl(x) - })?; - - Ok(()) -} +pub use ctx::*; +pub use field_map::*; /// Extract the `Content-Length` header value from a [`http::HeaderMap`], returning `None` if it's not /// present. This function will return `Err` if it's not possible to parse the `Content-Length` /// header. +#[cfg(any(feature = "p2", feature = "p3"))] fn get_content_length(headers: &http::HeaderMap) -> wasmtime::Result> { - let Some(v) = headers.get(CONTENT_LENGTH) else { + let Some(v) = headers.get(header::CONTENT_LENGTH) else { return Ok(None); }; let v = v.to_str()?; let v = v.parse()?; Ok(Some(v)) } + +/// Set of [http::header::HeaderName], that are forbidden by default +/// for requests and responses originating in the guest. +pub const DEFAULT_FORBIDDEN_HEADERS: [HeaderName; 9] = [ + header::CONNECTION, + HeaderName::from_static("keep-alive"), + header::PROXY_AUTHENTICATE, + header::PROXY_AUTHORIZATION, + HeaderName::from_static("proxy-connection"), + header::TRANSFER_ENCODING, + header::UPGRADE, + header::HOST, + HeaderName::from_static("http2-settings"), +]; diff --git a/crates/wasi-http/src/bindings.rs b/crates/wasi-http/src/p2/bindings.rs similarity index 89% rename from crates/wasi-http/src/bindings.rs rename to crates/wasi-http/src/p2/bindings.rs index bec9402a8775..f3e8b3503576 100644 --- a/crates/wasi-http/src/bindings.rs +++ b/crates/wasi-http/src/p2/bindings.rs @@ -2,8 +2,8 @@ #[expect(missing_docs, reason = "bindgen-generated code")] mod generated { - use crate::body; - use crate::types; + use crate::p2::body; + use crate::p2::types; wasmtime::component::bindgen!({ path: "wit", @@ -26,11 +26,12 @@ mod generated { "wasi:http/types.response-outparam": types::HostResponseOutparam, "wasi:http/types.outgoing-request": types::HostOutgoingRequest, "wasi:http/types.incoming-request": types::HostIncomingRequest, - "wasi:http/types.fields": types::HostFields, + "wasi:http/types.fields": crate::FieldMap, "wasi:http/types.request-options": types::HostRequestOptions, }, trappable_error_type: { - "wasi:http/types.error-code" => crate::HttpError, + "wasi:http/types.error-code" => crate::p2::HttpError, + "wasi:http/types.header-error" => crate::p2::HeaderError, }, }); } @@ -52,7 +53,7 @@ pub mod sync { imports: { default: tracing }, with: { // http is in this crate - "wasi:http": crate::bindings::http, + "wasi:http": crate::p2::bindings::http, // sync requires the wrapper in the wasmtime_wasi crate, in // order to have in_tokio "wasi:io": wasmtime_wasi::p2::bindings::sync::io, diff --git a/crates/wasi-http/src/body.rs b/crates/wasi-http/src/p2/body.rs similarity index 97% rename from crates/wasi-http/src/body.rs rename to crates/wasi-http/src/p2/body.rs index 000bd567c8c7..99dbb949e8f8 100644 --- a/crates/wasi-http/src/body.rs +++ b/crates/wasi-http/src/p2/body.rs @@ -1,7 +1,7 @@ //! Implementation of the `wasi:http/types` interface's various body types. -use crate::bindings::http::types; -use crate::types::FieldMap; +use crate::FieldMap; +use crate::p2::bindings::http::types; use bytes::Bytes; use http_body::{Body, Frame}; use http_body_util::BodyExt; @@ -25,7 +25,6 @@ pub type HyperOutgoingBody = UnsyncBoxBody; #[derive(Debug)] pub struct HostIncomingBody { body: IncomingBodyState, - field_size_limit: usize, /// An optional worker task to keep alive while this body is being read. /// This ensures that if the parent of this body is dropped before the body /// then the backing data behind this worker is kept alive. @@ -34,15 +33,10 @@ pub struct HostIncomingBody { impl HostIncomingBody { /// Create a new `HostIncomingBody` with the given `body` and a per-frame timeout - pub fn new( - body: HyperIncomingBody, - between_bytes_timeout: Duration, - field_size_limit: usize, - ) -> HostIncomingBody { + pub fn new(body: HyperIncomingBody, between_bytes_timeout: Duration) -> HostIncomingBody { let body = BodyWithTimeout::new(body, between_bytes_timeout); HostIncomingBody { body: IncomingBodyState::Start(body), - field_size_limit, worker: None, } } @@ -325,7 +319,7 @@ pub enum HostFutureTrailers { /// /// Note that `Ok(None)` means that there were no trailers for this request /// while `Ok(Some(_))` means that trailers were found in the request. - Done(Result, types::ErrorCode>), + Done(Result, types::ErrorCode>), /// Trailers have been consumed by `future-trailers.get`. Consumed, @@ -347,7 +341,7 @@ impl Pollable for HostFutureTrailers { // Trailers were read for us and here they are, so store the // result. Ok(StreamEnd::Trailers(Some(t))) => { - *self = Self::Done(Ok(Some(FieldMap::new(t, body.field_size_limit)))); + *self = Self::Done(Ok(Some(t))); } // The body wasn't fully read and was dropped before trailers // were reached. It's up to us now to complete the body. @@ -379,7 +373,7 @@ impl Pollable for HostFutureTrailers { // If this frame is a data frame ignore it as we're only // interested in trailers. if let Ok(header_map) = frame.into_trailers() { - break Ok(Some(FieldMap::new(header_map, body.field_size_limit))); + break Ok(Some(header_map)); } } } @@ -529,7 +523,7 @@ impl HostOutgoingBody { } let message = if let Some(ts) = trailers { - FinishMessage::Trailers(ts.into_inner()) + FinishMessage::Trailers(ts.into()) } else { FinishMessage::Finished }; diff --git a/crates/wasi-http/src/error.rs b/crates/wasi-http/src/p2/error.rs similarity index 57% rename from crates/wasi-http/src/error.rs rename to crates/wasi-http/src/p2/error.rs index 4279bd8483f3..7dae4ee30f46 100644 --- a/crates/wasi-http/src/error.rs +++ b/crates/wasi-http/src/p2/error.rs @@ -1,4 +1,5 @@ -use crate::bindings::http::types::ErrorCode; +use crate::FieldMapError; +use crate::p2::bindings::http::types::{self, ErrorCode}; use std::error::Error; use std::fmt; use wasmtime::component::ResourceTableError; @@ -58,9 +59,84 @@ impl fmt::Display for HttpError { impl Error for HttpError {} +/// A [`Result`] type where the error type defaults to [`HeaderError`]. +pub type HeaderResult = Result; + +/// A `wasi:http`-specific error type used to represent either a trap or an +/// [`types::HeaderError`]. +/// +/// Modeled after [`TrappableError`](wasmtime_wasi::TrappableError). +#[repr(transparent)] +pub struct HeaderError { + err: wasmtime::Error, +} + +impl HeaderError { + /// Create a new `HeaderError` that represents a trap. + pub fn trap(err: impl Into) -> HeaderError { + HeaderError { err: err.into() } + } + + /// Downcast this error to an [`ErrorCode`]. + pub fn downcast(self) -> wasmtime::Result { + self.err.downcast() + } + + /// Downcast this error to a reference to an [`ErrorCode`] + pub fn downcast_ref(&self) -> Option<&types::HeaderError> { + self.err.downcast_ref() + } +} + +impl From for HeaderError { + fn from(error: types::HeaderError) -> Self { + Self { err: error.into() } + } +} + +impl From for HeaderError { + fn from(error: ResourceTableError) -> Self { + HeaderError::trap(error) + } +} + +impl From for HeaderError { + fn from(_: http::header::InvalidHeaderName) -> Self { + HeaderError::from(types::HeaderError::InvalidSyntax) + } +} + +impl From for HeaderError { + fn from(_: http::header::InvalidHeaderValue) -> Self { + HeaderError::from(types::HeaderError::InvalidSyntax) + } +} + +impl From for HeaderError { + fn from(err: FieldMapError) -> Self { + match err { + FieldMapError::Immutable => types::HeaderError::Immutable.into(), + FieldMapError::InvalidHeaderName => types::HeaderError::InvalidSyntax.into(), + FieldMapError::TooManyFields | FieldMapError::TotalSizeTooBig => HeaderError::trap(err), + } + } +} + +impl fmt::Debug for HeaderError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.err.fmt(f) + } +} + +impl fmt::Display for HeaderError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.err.fmt(f) + } +} + #[cfg(feature = "default-send-request")] pub(crate) fn dns_error(rcode: String, info_code: u16) -> ErrorCode { - ErrorCode::DnsError(crate::bindings::http::types::DnsErrorPayload { + ErrorCode::DnsError(crate::p2::bindings::http::types::DnsErrorPayload { rcode: Some(rcode), info_code: Some(info_code), }) diff --git a/crates/wasi-http/src/http_impl.rs b/crates/wasi-http/src/p2/http_impl.rs similarity index 87% rename from crates/wasi-http/src/http_impl.rs rename to crates/wasi-http/src/p2/http_impl.rs index 903072cd43aa..9510f7a44d6f 100644 --- a/crates/wasi-http/src/http_impl.rs +++ b/crates/wasi-http/src/p2/http_impl.rs @@ -1,7 +1,7 @@ //! Implementation of the `wasi:http/outgoing-handler` interface. -use crate::{ - WasiHttpImpl, WasiHttpView, +use crate::p2::{ + HttpResult, WasiHttpCtxView, bindings::http::{ outgoing_handler, types::{self, Scheme}, @@ -15,16 +15,13 @@ use http_body_util::{BodyExt, Empty}; use hyper::Method; use wasmtime::component::Resource; -impl outgoing_handler::Host for WasiHttpImpl -where - T: WasiHttpView, -{ +impl outgoing_handler::Host for WasiHttpCtxView<'_> { fn handle( &mut self, request_id: Resource, options: Option>, - ) -> crate::HttpResult> { - let opts = options.and_then(|opts| self.table().get(&opts).ok()); + ) -> HttpResult> { + let opts = options.and_then(|opts| self.table.get(&opts).ok()); let connect_timeout = opts .and_then(|opts| opts.connect_timeout) @@ -38,7 +35,7 @@ where .and_then(|opts| opts.between_bytes_timeout) .unwrap_or(std::time::Duration::from_secs(600)); - let req = self.table().delete(request_id)?; + let req = self.table.delete(request_id)?; let mut builder = hyper::Request::builder(); builder = builder.method(match req.method { @@ -79,7 +76,7 @@ where builder = builder.uri(uri.build().map_err(http_request_error)?); - for (k, v) in req.headers.as_ref().iter() { + for (k, v) in req.headers.iter() { builder = builder.header(k, v); } @@ -93,7 +90,7 @@ where .body(body) .map_err(|err| internal_error(err.to_string()))?; - let future = self.send_request( + let future = self.hooks.send_request( request, OutgoingRequestConfig { use_tls, @@ -103,6 +100,6 @@ where }, )?; - Ok(self.table().push(future)?) + Ok(self.table.push(future)?) } } diff --git a/crates/wasi-http/src/p2/mod.rs b/crates/wasi-http/src/p2/mod.rs new file mode 100644 index 000000000000..3090b32d05ef --- /dev/null +++ b/crates/wasi-http/src/p2/mod.rs @@ -0,0 +1,704 @@ +//! # Wasmtime's WASI HTTPp2 Implementation +//! +//! This module is Wasmtime's host implementation of the `wasi:http` package as +//! part of WASIp2. This crate's implementation is primarily built on top of +//! [`hyper`] and [`tokio`]. +//! +//! # WASI HTTP Interfaces +//! +//! This crate contains implementations of the following interfaces: +//! +//! * [`wasi:http/incoming-handler`] +//! * [`wasi:http/outgoing-handler`] +//! * [`wasi:http/types`] +//! +//! The crate also contains an implementation of the [`wasi:http/proxy`] world. +//! +//! [`wasi:http/proxy`]: crate::p2::bindings::Proxy +//! [`wasi:http/outgoing-handler`]: crate::p2::bindings::http::outgoing_handler::Host +//! [`wasi:http/types`]: crate::p2::bindings::http::types::Host +//! [`wasi:http/incoming-handler`]: crate::p2::bindings::exports::wasi::http::incoming_handler::Guest +//! +//! This crate is very similar to [`wasmtime_wasi`] in the it uses the +//! `bindgen!` macro in Wasmtime to generate bindings to interfaces. Bindings +//! are located in the [`bindings`] module. +//! +//! # The `WasiHttp{View,Hooks}` traits +//! +//! All `bindgen!`-generated `Host` traits are implemented for the +//! [`WasiHttpCtxView`] type. This type is created from a store's data `T` +//! through the [`WasiHttpView`] trait. The [`add_to_linker_async`] function, +//! for example, uses [`WasiHttpView`] to acquire the context view. +//! +//! The [`WasiHttpCtxView`] structure requires that a [`ResourceTable`] and +//! [`WasiHttpCtx`] live within the store. This is store-specific state that is +//! used to implement various APIs and store host state. +//! +//! The final `hooks` field within [`WasiHttpCtxView`] is a trait object of +//! [`WasiHttpHooks`]. This provides a few more hooks, dynamically, to configure +//! how `wasi:http` behaves. For example [`WasiHttpHooks::send_request`] can +//! customize how outgoing HTTP requests are handled. The `hooks` field can be +//! initialized with the [`default_hooks`] function for the default behavior. +//! +//! # Async and Sync +//! +//! There are both asynchronous and synchronous bindings in this crate. For +//! example [`add_to_linker_async`] is for asynchronous embedders and +//! [`add_to_linker_sync`] is for synchronous embedders. Note that under the +//! hood both versions are implemented with `async` on top of [`tokio`]. +//! +//! # Examples +//! +//! Usage of this crate is done through a few steps to get everything hooked up: +//! +//! 1. First implement [`WasiHttpView`] for your type which is the `T` in +//! [`wasmtime::Store`]. +//! 2. Add WASI HTTP interfaces to a [`wasmtime::component::Linker`]. There +//! are a few options of how to do this: +//! * Use [`add_to_linker_async`] to bundle all interfaces in +//! `wasi:http/proxy` together +//! * Use [`add_only_http_to_linker_async`] to add only HTTP interfaces but +//! no others. This is useful when working with +//! [`wasmtime_wasi::p2::add_to_linker_async`] for example. +//! * Add individual interfaces such as with the +//! [`bindings::http::outgoing_handler::add_to_linker`] function. +//! 3. Use [`ProxyPre`](bindings::ProxyPre) to pre-instantiate a component +//! before serving requests. +//! 4. When serving requests use +//! [`ProxyPre::instantiate_async`](bindings::ProxyPre::instantiate_async) +//! to create instances and handle HTTP requests. +//! +//! A standalone example of doing all this looks like: +//! +//! ```no_run +//! use wasmtime::bail; +//! use hyper::server::conn::http1; +//! use std::sync::Arc; +//! use tokio::net::TcpListener; +//! use wasmtime::component::{Component, Linker, ResourceTable}; +//! use wasmtime::{Engine, Result, Store}; +//! use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; +//! use wasmtime_wasi_http::p2::bindings::ProxyPre; +//! use wasmtime_wasi_http::p2::bindings::http::types::Scheme; +//! use wasmtime_wasi_http::p2::body::HyperOutgoingBody; +//! use wasmtime_wasi_http::io::TokioIo; +//! use wasmtime_wasi_http::{WasiHttpCtx, p2::{WasiHttpView, WasiHttpCtxView}}; +//! +//! #[tokio::main] +//! async fn main() -> Result<()> { +//! let component = std::env::args().nth(1).unwrap(); +//! +//! // Prepare the `Engine` for Wasmtime +//! let engine = Engine::default(); +//! +//! // Compile the component on the command line to machine code +//! let component = Component::from_file(&engine, &component)?; +//! +//! // Prepare the `ProxyPre` which is a pre-instantiated version of the +//! // component that we have. This will make per-request instantiation +//! // much quicker. +//! let mut linker = Linker::new(&engine); +//! wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; +//! wasmtime_wasi_http::p2::add_only_http_to_linker_async(&mut linker)?; +//! let pre = ProxyPre::new(linker.instantiate_pre(&component)?)?; +//! +//! // Prepare our server state and start listening for connections. +//! let server = Arc::new(MyServer { pre }); +//! let listener = TcpListener::bind("127.0.0.1:8000").await?; +//! println!("Listening on {}", listener.local_addr()?); +//! +//! loop { +//! // Accept a TCP connection and serve all of its requests in a separate +//! // tokio task. Note that for now this only works with HTTP/1.1. +//! let (client, addr) = listener.accept().await?; +//! println!("serving new client from {addr}"); +//! +//! let server = server.clone(); +//! tokio::task::spawn(async move { +//! if let Err(e) = http1::Builder::new() +//! .keep_alive(true) +//! .serve_connection( +//! TokioIo::new(client), +//! hyper::service::service_fn(move |req| { +//! let server = server.clone(); +//! async move { server.handle_request(req).await } +//! }), +//! ) +//! .await +//! { +//! eprintln!("error serving client[{addr}]: {e:?}"); +//! } +//! }); +//! } +//! } +//! +//! struct MyServer { +//! pre: ProxyPre, +//! } +//! +//! impl MyServer { +//! async fn handle_request( +//! &self, +//! req: hyper::Request, +//! ) -> Result> { +//! // Create per-http-request state within a `Store` and prepare the +//! // initial resources passed to the `handle` function. +//! let mut store = Store::new( +//! self.pre.engine(), +//! MyClientState { +//! table: ResourceTable::new(), +//! wasi: WasiCtx::builder().inherit_stdio().build(), +//! http: WasiHttpCtx::new(), +//! }, +//! ); +//! let (sender, receiver) = tokio::sync::oneshot::channel(); +//! let req = store.data_mut().http().new_incoming_request(Scheme::Http, req)?; +//! let out = store.data_mut().http().new_response_outparam(sender)?; +//! let pre = self.pre.clone(); +//! +//! // Run the http request itself in a separate task so the task can +//! // optionally continue to execute beyond after the initial +//! // headers/response code are sent. +//! let task = tokio::task::spawn(async move { +//! let proxy = pre.instantiate_async(&mut store).await?; +//! +//! if let Err(e) = proxy +//! .wasi_http_incoming_handler() +//! .call_handle(store, req, out) +//! .await +//! { +//! return Err(e); +//! } +//! +//! Ok(()) +//! }); +//! +//! match receiver.await { +//! // If the client calls `response-outparam::set` then one of these +//! // methods will be called. +//! Ok(Ok(resp)) => Ok(resp), +//! Ok(Err(e)) => Err(e.into()), +//! +//! // Otherwise the `sender` will get dropped along with the `Store` +//! // meaning that the oneshot will get disconnected and here we can +//! // inspect the `task` result to see what happened +//! Err(_) => { +//! let e = match task.await { +//! Ok(Ok(())) => { +//! bail!("guest never invoked `response-outparam::set` method") +//! } +//! Ok(Err(e)) => e, +//! Err(e) => e.into(), +//! }; +//! return Err(e.context("guest never invoked `response-outparam::set` method")); +//! } +//! } +//! } +//! } +//! +//! struct MyClientState { +//! wasi: WasiCtx, +//! http: WasiHttpCtx, +//! table: ResourceTable, +//! } +//! +//! impl WasiView for MyClientState { +//! fn ctx(&mut self) -> WasiCtxView<'_> { +//! WasiCtxView { ctx: &mut self.wasi, table: &mut self.table } +//! } +//! } +//! +//! impl WasiHttpView for MyClientState { +//! fn http(&mut self) -> WasiHttpCtxView<'_> { +//! WasiHttpCtxView { +//! ctx: &mut self.http, +//! table: &mut self.table, +//! hooks: Default::default(), +//! } +//! } +//! } +//! ``` + +#[cfg(feature = "default-send-request")] +use self::bindings::http::types::ErrorCode; +use crate::{DEFAULT_FORBIDDEN_HEADERS, WasiHttpCtx}; +use http::HeaderName; +use wasmtime::component::{HasData, Linker, ResourceTable}; + +mod error; +mod http_impl; +mod types_impl; + +pub mod bindings; +pub mod body; +pub mod types; + +pub use self::error::*; + +/// A trait which provides hooks into internal WASI HTTP operations. +/// +/// # Example +/// +/// ``` +/// use wasmtime::component::ResourceTable; +/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; +/// use wasmtime_wasi_http::WasiHttpCtx; +/// use wasmtime_wasi_http::p2::{WasiHttpView, WasiHttpCtxView}; +/// +/// struct MyState { +/// ctx: WasiCtx, +/// http_ctx: WasiHttpCtx, +/// table: ResourceTable, +/// } +/// +/// impl WasiHttpView for MyState { +/// fn http(&mut self) -> WasiHttpCtxView<'_> { +/// WasiHttpCtxView { +/// ctx: &mut self.http_ctx, +/// table: &mut self.table, +/// hooks: Default::default(), +/// } +/// } +/// } +/// +/// impl WasiView for MyState { +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table } +/// } +/// } +/// +/// impl MyState { +/// fn new() -> MyState { +/// let mut wasi = WasiCtx::builder(); +/// wasi.arg("./foo.wasm"); +/// wasi.arg("--help"); +/// wasi.env("FOO", "bar"); +/// +/// MyState { +/// ctx: wasi.build(), +/// table: ResourceTable::new(), +/// http_ctx: WasiHttpCtx::new(), +/// } +/// } +/// } +/// ``` +pub trait WasiHttpHooks: Send { + /// Send an outgoing request. + #[cfg(feature = "default-send-request")] + fn send_request( + &mut self, + request: hyper::Request, + config: types::OutgoingRequestConfig, + ) -> HttpResult { + Ok(default_send_request(request, config)) + } + + /// Send an outgoing request. + #[cfg(not(feature = "default-send-request"))] + fn send_request( + &mut self, + request: hyper::Request, + config: types::OutgoingRequestConfig, + ) -> HttpResult; + + /// Whether a given header should be considered forbidden and not allowed. + fn is_forbidden_header(&mut self, name: &HeaderName) -> bool { + DEFAULT_FORBIDDEN_HEADERS.contains(name) + } + + /// Number of distinct write calls to the outgoing body's output-stream + /// that the implementation will buffer. + /// Default: 1. + fn outgoing_body_buffer_chunks(&mut self) -> usize { + DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS + } + + /// Maximum size allowed in a write call to the outgoing body's output-stream. + /// Default: 1024 * 1024. + fn outgoing_body_chunk_size(&mut self) -> usize { + DEFAULT_OUTGOING_BODY_CHUNK_SIZE + } +} + +#[cfg(feature = "default-send-request")] +impl<'a> Default for &'a mut dyn WasiHttpHooks { + fn default() -> Self { + let x: &mut [(); 0] = &mut []; + x + } +} + +#[doc(hidden)] +#[cfg(feature = "default-send-request")] +impl WasiHttpHooks for [(); 0] {} + +/// Returns a value suitable for the `WasiHttpCtxView::hooks` field which has +/// the default behavior for `wasi:http`. +#[cfg(feature = "default-send-request")] +pub fn default_hooks() -> &'static mut dyn WasiHttpHooks { + Default::default() +} + +/// The default value configured for [`WasiHttpHooks::outgoing_body_buffer_chunks`] in [`WasiHttpView`]. +pub const DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS: usize = 1; +/// The default value configured for [`WasiHttpHooks::outgoing_body_chunk_size`] in [`WasiHttpView`]. +pub const DEFAULT_OUTGOING_BODY_CHUNK_SIZE: usize = 1024 * 1024; + +/// Structure which `wasi:http` `Host`-style traits are implemented for. +/// +/// This structure is used by embedders with the [`WasiHttpView`] trait's return +/// value and is used to provide access to this crate all internals necessary to +/// implement `wasi:http`. This is similar to [`wasmtime_wasi::WasiCtxView`] +/// for example. +pub struct WasiHttpCtxView<'a> { + /// A reference to a per-store [`WasiHttpCtx`]. + pub ctx: &'a mut WasiHttpCtx, + /// A reference to a per-store table of resources to store host structures + /// within. + pub table: &'a mut ResourceTable, + /// A reference to a per-store set of hooks that can be used to customize + /// `wasi:http` behavior. + pub hooks: &'a mut dyn WasiHttpHooks, +} + +/// The type for which this crate implements the `wasi:http` interfaces. +pub struct WasiHttp; + +impl HasData for WasiHttp { + type Data<'a> = WasiHttpCtxView<'a>; +} + +/// A trait used to project state that this crate needs to implement `wasi:http` +/// from the `self` type. +/// +/// This trait is used in [`add_to_linker_sync`] and [`add_to_linker_async`] for +/// example as a bound on `T` in `Store`. This is used to access data from +/// `T`, the data within a `Store`, an instance of [`WasiHttpCtxView`]. The +/// [`WasiHttpCtxView`] contains contextual information such as the +/// [`ResourceTable`] for the store, HTTP context info in [`WasiHttpCtx`], and +/// any hooks via [`WasiHttpHooks`] if the embedder desires. +/// +/// # Example +/// +/// ``` +/// use wasmtime::component::ResourceTable; +/// use wasmtime_wasi_http::WasiHttpCtx; +/// use wasmtime_wasi_http::p2::{WasiHttpView, WasiHttpCtxView}; +/// +/// struct MyState { +/// http_ctx: WasiHttpCtx, +/// table: ResourceTable, +/// } +/// +/// impl WasiHttpView for MyState { +/// fn http(&mut self) -> WasiHttpCtxView<'_> { +/// WasiHttpCtxView { +/// ctx: &mut self.http_ctx, +/// table: &mut self.table, +/// hooks: Default::default(), +/// } +/// } +/// } +/// ``` +pub trait WasiHttpView { + /// Returns an instance of [`WasiHttpCtxView`] projected out of `self`. + fn http(&mut self) -> WasiHttpCtxView<'_>; +} + +/// Add all of the `wasi:http/proxy` world's interfaces to a [`wasmtime::component::Linker`]. +/// +/// This function will add the `async` variant of all interfaces into the +/// `Linker` provided. For embeddings with async support disabled see +/// [`add_to_linker_sync`] instead. +/// +/// # Example +/// +/// ``` +/// use wasmtime::{Engine, Result}; +/// use wasmtime::component::{ResourceTable, Linker}; +/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; +/// use wasmtime_wasi_http::{WasiHttpCtx, p2::{WasiHttpView, WasiHttpCtxView}}; +/// +/// fn main() -> Result<()> { +/// let engine = Engine::default(); +/// +/// let mut linker = Linker::::new(&engine); +/// wasmtime_wasi_http::p2::add_to_linker_async(&mut linker)?; +/// // ... add any further functionality to `linker` if desired ... +/// +/// Ok(()) +/// } +/// +/// struct MyState { +/// ctx: WasiCtx, +/// http_ctx: WasiHttpCtx, +/// table: ResourceTable, +/// } +/// +/// impl WasiHttpView for MyState { +/// fn http(&mut self) -> WasiHttpCtxView<'_> { +/// WasiHttpCtxView { +/// ctx: &mut self.http_ctx, +/// table: &mut self.table, +/// hooks: Default::default(), +/// } +/// } +/// } +/// +/// impl WasiView for MyState { +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table } +/// } +/// } +/// ``` +pub fn add_to_linker_async(l: &mut wasmtime::component::Linker) -> wasmtime::Result<()> +where + T: WasiHttpView + wasmtime_wasi::WasiView + 'static, +{ + wasmtime_wasi::p2::add_to_linker_proxy_interfaces_async(l)?; + add_only_http_to_linker_async(l) +} + +/// A slimmed down version of [`add_to_linker_async`] which only adds +/// `wasi:http` interfaces to the linker. +/// +/// This is useful when using [`wasmtime_wasi::p2::add_to_linker_async`] for +/// example to avoid re-adding the same interfaces twice. +pub fn add_only_http_to_linker_async( + l: &mut wasmtime::component::Linker, +) -> wasmtime::Result<()> +where + T: WasiHttpView + 'static, +{ + let options = bindings::LinkOptions::default(); // FIXME: Thread through to the CLI options. + bindings::http::outgoing_handler::add_to_linker::<_, WasiHttp>(l, T::http)?; + bindings::http::types::add_to_linker::<_, WasiHttp>(l, &options.into(), T::http)?; + + Ok(()) +} + +/// Add all of the `wasi:http/proxy` world's interfaces to a [`wasmtime::component::Linker`]. +/// +/// This function will add the `sync` variant of all interfaces into the +/// `Linker` provided. For embeddings with async support see +/// [`add_to_linker_async`] instead. +/// +/// # Example +/// +/// ``` +/// use wasmtime::{Engine, Result, Config}; +/// use wasmtime::component::{ResourceTable, Linker}; +/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; +/// use wasmtime_wasi_http::WasiHttpCtx; +/// use wasmtime_wasi_http::p2::{WasiHttpView, WasiHttpCtxView}; +/// +/// fn main() -> Result<()> { +/// let config = Config::default(); +/// let engine = Engine::new(&config)?; +/// +/// let mut linker = Linker::::new(&engine); +/// wasmtime_wasi_http::p2::add_to_linker_sync(&mut linker)?; +/// // ... add any further functionality to `linker` if desired ... +/// +/// Ok(()) +/// } +/// +/// struct MyState { +/// ctx: WasiCtx, +/// http_ctx: WasiHttpCtx, +/// table: ResourceTable, +/// } +/// impl WasiHttpView for MyState { +/// fn http(&mut self) -> WasiHttpCtxView<'_> { +/// WasiHttpCtxView { +/// ctx: &mut self.http_ctx, +/// table: &mut self.table, +/// hooks: Default::default(), +/// } +/// } +/// } +/// impl WasiView for MyState { +/// fn ctx(&mut self) -> WasiCtxView<'_> { +/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table } +/// } +/// } +/// ``` +pub fn add_to_linker_sync(l: &mut Linker) -> wasmtime::Result<()> +where + T: WasiHttpView + wasmtime_wasi::WasiView + 'static, +{ + wasmtime_wasi::p2::add_to_linker_proxy_interfaces_sync(l)?; + add_only_http_to_linker_sync(l) +} + +/// A slimmed down version of [`add_to_linker_sync`] which only adds +/// `wasi:http` interfaces to the linker. +/// +/// This is useful when using [`wasmtime_wasi::p2::add_to_linker_sync`] for +/// example to avoid re-adding the same interfaces twice. +pub fn add_only_http_to_linker_sync(l: &mut Linker) -> wasmtime::Result<()> +where + T: WasiHttpView + 'static, +{ + let options = bindings::LinkOptions::default(); // FIXME: Thread through to the CLI options. + bindings::sync::http::outgoing_handler::add_to_linker::<_, WasiHttp>(l, T::http)?; + bindings::sync::http::types::add_to_linker::<_, WasiHttp>(l, &options.into(), T::http)?; + + Ok(()) +} + +/// The default implementation of how an outgoing request is sent. +/// +/// This implementation is used by the `wasi:http/outgoing-handler` interface +/// default implementation. +#[cfg(feature = "default-send-request")] +pub fn default_send_request( + request: hyper::Request, + config: types::OutgoingRequestConfig, +) -> types::HostFutureIncomingResponse { + let handle = wasmtime_wasi::runtime::spawn(async move { + Ok(default_send_request_handler(request, config).await) + }); + types::HostFutureIncomingResponse::pending(handle) +} + +/// The underlying implementation of how an outgoing request is sent. This should likely be spawned +/// in a task. +/// +/// This is called from [default_send_request] to actually send the request. +#[cfg(feature = "default-send-request")] +pub async fn default_send_request_handler( + mut request: hyper::Request, + types::OutgoingRequestConfig { + use_tls, + connect_timeout, + first_byte_timeout, + between_bytes_timeout, + }: types::OutgoingRequestConfig, +) -> Result { + use crate::io::TokioIo; + use crate::p2::{error::dns_error, hyper_request_error}; + use http_body_util::BodyExt; + use tokio::net::TcpStream; + use tokio::time::timeout; + + let authority = if let Some(authority) = request.uri().authority() { + if authority.port().is_some() { + authority.to_string() + } else { + let port = if use_tls { 443 } else { 80 }; + format!("{}:{port}", authority.to_string()) + } + } else { + return Err(ErrorCode::HttpRequestUriInvalid); + }; + let tcp_stream = timeout(connect_timeout, TcpStream::connect(&authority)) + .await + .map_err(|_| ErrorCode::ConnectionTimeout)? + .map_err(|e| match e.kind() { + std::io::ErrorKind::AddrNotAvailable => { + dns_error("address not available".to_string(), 0) + } + + _ => { + if e.to_string() + .starts_with("failed to lookup address information") + { + dns_error("address not available".to_string(), 0) + } else { + ErrorCode::ConnectionRefused + } + } + })?; + + let (mut sender, worker) = if use_tls { + use rustls::pki_types::ServerName; + + // derived from https://github.com/rustls/rustls/blob/main/examples/src/bin/simpleclient.rs + let root_cert_store = rustls::RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.into(), + }; + let config = rustls::ClientConfig::builder() + .with_root_certificates(root_cert_store) + .with_no_client_auth(); + let connector = tokio_rustls::TlsConnector::from(std::sync::Arc::new(config)); + let mut parts = authority.split(":"); + let host = parts.next().unwrap_or(&authority); + let domain = ServerName::try_from(host) + .map_err(|e| { + tracing::warn!("dns lookup error: {e:?}"); + dns_error("invalid dns name".to_string(), 0) + })? + .to_owned(); + let stream = connector.connect(domain, tcp_stream).await.map_err(|e| { + tracing::warn!("tls protocol error: {e:?}"); + ErrorCode::TlsProtocolError + })?; + let stream = TokioIo::new(stream); + + let (sender, conn) = timeout( + connect_timeout, + hyper::client::conn::http1::handshake(stream), + ) + .await + .map_err(|_| ErrorCode::ConnectionTimeout)? + .map_err(hyper_request_error)?; + + let worker = wasmtime_wasi::runtime::spawn(async move { + match conn.await { + Ok(()) => {} + // TODO: shouldn't throw away this error and ideally should + // surface somewhere. + Err(e) => tracing::warn!("dropping error {e}"), + } + }); + + (sender, worker) + } else { + let tcp_stream = TokioIo::new(tcp_stream); + let (sender, conn) = timeout( + connect_timeout, + // TODO: we should plumb the builder through the http context, and use it here + hyper::client::conn::http1::handshake(tcp_stream), + ) + .await + .map_err(|_| ErrorCode::ConnectionTimeout)? + .map_err(hyper_request_error)?; + + let worker = wasmtime_wasi::runtime::spawn(async move { + match conn.await { + Ok(()) => {} + // TODO: same as above, shouldn't throw this error away. + Err(e) => tracing::warn!("dropping error {e}"), + } + }); + + (sender, worker) + }; + + // at this point, the request contains the scheme and the authority, but + // the http packet should only include those if addressing a proxy, so + // remove them here, since SendRequest::send_request does not do it for us + *request.uri_mut() = http::Uri::builder() + .path_and_query( + request + .uri() + .path_and_query() + .map(|p| p.as_str()) + .unwrap_or("/"), + ) + .build() + .expect("comes from valid request"); + + let resp = timeout(first_byte_timeout, sender.send_request(request)) + .await + .map_err(|_| ErrorCode::ConnectionReadTimeout)? + .map_err(hyper_request_error)? + .map(|body| body.map_err(hyper_request_error).boxed_unsync()); + + Ok(types::IncomingResponse { + resp, + worker: Some(worker), + between_bytes_timeout, + }) +} diff --git a/crates/wasi-http/src/p2/types.rs b/crates/wasi-http/src/p2/types.rs new file mode 100644 index 000000000000..dd845ad7b2e5 --- /dev/null +++ b/crates/wasi-http/src/p2/types.rs @@ -0,0 +1,301 @@ +//! Implements the base structure that will provide the implementation of the +//! wasi-http API. + +use crate::FieldMap; +use crate::p2::{ + WasiHttpCtxView, WasiHttpHooks, + bindings::http::types::{self, ErrorCode, Method, Scheme}, + body::{HostIncomingBody, HyperIncomingBody, HyperOutgoingBody}, +}; +use bytes::Bytes; +use http_body_util::BodyExt; +use hyper::body::Body; +use std::time::Duration; +use wasmtime::component::Resource; +use wasmtime::{Result, bail}; +use wasmtime_wasi::p2::Pollable; +use wasmtime_wasi::runtime::AbortOnDropJoinHandle; + +/// Removes forbidden headers from a [`FieldMap`]. +pub(crate) fn remove_forbidden_headers( + hooks: &mut dyn WasiHttpHooks, + headers: &mut http::HeaderMap, +) { + let forbidden_keys = Vec::from_iter(headers.keys().filter_map(|name| { + if hooks.is_forbidden_header(name) { + Some(name.clone()) + } else { + None + } + })); + + for name in forbidden_keys { + headers.remove(&name); + } +} + +/// Configuration for an outgoing request. +pub struct OutgoingRequestConfig { + /// Whether to use TLS for the request. + pub use_tls: bool, + /// The timeout for connecting. + pub connect_timeout: Duration, + /// The timeout until the first byte. + pub first_byte_timeout: Duration, + /// The timeout between chunks of a streaming body + pub between_bytes_timeout: Duration, +} + +impl From for types::Method { + fn from(method: http::Method) -> Self { + if method == http::Method::GET { + types::Method::Get + } else if method == hyper::Method::HEAD { + types::Method::Head + } else if method == hyper::Method::POST { + types::Method::Post + } else if method == hyper::Method::PUT { + types::Method::Put + } else if method == hyper::Method::DELETE { + types::Method::Delete + } else if method == hyper::Method::CONNECT { + types::Method::Connect + } else if method == hyper::Method::OPTIONS { + types::Method::Options + } else if method == hyper::Method::TRACE { + types::Method::Trace + } else if method == hyper::Method::PATCH { + types::Method::Patch + } else { + types::Method::Other(method.to_string()) + } + } +} + +impl TryInto for types::Method { + type Error = http::method::InvalidMethod; + + fn try_into(self) -> Result { + match self { + Method::Get => Ok(http::Method::GET), + Method::Head => Ok(http::Method::HEAD), + Method::Post => Ok(http::Method::POST), + Method::Put => Ok(http::Method::PUT), + Method::Delete => Ok(http::Method::DELETE), + Method::Connect => Ok(http::Method::CONNECT), + Method::Options => Ok(http::Method::OPTIONS), + Method::Trace => Ok(http::Method::TRACE), + Method::Patch => Ok(http::Method::PATCH), + Method::Other(s) => http::Method::from_bytes(s.as_bytes()), + } + } +} + +/// The concrete type behind a `wasi:http/types.incoming-request` resource. +#[derive(Debug)] +pub struct HostIncomingRequest { + pub(crate) method: http::method::Method, + pub(crate) uri: http::uri::Uri, + pub(crate) headers: FieldMap, + pub(crate) scheme: Scheme, + pub(crate) authority: String, + /// The body of the incoming request. + pub body: Option, +} + +impl WasiHttpCtxView<'_> { + /// Create a new incoming request resource. + pub fn new_incoming_request( + &mut self, + scheme: Scheme, + req: hyper::Request, + ) -> wasmtime::Result> + where + B: Body + Send + 'static, + B::Error: Into, + { + let (mut parts, body) = req.into_parts(); + let body = body.map_err(Into::into).boxed_unsync(); + let body = HostIncomingBody::new( + body, + // TODO: this needs to be plumbed through + std::time::Duration::from_millis(600 * 1000), + ); + let authority = match parts.uri.authority() { + Some(authority) => authority.to_string(), + None => match parts.headers.get(http::header::HOST) { + Some(host) => host.to_str()?.to_string(), + None => bail!("invalid HTTP request missing authority in URI and host header"), + }, + }; + + remove_forbidden_headers(self.hooks, &mut parts.headers); + let headers = FieldMap::new_immutable(parts.headers); + + let req = HostIncomingRequest { + method: parts.method, + uri: parts.uri, + headers, + authority, + scheme, + body: Some(body), + }; + Ok(self.table.push(req)?) + } +} + +/// The concrete type behind a `wasi:http/types.response-outparam` resource. +pub struct HostResponseOutparam { + /// The sender for sending a response. + pub result: + tokio::sync::oneshot::Sender, types::ErrorCode>>, +} + +impl WasiHttpCtxView<'_> { + /// Create a new outgoing response resource. + pub fn new_response_outparam( + &mut self, + result: tokio::sync::oneshot::Sender< + Result, types::ErrorCode>, + >, + ) -> wasmtime::Result> { + let id = self.table.push(HostResponseOutparam { result })?; + Ok(id) + } +} + +/// The concrete type behind a `wasi:http/types.outgoing-response` resource. +pub struct HostOutgoingResponse { + /// The status of the response. + pub status: http::StatusCode, + /// The headers of the response. + pub headers: FieldMap, + /// The body of the response. + pub body: Option, +} + +impl TryFrom for hyper::Response { + type Error = http::Error; + + fn try_from( + resp: HostOutgoingResponse, + ) -> Result, Self::Error> { + use http_body_util::Empty; + + let mut builder = hyper::Response::builder().status(resp.status); + + *builder.headers_mut().unwrap() = resp.headers.into(); + + match resp.body { + Some(body) => builder.body(body), + None => builder.body( + Empty::::new() + .map_err(|_| unreachable!("Infallible error")) + .boxed_unsync(), + ), + } + } +} + +/// The concrete type behind a `wasi:http/types.outgoing-request` resource. +#[derive(Debug)] +pub struct HostOutgoingRequest { + /// The method of the request. + pub method: Method, + /// The scheme of the request. + pub scheme: Option, + /// The authority of the request. + pub authority: Option, + /// The path and query of the request. + pub path_with_query: Option, + /// The request headers. + pub headers: FieldMap, + /// The request body. + pub body: Option, +} + +/// The concrete type behind a `wasi:http/types.request-options` resource. +#[derive(Debug, Default)] +pub struct HostRequestOptions { + /// How long to wait for a connection to be established. + pub connect_timeout: Option, + /// How long to wait for the first byte of the response body. + pub first_byte_timeout: Option, + /// How long to wait between frames of the response body. + pub between_bytes_timeout: Option, +} + +/// The concrete type behind a `wasi:http/types.incoming-response` resource. +#[derive(Debug)] +pub struct HostIncomingResponse { + /// The response status + pub status: u16, + /// The response headers + pub headers: FieldMap, + /// The response body + pub body: Option, +} + +/// A handle to a future incoming response. +pub type FutureIncomingResponseHandle = + AbortOnDropJoinHandle>>; + +/// A response that is in the process of being received. +#[derive(Debug)] +pub struct IncomingResponse { + /// The response itself. + pub resp: hyper::Response, + /// Optional worker task that continues to process the response. + pub worker: Option>, + /// The timeout between chunks of the response. + pub between_bytes_timeout: std::time::Duration, +} + +/// The concrete type behind a `wasi:http/types.future-incoming-response` resource. +#[derive(Debug)] +pub enum HostFutureIncomingResponse { + /// A pending response + Pending(FutureIncomingResponseHandle), + /// The response is ready. + /// + /// An outer error will trap while the inner error gets returned to the guest. + Ready(wasmtime::Result>), + /// The response has been consumed. + Consumed, +} + +impl HostFutureIncomingResponse { + /// Create a new `HostFutureIncomingResponse` that is pending on the provided task handle. + pub fn pending(handle: FutureIncomingResponseHandle) -> Self { + Self::Pending(handle) + } + + /// Create a new `HostFutureIncomingResponse` that is ready. + pub fn ready(result: wasmtime::Result>) -> Self { + Self::Ready(result) + } + + /// Returns `true` if the response is ready. + pub fn is_ready(&self) -> bool { + matches!(self, Self::Ready(_)) + } + + /// Unwrap the response, panicking if it is not ready. + pub fn unwrap_ready(self) -> wasmtime::Result> { + match self { + Self::Ready(res) => res, + Self::Pending(_) | Self::Consumed => { + panic!("unwrap_ready called on a pending HostFutureIncomingResponse") + } + } + } +} + +#[async_trait::async_trait] +impl Pollable for HostFutureIncomingResponse { + async fn ready(&mut self) { + if let Self::Pending(handle) = self { + *self = Self::Ready(handle.await); + } + } +} diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/p2/types_impl.rs similarity index 51% rename from crates/wasi-http/src/types_impl.rs rename to crates/wasi-http/src/p2/types_impl.rs index 45db0df7d8ab..e403784a7b52 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/p2/types_impl.rs @@ -1,25 +1,26 @@ //! Implementation for the `wasi:http/types` interface. -use crate::bindings::http::types::{self, Headers, Method, Scheme, StatusCode, Trailers}; -use crate::body::{HostFutureTrailers, HostIncomingBody, HostOutgoingBody, StreamContext}; -use crate::types::{ - FieldMap, FieldSizeLimitError, HostFields, HostFutureIncomingResponse, HostIncomingRequest, - HostIncomingResponse, HostOutgoingRequest, HostOutgoingResponse, HostResponseOutparam, - remove_forbidden_headers, +use crate::FieldMap; +use crate::get_content_length; +use crate::p2::bindings::http::types::{self, Method, Scheme, StatusCode, Trailers}; +use crate::p2::body::{HostFutureTrailers, HostIncomingBody, HostOutgoingBody, StreamContext}; +use crate::p2::types::{ + HostFutureIncomingResponse, HostIncomingRequest, HostIncomingResponse, HostOutgoingRequest, + HostOutgoingResponse, HostResponseOutparam, remove_forbidden_headers, }; -use crate::{HttpError, HttpResult, WasiHttpImpl, WasiHttpView, get_content_length}; -use std::any::Any; +use crate::p2::{HeaderError, HeaderResult, HttpError, HttpResult, WasiHttpCtxView}; +use http::{HeaderName, HeaderValue}; use std::str::FromStr; -use wasmtime::bail; -use wasmtime::component::{Resource, ResourceTable, ResourceTableError}; +use wasmtime::component::Resource; use wasmtime::{error::Context as _, format_err}; use wasmtime_wasi::p2::{DynInputStream, DynOutputStream, DynPollable}; -impl crate::bindings::http::types::Host for WasiHttpImpl -where - T: WasiHttpView, -{ - fn convert_error_code(&mut self, err: crate::HttpError) -> wasmtime::Result { +impl types::Host for WasiHttpCtxView<'_> { + fn convert_error_code(&mut self, err: HttpError) -> wasmtime::Result { + err.downcast() + } + + fn convert_header_error(&mut self, err: HeaderError) -> wasmtime::Result { err.downcast() } @@ -27,137 +28,57 @@ where &mut self, err: wasmtime::component::Resource, ) -> wasmtime::Result> { - let e = self.table().get(&err)?; + let e = self.table.get(&err)?; Ok(e.downcast_ref::().cloned()) } } -/// Take ownership of the underlying [`FieldMap`] associated with this fields resource. If the -/// fields resource references another fields, the returned [`FieldMap`] will be cloned. -fn move_fields( - table: &mut ResourceTable, - id: Resource, -) -> Result { - match table.delete(id)? { - HostFields::Ref { parent, get_fields } => { - let entry = table.get_any_mut(parent)?; - Ok(get_fields(entry).clone()) - } - - HostFields::Owned { fields } => Ok(fields), - } -} - -fn get_fields<'a>( - table: &'a mut ResourceTable, - id: &Resource, -) -> wasmtime::Result<&'a FieldMap> { - let fields = table.get(&id)?; - if let HostFields::Ref { parent, get_fields } = *fields { - let entry = table.get_any_mut(parent)?; - return Ok(get_fields(entry)); - } - - match table.get_mut(&id)? { - HostFields::Owned { fields } => Ok(fields), - // NB: ideally the `if let` above would go here instead. That makes - // the borrow-checker unhappy. Unclear why. If you, dear reader, can - // refactor this to remove the `unreachable!` please do. - HostFields::Ref { .. } => unreachable!(), - } -} - -fn get_fields_mut<'a>( - table: &'a mut ResourceTable, - id: &Resource, -) -> wasmtime::Result> { - match table.get_mut(&id)? { - HostFields::Owned { fields } => Ok(Ok(fields)), - HostFields::Ref { .. } => Ok(Err(types::HeaderError::Immutable)), - } -} - -impl crate::bindings::http::types::HostFields for WasiHttpImpl -where - T: WasiHttpView, -{ - fn new(&mut self) -> wasmtime::Result> { - let limit = self.ctx().field_size_limit; +impl types::HostFields for WasiHttpCtxView<'_> { + fn new(&mut self) -> wasmtime::Result> { + let limit = self.ctx.field_size_limit; let id = self - .table() - .push(HostFields::Owned { - fields: FieldMap::empty(limit), - }) + .table + .push(FieldMap::new_mutable(limit)) .context("[new_fields] pushing fields")?; Ok(id) } - fn from_list( - &mut self, - entries: Vec<(String, Vec)>, - ) -> wasmtime::Result, types::HeaderError>> { - let mut fields = hyper::HeaderMap::new(); + fn from_list(&mut self, entries: Vec<(String, Vec)>) -> HeaderResult> { + let mut fields = FieldMap::new_mutable(self.ctx.field_size_limit); for (header, value) in entries { - let header = match hyper::header::HeaderName::from_bytes(header.as_bytes()) { - Ok(header) => header, - Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), - }; - - if self.is_forbidden_header(&header) { - return Ok(Err(types::HeaderError::Forbidden)); + let header = HeaderName::from_bytes(header.as_bytes())?; + if self.hooks.is_forbidden_header(&header) { + return Err(types::HeaderError::Forbidden.into()); } - - let value = match hyper::header::HeaderValue::from_bytes(&value) { - Ok(value) => value, - Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), - }; - - fields.append(header, value); - } - - let size = FieldMap::content_size(&fields); - if size > self.ctx().field_size_limit { - bail!(FieldSizeLimitError { - size, - limit: self.ctx().field_size_limit, - }); + let value = HeaderValue::from_bytes(&value)?; + fields.append(header, value)?; } - let fields = FieldMap::new(fields, self.ctx().field_size_limit); - let id = self - .table() - .push(HostFields::Owned { fields }) - .context("[new_fields] pushing fields")?; - Ok(Ok(id)) + Ok(self.table.push(fields)?) } - fn drop(&mut self, fields: Resource) -> wasmtime::Result<()> { - self.table() + fn drop(&mut self, fields: Resource) -> wasmtime::Result<()> { + self.table .delete(fields) .context("[drop_fields] deleting fields")?; Ok(()) } - fn get( - &mut self, - fields: Resource, - name: String, - ) -> wasmtime::Result>> { - let fields = get_fields(self.table(), &fields).context("[fields_get] getting fields")?; + fn get(&mut self, fields: Resource, name: String) -> wasmtime::Result>> { + let fields = self.table.get(&fields)?; - let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) { + let header = match HeaderName::from_bytes(name.as_bytes()) { Ok(header) => header, Err(_) => return Ok(vec![]), }; - if !fields.as_ref().contains_key(&header) { + if !fields.contains_key(&header) { return Ok(vec![]); } let res = fields - .as_ref() .get_all(&header) .into_iter() .map(|val| val.as_bytes().to_owned()) @@ -165,183 +86,125 @@ where Ok(res) } - fn has(&mut self, fields: Resource, name: String) -> wasmtime::Result { - let fields = get_fields(self.table(), &fields).context("[fields_get] getting fields")?; + fn has(&mut self, fields: Resource, name: String) -> wasmtime::Result { + let fields = self.table.get(&fields)?; - match hyper::header::HeaderName::from_bytes(name.as_bytes()) { - Ok(header) => Ok(fields.as_ref().contains_key(&header)), + match HeaderName::from_bytes(name.as_bytes()) { + Ok(header) => Ok(fields.contains_key(&header)), Err(_) => Ok(false), } } fn set( &mut self, - fields: Resource, + fields: Resource, name: String, byte_values: Vec>, - ) -> wasmtime::Result> { - let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) { - Ok(header) => header, - Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), - }; + ) -> HeaderResult<()> { + let header = HeaderName::from_bytes(name.as_bytes())?; - if self.is_forbidden_header(&header) { - return Ok(Err(types::HeaderError::Forbidden)); + if self.hooks.is_forbidden_header(&header) { + return Err(types::HeaderError::Forbidden.into()); } let mut values = Vec::with_capacity(byte_values.len()); for value in byte_values { - match hyper::header::HeaderValue::from_bytes(&value) { - Ok(value) => values.push(value), - Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), - } + values.push(HeaderValue::from_bytes(&value)?); } - match get_fields_mut(self.table(), &fields) - .context("[fields_set] getting mutable fields")? - { - Ok(fields) => { - fields.remove_all(&header); - for value in values { - fields.append(&header, value)?; - } - Ok(Ok(())) - } - Err(e) => Ok(Err(e)), - } + let fields = self.table.get_mut(&fields)?; + fields.set(header, values)?; + Ok(()) } - fn delete( - &mut self, - fields: Resource, - name: String, - ) -> wasmtime::Result> { - let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) { - Ok(header) => header, - Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), - }; + fn delete(&mut self, fields: Resource, name: String) -> HeaderResult<()> { + let header = HeaderName::from_bytes(name.as_bytes())?; - if self.is_forbidden_header(&header) { - return Ok(Err(types::HeaderError::Forbidden)); + if self.hooks.is_forbidden_header(&header) { + return Err(types::HeaderError::Forbidden.into()); } - Ok(get_fields_mut(self.table(), &fields)?.map(|fields| { - fields.remove_all(&header); - })) + let fields = self.table.get_mut(&fields)?; + fields.remove_all(header)?; + Ok(()) } fn append( &mut self, - fields: Resource, + fields: Resource, name: String, value: Vec, - ) -> wasmtime::Result> { - let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) { - Ok(header) => header, - Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), - }; + ) -> HeaderResult<()> { + let header = HeaderName::from_bytes(name.as_bytes())?; - if self.is_forbidden_header(&header) { - return Ok(Err(types::HeaderError::Forbidden)); + if self.hooks.is_forbidden_header(&header) { + return Err(types::HeaderError::Forbidden.into()); } - let value = match hyper::header::HeaderValue::from_bytes(&value) { - Ok(value) => value, - Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)), - }; + let value = HeaderValue::from_bytes(&value)?; - match get_fields_mut(self.table(), &fields) - .context("[fields_append] getting mutable fields")? - { - Ok(fields) => { - fields.append(&header, value)?; - Ok(Ok(())) - } - Err(e) => Ok(Err(e)), - } + let fields = self.table.get_mut(&fields)?; + fields.append(header, value)?; + Ok(()) } - fn entries( - &mut self, - fields: Resource, - ) -> wasmtime::Result)>> { - Ok(get_fields(self.table(), &fields)? - .as_ref() + fn entries(&mut self, fields: Resource) -> wasmtime::Result)>> { + Ok(self + .table + .get(&fields)? .iter() .map(|(name, value)| (name.as_str().to_owned(), value.as_bytes().to_owned())) .collect()) } - fn clone(&mut self, fields: Resource) -> wasmtime::Result> { - let fields = get_fields(self.table(), &fields) - .context("[fields_clone] getting fields")? - .clone(); - - let id = self - .table() - .push(HostFields::Owned { fields }) - .context("[fields_clone] pushing fields")?; - + fn clone(&mut self, fields: Resource) -> wasmtime::Result> { + let mut fields = self.table.get(&fields)?.clone(); + fields.set_mutable(self.ctx.field_size_limit); + let id = self.table.push(fields)?; Ok(id) } } -impl crate::bindings::http::types::HostIncomingRequest for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostIncomingRequest for WasiHttpCtxView<'_> { fn method(&mut self, id: Resource) -> wasmtime::Result { - let method = self.table().get(&id)?.method.clone(); + let method = self.table.get(&id)?.method.clone(); Ok(method.into()) } fn path_with_query( &mut self, id: Resource, ) -> wasmtime::Result> { - let req = self.table().get(&id)?; + let req = self.table.get(&id)?; Ok(req .uri .path_and_query() .map(|path_and_query| path_and_query.as_str().to_owned())) } fn scheme(&mut self, id: Resource) -> wasmtime::Result> { - let req = self.table().get(&id)?; + let req = self.table.get(&id)?; Ok(Some(req.scheme.clone())) } fn authority(&mut self, id: Resource) -> wasmtime::Result> { - let req = self.table().get(&id)?; + let req = self.table.get(&id)?; Ok(Some(req.authority.clone())) } fn headers( &mut self, id: Resource, - ) -> wasmtime::Result> { - let _ = self.table().get(&id)?; - - fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { - &mut elem.downcast_mut::().unwrap().headers - } - - let headers = self.table().push_child( - HostFields::Ref { - parent: id.rep(), - get_fields, - }, - &id, - )?; - - Ok(headers) + ) -> wasmtime::Result> { + let req = self.table.get(&id)?; + Ok(self.table.push(req.headers.clone())?) } fn consume( &mut self, id: Resource, ) -> wasmtime::Result, ()>> { - let req = self.table().get_mut(&id)?; + let req = self.table.get_mut(&id)?; match req.body.take() { Some(body) => { - let id = self.table().push(body)?; + let id = self.table.push(body)?; Ok(Ok(id)) } @@ -350,22 +213,20 @@ where } fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { - let _ = self.table().delete(id)?; + let _ = self.table.delete(id)?; Ok(()) } } -impl crate::bindings::http::types::HostOutgoingRequest for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostOutgoingRequest for WasiHttpCtxView<'_> { fn new( &mut self, - headers: Resource, + headers: Resource, ) -> wasmtime::Result> { - let headers = move_fields(self.table(), headers)?; + let mut headers = self.table.delete(headers)?; + headers.set_immutable(); - self.table() + self.table .push(HostOutgoingRequest { path_with_query: None, authority: None, @@ -381,10 +242,10 @@ where &mut self, request: Resource, ) -> wasmtime::Result, ()>> { - let buffer_chunks = self.outgoing_body_buffer_chunks(); - let chunk_size = self.outgoing_body_chunk_size(); + let buffer_chunks = self.hooks.outgoing_body_buffer_chunks(); + let chunk_size = self.hooks.outgoing_body_chunk_size(); let req = self - .table() + .table .get_mut(&request) .context("[outgoing_request_write] getting request")?; @@ -392,7 +253,7 @@ where return Ok(Err(())); } - let size = match get_content_length(req.headers.as_ref()) { + let size = match get_content_length(&req.headers) { Ok(size) => size, Err(..) => return Ok(Err(())), }; @@ -404,13 +265,13 @@ where // The output stream will necessarily outlive the request, because we could be still // writing to the stream after `outgoing-handler.handle` is called. - let outgoing_body = self.table().push(host_body)?; + let outgoing_body = self.table.push(host_body)?; Ok(Ok(outgoing_body)) } fn drop(&mut self, request: Resource) -> wasmtime::Result<()> { - let _ = self.table().delete(request)?; + let _ = self.table.delete(request)?; Ok(()) } @@ -418,7 +279,7 @@ where &mut self, request: wasmtime::component::Resource, ) -> wasmtime::Result { - Ok(self.table().get(&request)?.method.clone()) + Ok(self.table.get(&request)?.method.clone()) } fn set_method( @@ -426,7 +287,7 @@ where request: wasmtime::component::Resource, method: Method, ) -> wasmtime::Result> { - let req = self.table().get_mut(&request)?; + let req = self.table.get_mut(&request)?; if let Method::Other(s) = &method { if let Err(_) = http::Method::from_str(s) { @@ -443,7 +304,7 @@ where &mut self, request: wasmtime::component::Resource, ) -> wasmtime::Result> { - Ok(self.table().get(&request)?.path_with_query.clone()) + Ok(self.table.get(&request)?.path_with_query.clone()) } fn set_path_with_query( @@ -451,7 +312,7 @@ where request: wasmtime::component::Resource, path_with_query: Option, ) -> wasmtime::Result> { - let req = self.table().get_mut(&request)?; + let req = self.table.get_mut(&request)?; if let Some(s) = path_with_query.as_ref() { if let Err(_) = http::uri::PathAndQuery::from_str(s) { @@ -468,7 +329,7 @@ where &mut self, request: wasmtime::component::Resource, ) -> wasmtime::Result> { - Ok(self.table().get(&request)?.scheme.clone()) + Ok(self.table.get(&request)?.scheme.clone()) } fn set_scheme( @@ -476,7 +337,7 @@ where request: wasmtime::component::Resource, scheme: Option, ) -> wasmtime::Result> { - let req = self.table().get_mut(&request)?; + let req = self.table.get_mut(&request)?; if let Some(types::Scheme::Other(s)) = scheme.as_ref() { if let Err(_) = http::uri::Scheme::from_str(s.as_str()) { @@ -493,7 +354,7 @@ where &mut self, request: wasmtime::component::Resource, ) -> wasmtime::Result> { - Ok(self.table().get(&request)?.authority.clone()) + Ok(self.table.get(&request)?.authority.clone()) } fn set_authority( @@ -501,7 +362,7 @@ where request: wasmtime::component::Resource, authority: Option, ) -> wasmtime::Result> { - let req = self.table().get_mut(&request)?; + let req = self.table.get_mut(&request)?; if let Some(s) = authority.as_ref() { if let Err(_) = http::uri::Authority::from_str(s.as_str()) { @@ -517,37 +378,16 @@ where fn headers( &mut self, request: wasmtime::component::Resource, - ) -> wasmtime::Result> { - let _ = self - .table() - .get(&request) - .context("[outgoing_request_headers] getting request")?; - - fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { - &mut elem - .downcast_mut::() - .unwrap() - .headers - } - - let id = self.table().push_child( - HostFields::Ref { - parent: request.rep(), - get_fields, - }, - &request, - )?; - + ) -> wasmtime::Result> { + let req = self.table.get(&request)?; + let id = self.table.push(req.headers.clone())?; Ok(id) } } -impl crate::bindings::http::types::HostResponseOutparam for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostResponseOutparam for WasiHttpCtxView<'_> { fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { - let _ = self.table().delete(id)?; + let _ = self.table.delete(id)?; Ok(()) } fn set( @@ -556,11 +396,11 @@ where resp: Result, types::ErrorCode>, ) -> wasmtime::Result<()> { let val = match resp { - Ok(resp) => Ok(self.table().delete(resp)?.try_into()?), + Ok(resp) => Ok(self.table.delete(resp)?.try_into()?), Err(e) => Err(e), }; - let resp = self.table().delete(id)?; + let resp = self.table.delete(id)?; // Giving the API doesn't return any error, it's probably // better to ignore the error than trap the guest, in case of // host timeout and dropped the receiver side of the channel. @@ -573,19 +413,16 @@ where &mut self, _id: Resource, _status: u16, - _headers: Resource, + _headers: Resource, ) -> HttpResult<()> { Err(HttpError::trap(format_err!("not implemented"))) } } -impl crate::bindings::http::types::HostIncomingResponse for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostIncomingResponse for WasiHttpCtxView<'_> { fn drop(&mut self, response: Resource) -> wasmtime::Result<()> { let _ = self - .table() + .table .delete(response) .context("[drop_incoming_response] deleting response")?; Ok(()) @@ -593,7 +430,7 @@ where fn status(&mut self, response: Resource) -> wasmtime::Result { let r = self - .table() + .table .get(&response) .context("[incoming_response_status] getting response")?; Ok(r.status) @@ -602,24 +439,9 @@ where fn headers( &mut self, response: Resource, - ) -> wasmtime::Result> { - let _ = self - .table() - .get(&response) - .context("[incoming_response_headers] getting response")?; - - fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { - &mut elem.downcast_mut::().unwrap().headers - } - - let id = self.table().push_child( - HostFields::Ref { - parent: response.rep(), - get_fields, - }, - &response, - )?; - + ) -> wasmtime::Result> { + let resp = self.table.get(&response)?; + let id = self.table.push(resp.headers.clone())?; Ok(id) } @@ -627,14 +449,14 @@ where &mut self, response: Resource, ) -> wasmtime::Result, ()>> { - let table = self.table(); - let r = table + let r = self + .table .get_mut(&response) .context("[incoming_response_consume] getting response")?; match r.body.take() { Some(body) => { - let id = self.table().push(body)?; + let id = self.table.push(body)?; Ok(Ok(id)) } @@ -643,13 +465,10 @@ where } } -impl crate::bindings::http::types::HostFutureTrailers for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostFutureTrailers for WasiHttpCtxView<'_> { fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { let _ = self - .table() + .table .delete(id) .context("[drop future-trailers] deleting future-trailers")?; Ok(()) @@ -659,7 +478,7 @@ where &mut self, index: Resource, ) -> wasmtime::Result> { - wasmtime_wasi::p2::subscribe(self.table(), index) + wasmtime_wasi::p2::subscribe(self.table, index) } fn get( @@ -667,7 +486,7 @@ where id: Resource, ) -> wasmtime::Result>, types::ErrorCode>, ()>>> { - let trailers = self.table().get_mut(&id)?; + let trailers = self.table.get_mut(&id)?; match trailers { HostFutureTrailers::Waiting { .. } => return Ok(None), HostFutureTrailers::Consumed => return Ok(Some(Err(()))), @@ -685,27 +504,24 @@ where Err(e) => return Ok(Some(Ok(Err(e)))), }; - remove_forbidden_headers(self, &mut fields); + remove_forbidden_headers(self.hooks, &mut fields); - let ts = self.table().push(HostFields::Owned { fields })?; + let ts = self.table.push(FieldMap::new_immutable(fields))?; Ok(Some(Ok(Ok(Some(ts))))) } } -impl crate::bindings::http::types::HostIncomingBody for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostIncomingBody for WasiHttpCtxView<'_> { fn stream( &mut self, id: Resource, ) -> wasmtime::Result, ()>> { - let body = self.table().get_mut(&id)?; + let body = self.table.get_mut(&id)?; if let Some(stream) = body.take_stream() { let stream: DynInputStream = Box::new(stream); - let stream = self.table().push_child(stream, &id)?; + let stream = self.table.push_child(stream, &id)?; return Ok(Ok(stream)); } @@ -716,28 +532,26 @@ where &mut self, id: Resource, ) -> wasmtime::Result> { - let body = self.table().delete(id)?; - let trailers = self.table().push(body.into_future_trailers())?; + let body = self.table.delete(id)?; + let trailers = self.table.push(body.into_future_trailers())?; Ok(trailers) } fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { - let _ = self.table().delete(id)?; + let _ = self.table.delete(id)?; Ok(()) } } -impl crate::bindings::http::types::HostOutgoingResponse for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostOutgoingResponse for WasiHttpCtxView<'_> { fn new( &mut self, - headers: Resource, + headers: Resource, ) -> wasmtime::Result> { - let fields = move_fields(self.table(), headers)?; + let mut fields = self.table.delete(headers)?; + fields.set_immutable(); - let id = self.table().push(HostOutgoingResponse { + let id = self.table.push(HostOutgoingResponse { status: http::StatusCode::OK, headers: fields, body: None, @@ -750,15 +564,15 @@ where &mut self, id: Resource, ) -> wasmtime::Result, ()>> { - let buffer_chunks = self.outgoing_body_buffer_chunks(); - let chunk_size = self.outgoing_body_chunk_size(); - let resp = self.table().get_mut(&id)?; + let buffer_chunks = self.hooks.outgoing_body_buffer_chunks(); + let chunk_size = self.hooks.outgoing_body_chunk_size(); + let resp = self.table.get_mut(&id)?; if resp.body.is_some() { return Ok(Err(())); } - let size = match get_content_length(resp.headers.as_ref()) { + let size = match get_content_length(&resp.headers) { Ok(size) => size, Err(..) => return Ok(Err(())), }; @@ -768,7 +582,7 @@ where resp.body.replace(body); - let id = self.table().push(host)?; + let id = self.table.push(host)?; Ok(Ok(id)) } @@ -777,7 +591,7 @@ where &mut self, id: Resource, ) -> wasmtime::Result { - Ok(self.table().get(&id)?.status.into()) + Ok(self.table.get(&id)?.status.into()) } fn set_status_code( @@ -785,7 +599,7 @@ where id: Resource, status: types::StatusCode, ) -> wasmtime::Result> { - let resp = self.table().get_mut(&id)?; + let resp = self.table.get_mut(&id)?; match http::StatusCode::from_u16(status) { Ok(status) => resp.status = status, @@ -798,36 +612,20 @@ where fn headers( &mut self, id: Resource, - ) -> wasmtime::Result> { - // Trap if the outgoing-response doesn't exist. - let _ = self.table().get(&id)?; - - fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { - let resp = elem.downcast_mut::().unwrap(); - &mut resp.headers - } - - Ok(self.table().push_child( - HostFields::Ref { - parent: id.rep(), - get_fields, - }, - &id, - )?) + ) -> wasmtime::Result> { + let resp = self.table.get(&id)?; + Ok(self.table.push(resp.headers.clone())?) } fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { - let _ = self.table().delete(id)?; + let _ = self.table.delete(id)?; Ok(()) } } -impl crate::bindings::http::types::HostFutureIncomingResponse for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostFutureIncomingResponse for WasiHttpCtxView<'_> { fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { - let _ = self.table().delete(id)?; + let _ = self.table.delete(id)?; Ok(()) } @@ -837,8 +635,7 @@ where ) -> wasmtime::Result< Option, types::ErrorCode>, ()>>, > { - let field_size_limit = self.ctx().field_size_limit; - let resp = self.table().get_mut(&id)?; + let resp = self.table.get_mut(&id)?; match resp { HostFutureIncomingResponse::Pending(_) => return Ok(None), @@ -858,17 +655,15 @@ where Ok(Err(e)) => return Ok(Some(Ok(Err(e)))), }; - let (parts, body) = resp.resp.into_parts(); + let (mut parts, body) = resp.resp.into_parts(); + remove_forbidden_headers(self.hooks, &mut parts.headers); + let headers = FieldMap::new_immutable(parts.headers); - let mut headers = FieldMap::new(parts.headers, field_size_limit); - remove_forbidden_headers(self, &mut headers); - - let resp = self.table().push(HostIncomingResponse { + let resp = self.table.push(HostIncomingResponse { status: parts.status.as_u16(), headers, body: Some({ - let mut body = - HostIncomingBody::new(body, resp.between_bytes_timeout, field_size_limit); + let mut body = HostIncomingBody::new(body, resp.between_bytes_timeout); if let Some(worker) = resp.worker { body.retain_worker(worker); } @@ -883,21 +678,18 @@ where &mut self, id: Resource, ) -> wasmtime::Result> { - wasmtime_wasi::p2::subscribe(self.table(), id) + wasmtime_wasi::p2::subscribe(self.table, id) } } -impl crate::bindings::http::types::HostOutgoingBody for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostOutgoingBody for WasiHttpCtxView<'_> { fn write( &mut self, id: Resource, ) -> wasmtime::Result, ()>> { - let body = self.table().get_mut(&id)?; + let body = self.table.get_mut(&id)?; if let Some(stream) = body.take_output_stream() { - let id = self.table().push_child(stream, &id)?; + let id = self.table.push_child(stream, &id)?; Ok(Ok(id)) } else { Ok(Err(())) @@ -908,11 +700,11 @@ where &mut self, id: Resource, ts: Option>, - ) -> crate::HttpResult<()> { - let body = self.table().delete(id)?; + ) -> HttpResult<()> { + let body = self.table.delete(id)?; let ts = if let Some(ts) = ts { - Some(move_fields(self.table(), ts)?) + Some(self.table.delete(ts)?) } else { None }; @@ -922,17 +714,14 @@ where } fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { - self.table().delete(id)?.abort(); + self.table.delete(id)?.abort(); Ok(()) } } -impl crate::bindings::http::types::HostRequestOptions for WasiHttpImpl -where - T: WasiHttpView, -{ +impl types::HostRequestOptions for WasiHttpCtxView<'_> { fn new(&mut self) -> wasmtime::Result> { - let id = self.table().push(types::RequestOptions::default())?; + let id = self.table.push(types::RequestOptions::default())?; Ok(id) } @@ -940,11 +729,7 @@ where &mut self, opts: Resource, ) -> wasmtime::Result> { - let nanos = self - .table() - .get(&opts)? - .connect_timeout - .map(|d| d.as_nanos()); + let nanos = self.table.get(&opts)?.connect_timeout.map(|d| d.as_nanos()); if let Some(nanos) = nanos { Ok(Some(nanos.try_into()?)) @@ -958,8 +743,7 @@ where opts: Resource, duration: Option, ) -> wasmtime::Result> { - self.table().get_mut(&opts)?.connect_timeout = - duration.map(std::time::Duration::from_nanos); + self.table.get_mut(&opts)?.connect_timeout = duration.map(std::time::Duration::from_nanos); Ok(Ok(())) } @@ -968,7 +752,7 @@ where opts: Resource, ) -> wasmtime::Result> { let nanos = self - .table() + .table .get(&opts)? .first_byte_timeout .map(|d| d.as_nanos()); @@ -985,7 +769,7 @@ where opts: Resource, duration: Option, ) -> wasmtime::Result> { - self.table().get_mut(&opts)?.first_byte_timeout = + self.table.get_mut(&opts)?.first_byte_timeout = duration.map(std::time::Duration::from_nanos); Ok(Ok(())) } @@ -995,7 +779,7 @@ where opts: Resource, ) -> wasmtime::Result> { let nanos = self - .table() + .table .get(&opts)? .between_bytes_timeout .map(|d| d.as_nanos()); @@ -1012,13 +796,13 @@ where opts: Resource, duration: Option, ) -> wasmtime::Result> { - self.table().get_mut(&opts)?.between_bytes_timeout = + self.table.get_mut(&opts)?.between_bytes_timeout = duration.map(std::time::Duration::from_nanos); Ok(Ok(())) } fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - let _ = self.table().delete(rep)?; + let _ = self.table.delete(rep)?; Ok(()) } } diff --git a/crates/wasi-http/src/p3/bindings.rs b/crates/wasi-http/src/p3/bindings.rs index 307d3ff3480a..2e7804ce72fe 100644 --- a/crates/wasi-http/src/p3/bindings.rs +++ b/crates/wasi-http/src/p3/bindings.rs @@ -30,7 +30,7 @@ mod generated { }); mod with { - pub type Fields = crate::p3::MaybeMutable; + pub type Fields = crate::FieldMap; pub type RequestOptions = crate::p3::MaybeMutable; } } diff --git a/crates/wasi-http/src/p3/body.rs b/crates/wasi-http/src/p3/body.rs index 79e27a4f4751..37f96c0579f9 100644 --- a/crates/wasi-http/src/p3/body.rs +++ b/crates/wasi-http/src/p3/body.rs @@ -1,11 +1,11 @@ -use crate::p3::bindings::http::types::{ErrorCode, Fields, Trailers}; +use crate::FieldMap; +use crate::p3::bindings::http::types::{ErrorCode, Trailers}; use crate::p3::{WasiHttp, WasiHttpCtxView}; use bytes::Bytes; use core::iter; use core::num::NonZeroUsize; use core::pin::Pin; use core::task::{Context, Poll, ready}; -use http::HeaderMap; use http_body::Body as _; use http_body_util::combinators::UnsyncBoxBody; use std::any::{Any, TypeId}; @@ -259,7 +259,7 @@ impl StreamConsumer for UnlimitedGuestBodyConsumer { /// [http_body::Body] implementation for bodies originating in the guest. pub(crate) struct GuestBody { contents_rx: Option>>, - trailers_rx: Option>, ErrorCode>>>, + trailers_rx: Option>, ErrorCode>>>, content_length: Option, } @@ -364,7 +364,7 @@ impl http_body::Body for GuestBody { self.trailers_rx = None; match res { Ok(Ok(Some(trailers))) => Poll::Ready(Some(Ok(http_body::Frame::trailers( - Arc::unwrap_or_clone(trailers), + Arc::unwrap_or_clone(trailers).into(), )))), Ok(Ok(None)) => Poll::Ready(None), Ok(Err(err)) => Poll::Ready(Some(Err(err))), @@ -404,7 +404,7 @@ impl http_body::Body for GuestBody { /// [FutureConsumer] implementation for trailers originating in the guest. struct GuestTrailerConsumer { - tx: Option>, ErrorCode>>>, + tx: Option>, ErrorCode>>>, getter: fn(&mut T) -> WasiHttpCtxView<'_>, } @@ -484,55 +484,67 @@ where if self.body.is_end_stream() { break 'result Ok(None); } else { - return Poll::Ready(Ok(StreamResult::Completed)); + // Destination has zero capacity but the body could still have data. Cannot return + // `Completed` (nothing was produced — violates the StreamProducer contract). Fall + // through to poll the body with `cap = None` so it buffers the frame. + None } } None => None, }; - match Pin::new(&mut self.body).poll_frame(cx) { - Poll::Ready(Some(Ok(frame))) => { - match frame.into_data().map_err(http_body::Frame::into_trailers) { - Ok(mut frame) => { - // Libraries like `Reqwest` generate a 0-length frame after sensing end-of-stream, - // so we have to check for the body's end-of-stream indicator here too - if frame.len() == 0 && self.body.is_end_stream() { - break 'result Ok(None); - } + loop { + match Pin::new(&mut self.body).poll_frame(cx) { + Poll::Ready(Some(Ok(frame))) => { + match frame.into_data().map_err(http_body::Frame::into_trailers) { + Ok(mut frame) => { + if frame.len() == 0 { + // Zero-length data frames are valid per the http_body::Body + // trait contract and RFC 9113 §6.1 — skip and re-poll + // directly rather than using `wake_by_ref()` + Pending, which + // would just re-enter this function via the task queue + if self.body.is_end_stream() { + break 'result Ok(None); + } + continue; + } - if let Some(cap) = cap { - let n = frame.len(); - let cap = cap.into(); - if n > cap { - // data frame does not fit in destination, fill it and buffer the rest - dst.set_buffer(Cursor::new(frame.split_off(cap))); - let mut dst = dst.as_direct(store, cap); - dst.remaining().copy_from_slice(&frame); - dst.mark_written(cap); + if let Some(cap) = cap { + let n = frame.len(); + let cap = cap.into(); + if n > cap { + // data frame does not fit in destination, fill it and buffer the rest + dst.set_buffer(Cursor::new(frame.split_off(cap))); + let mut dst = dst.as_direct(store, cap); + dst.remaining().copy_from_slice(&frame); + dst.mark_written(cap); + } else { + // copy the whole frame into the destination + let mut dst = dst.as_direct(store, n); + dst.remaining()[..n].copy_from_slice(&frame); + dst.mark_written(n); + } } else { - // copy the whole frame into the destination - let mut dst = dst.as_direct(store, n); - dst.remaining()[..n].copy_from_slice(&frame); - dst.mark_written(n); + dst.set_buffer(Cursor::new(frame)); } - } else { - dst.set_buffer(Cursor::new(frame)); + return Poll::Ready(Ok(StreamResult::Completed)); } - return Poll::Ready(Ok(StreamResult::Completed)); - } - Err(Ok(trailers)) => { - let trailers = (self.getter)(store.data_mut()) - .table - .push(Fields::new_mutable(trailers)) - .context("failed to push trailers to table")?; - break 'result Ok(Some(trailers)); + Err(Ok(trailers)) => { + let view = (self.getter)(store.data_mut()); + let trailers = FieldMap::new_immutable(trailers); + let trailers = view + .table + .push(trailers) + .context("failed to push trailers to table")?; + break 'result Ok(Some(trailers)); + } + Err(Err(..)) => break 'result Err(ErrorCode::HttpProtocolError), } - Err(Err(..)) => break 'result Err(ErrorCode::HttpProtocolError), } + Poll::Ready(Some(Err(err))) => break 'result Err(err), + Poll::Ready(None) => break 'result Ok(None), + Poll::Pending if finish => return Poll::Ready(Ok(StreamResult::Cancelled)), + Poll::Pending => return Poll::Pending, } - Poll::Ready(Some(Err(err))) => break 'result Err(err), - Poll::Ready(None) => break 'result Ok(None), - Poll::Pending if finish => return Poll::Ready(Ok(StreamResult::Cancelled)), - Poll::Pending => return Poll::Pending, } }; self.close(res); diff --git a/crates/wasi-http/src/p3/host/handler.rs b/crates/wasi-http/src/p3/host/handler.rs index 8e57d898257b..ae49bfdfd842 100644 --- a/crates/wasi-http/src/p3/host/handler.rs +++ b/crates/wasi-http/src/p3/host/handler.rs @@ -1,3 +1,4 @@ +use crate::FieldMap; use crate::p3::bindings::http::client::{Host, HostWithStore}; use crate::p3::bindings::http::types::{ErrorCode, Request, Response}; use crate::p3::body::{Body, BodyExt as _}; @@ -58,7 +59,7 @@ impl HostWithStore for WasiHttp { .map_err(HttpError::trap)?; let (req, options) = req.into_http_with_getter(&mut store, io_task_result(io_result_rx), getter)?; - HttpResult::Ok(store.get().ctx.send_request( + HttpResult::Ok(store.get().hooks.send_request( req.map(|body| body.with_state(io_task_rx).boxed_unsync()), options.as_deref().copied(), Box::new(async { @@ -97,7 +98,7 @@ impl HostWithStore for WasiHttp { }; let res = Response { status, - headers: Arc::new(headers), + headers: FieldMap::new_immutable(headers), body: Body::Host { body, result_tx: res_result_tx, diff --git a/crates/wasi-http/src/p3/host/types.rs b/crates/wasi-http/src/p3/host/types.rs index ff7f397f7665..aa48d265526c 100644 --- a/crates/wasi-http/src/p3/host/types.rs +++ b/crates/wasi-http/src/p3/host/types.rs @@ -1,3 +1,4 @@ +use crate::FieldMap; use crate::p3::bindings::clocks::monotonic_clock::Duration; use crate::p3::bindings::http::types::{ ErrorCode, FieldName, FieldValue, Fields, HeaderError, Headers, Host, HostFields, HostRequest, @@ -42,9 +43,15 @@ fn push_fields(table: &mut ResourceTable, fields: Fields) -> wasmtime::Result) -> wasmtime::Result { - table + let mut fields = table .delete(fields) - .context("failed to delete fields from table") + .context("failed to delete fields from table")?; + // When fields are passed by ownership to the host that flags them as + // immutable within `wasi:http`, and this semantically means that putting + // fields in a request, then getting them back out, will return an immutable + // view of the headers rather than mutable for example. + fields.set_immutable(); + Ok(fields) } fn get_request<'a>( @@ -179,24 +186,23 @@ impl FutureProducer for GuestBodyResultProducer { impl HostFields for WasiHttpCtxView<'_> { fn new(&mut self) -> wasmtime::Result> { - push_fields(self.table, Fields::new_mutable_default()) + push_fields(self.table, FieldMap::new_mutable(self.ctx.field_size_limit)) } fn from_list( &mut self, entries: Vec<(FieldName, FieldValue)>, ) -> HeaderResult> { - let mut fields = http::HeaderMap::default(); + let mut fields = FieldMap::new_mutable(self.ctx.field_size_limit); for (name, value) in entries { let name = name.parse().or(Err(HeaderError::InvalidSyntax))?; - if self.ctx.is_forbidden_header(&name) { + if self.hooks.is_forbidden_header(&name) { return Err(HeaderError::Forbidden.into()); } let value = parse_header_value(&name, value)?; - fields.append(name, value); + fields.append(name, value)?; } - let fields = push_fields(self.table, Fields::new_mutable(fields)) - .map_err(crate::p3::HeaderError::trap)?; + let fields = push_fields(self.table, fields).map_err(crate::p3::HeaderError::trap)?; Ok(fields) } @@ -224,8 +230,8 @@ impl HostFields for WasiHttpCtxView<'_> { name: FieldName, value: Vec, ) -> HeaderResult<()> { - let name = name.parse().or(Err(HeaderError::InvalidSyntax))?; - if self.ctx.is_forbidden_header(&name) { + let name = name.parse().map_err(|_| HeaderError::InvalidSyntax)?; + if self.hooks.is_forbidden_header(&name) { return Err(HeaderError::Forbidden.into()); } let mut values = Vec::with_capacity(value.len()); @@ -233,23 +239,16 @@ impl HostFields for WasiHttpCtxView<'_> { let value = parse_header_value(&name, value)?; values.push(value); } - let fields = get_fields_mut(self.table, &fields)?; - let fields = fields.get_mut().ok_or(HeaderError::Immutable)?; - fields.remove(&name); - for value in values { - fields.append(&name, value); - } + get_fields_mut(self.table, &fields)?.set(name, values)?; Ok(()) } fn delete(&mut self, fields: Resource, name: FieldName) -> HeaderResult<()> { - let name = name.parse().or(Err(HeaderError::InvalidSyntax))?; - if self.ctx.is_forbidden_header(&name) { + let name = name.parse().map_err(|_| HeaderError::InvalidSyntax)?; + if self.hooks.is_forbidden_header(&name) { return Err(HeaderError::Forbidden.into()); } - let fields = get_fields_mut(self.table, &fields)?; - let fields = fields.get_mut().ok_or(HeaderError::Immutable)?; - fields.remove(&name); + get_fields_mut(self.table, &fields)?.remove_all(name)?; Ok(()) } @@ -259,15 +258,12 @@ impl HostFields for WasiHttpCtxView<'_> { name: FieldName, ) -> HeaderResult> { let name = name.parse().or(Err(HeaderError::InvalidSyntax))?; - if self.ctx.is_forbidden_header(&name) { + if self.hooks.is_forbidden_header(&name) { return Err(HeaderError::Forbidden.into()); } - let fields = get_fields_mut(self.table, &fields)?; - let fields = fields.get_mut().ok_or(HeaderError::Immutable)?; - let http::header::Entry::Occupied(entry) = fields.entry(name) else { - return Ok(Vec::default()); - }; - let (.., values) = entry.remove_entry_mult(); + let values = get_fields_mut(self.table, &fields)? + .remove_all(name)? + .into_iter(); Ok(values.map(|value| value.as_bytes().into()).collect()) } @@ -278,13 +274,11 @@ impl HostFields for WasiHttpCtxView<'_> { value: FieldValue, ) -> HeaderResult<()> { let name = name.parse().or(Err(HeaderError::InvalidSyntax))?; - if self.ctx.is_forbidden_header(&name) { + if self.hooks.is_forbidden_header(&name) { return Err(HeaderError::Forbidden.into()); } let value = parse_header_value(&name, value)?; - let fields = get_fields_mut(self.table, &fields)?; - let fields = fields.get_mut().ok_or(HeaderError::Immutable)?; - fields.append(name, value); + get_fields_mut(self.table, &fields)?.append(name, value)?; Ok(()) } @@ -301,8 +295,9 @@ impl HostFields for WasiHttpCtxView<'_> { } fn clone(&mut self, fields: Resource) -> wasmtime::Result> { - let fields = get_fields(self.table, &fields)?; - push_fields(self.table, Fields::new_mutable(Arc::clone(fields))) + let mut fields = get_fields(self.table, &fields)?.clone(); + fields.set_mutable(self.ctx.field_size_limit); + push_fields(self.table, fields) } fn drop(&mut self, fields: Resource) -> wasmtime::Result<()> { @@ -348,7 +343,7 @@ impl HostRequestWithStore for WasiHttp { scheme: None, authority: None, path_with_query: None, - headers: headers.into(), + headers, options: options.map(Into::into), body, }; @@ -496,7 +491,7 @@ impl HostRequest for WasiHttpCtxView<'_> { fn get_headers(&mut self, req: Resource) -> wasmtime::Result> { let Request { headers, .. } = get_request(self.table, &req)?; - push_fields(self.table, Fields::new_immutable(Arc::clone(headers))) + push_fields(self.table, headers.clone()) } } @@ -624,7 +619,7 @@ impl HostResponseWithStore for WasiHttp { let headers = delete_fields(table, headers)?; let res = Response { status: http::StatusCode::OK, - headers: headers.into(), + headers, body, }; let res = table @@ -687,7 +682,7 @@ impl HostResponse for WasiHttpCtxView<'_> { fn get_headers(&mut self, res: Resource) -> wasmtime::Result> { let Response { headers, .. } = get_response(self.table, &res)?; - push_fields(self.table, Fields::new_immutable(Arc::clone(headers))) + push_fields(self.table, headers.clone()) } } diff --git a/crates/wasi-http/src/p3/mod.rs b/crates/wasi-http/src/p3/mod.rs index d8af50b68533..153956fe012c 100644 --- a/crates/wasi-http/src/p3/mod.rs +++ b/crates/wasi-http/src/p3/mod.rs @@ -22,7 +22,7 @@ pub use request::{Request, RequestOptions}; pub use response::Response; use crate::p3::bindings::http::types::ErrorCode; -use crate::types::DEFAULT_FORBIDDEN_HEADERS; +use crate::{DEFAULT_FORBIDDEN_HEADERS, FieldMapError, WasiHttpCtx}; use bindings::http::{client, types}; use bytes::Bytes; use core::ops::Deref; @@ -39,6 +39,18 @@ pub(crate) type HttpError = TrappableError; pub(crate) type HeaderResult = Result; pub(crate) type HeaderError = TrappableError; +impl From for HeaderError { + fn from(e: FieldMapError) -> Self { + match e { + FieldMapError::Immutable => types::HeaderError::Immutable.into(), + FieldMapError::InvalidHeaderName => types::HeaderError::InvalidSyntax.into(), + FieldMapError::TooManyFields | FieldMapError::TotalSizeTooBig => { + types::HeaderError::SizeExceeded.into() + } + } + } +} + pub(crate) type RequestOptionsResult = Result; pub(crate) type RequestOptionsError = TrappableError; @@ -50,7 +62,7 @@ impl HasData for WasiHttp { } /// A trait which provides internal WASI HTTP state. -pub trait WasiHttpCtx: Send { +pub trait WasiHttpHooks: Send { /// Whether a given header should be considered forbidden and not allowed. fn is_forbidden_header(&mut self, name: &HeaderName) -> bool { DEFAULT_FORBIDDEN_HEADERS.contains(name) @@ -150,21 +162,35 @@ pub trait WasiHttpCtx: Send { >; } -/// Default implementation of [WasiHttpCtx]. #[cfg(feature = "default-send-request")] -#[derive(Clone, Default)] -pub struct DefaultWasiHttpCtx; +impl<'a> Default for &'a mut dyn WasiHttpHooks { + fn default() -> Self { + let x: &mut [(); 0] = &mut []; + x + } +} + +#[doc(hidden)] +#[cfg(feature = "default-send-request")] +impl WasiHttpHooks for [(); 0] {} +/// Returns a value suitable for the `WasiHttpCtxView::hooks` field which has +/// the default behavior for `wasi:http`. #[cfg(feature = "default-send-request")] -impl WasiHttpCtx for DefaultWasiHttpCtx {} +pub fn default_hooks() -> &'static mut dyn WasiHttpHooks { + Default::default() +} /// View into [WasiHttpCtx] implementation and [ResourceTable]. pub struct WasiHttpCtxView<'a> { - /// Mutable reference to the WASI HTTP context. - pub ctx: &'a mut dyn WasiHttpCtx, + /// Mutable reference to the WASI HTTP hooks. + pub hooks: &'a mut dyn WasiHttpHooks, /// Mutable reference to table used to manage resources. pub table: &'a mut ResourceTable, + + /// Mutable reference to the WASI HTTP context. + pub ctx: &'a mut WasiHttpCtx, } /// A trait which provides internal WASI HTTP state. @@ -184,7 +210,7 @@ pub trait WasiHttpView: Send { /// ``` /// use wasmtime::{Engine, Result, Store, Config}; /// use wasmtime::component::{Linker, ResourceTable}; -/// use wasmtime_wasi_http::p3::{DefaultWasiHttpCtx, WasiHttpCtxView, WasiHttpView}; +/// use wasmtime_wasi_http::{WasiHttpCtx, p3::{WasiHttpCtxView, WasiHttpView}}; /// /// fn main() -> Result<()> { /// let mut config = Config::new(); @@ -207,7 +233,7 @@ pub trait WasiHttpView: Send { /// /// #[derive(Default)] /// struct MyState { -/// http: DefaultWasiHttpCtx, +/// http: WasiHttpCtx, /// table: ResourceTable, /// } /// @@ -216,6 +242,7 @@ pub trait WasiHttpView: Send { /// WasiHttpCtxView { /// ctx: &mut self.http, /// table: &mut self.table, +/// hooks: Default::default(), /// } /// } /// } diff --git a/crates/wasi-http/src/p3/request.rs b/crates/wasi-http/src/p3/request.rs index e0671fc0dba2..aa3ed706abbb 100644 --- a/crates/wasi-http/src/p3/request.rs +++ b/crates/wasi-http/src/p3/request.rs @@ -1,12 +1,12 @@ -use crate::get_content_length; use crate::p3::bindings::http::types::ErrorCode; use crate::p3::body::{Body, BodyExt as _, GuestBody}; use crate::p3::{HttpError, HttpResult, WasiHttpCtxView, WasiHttpView}; +use crate::{FieldMap, get_content_length}; use bytes::Bytes; use core::time::Duration; use http::header::HOST; use http::uri::{Authority, PathAndQuery, Scheme}; -use http::{HeaderMap, HeaderValue, Method, Uri}; +use http::{HeaderValue, Method, Uri}; use http_body_util::BodyExt as _; use http_body_util::combinators::UnsyncBoxBody; use std::sync::Arc; @@ -36,7 +36,7 @@ pub struct Request { /// The path and query of the request. pub path_with_query: Option, /// The request headers. - pub headers: Arc, + pub headers: FieldMap, /// Request options. pub options: Option>, /// Request body. @@ -55,7 +55,7 @@ impl Request { scheme: Option, authority: Option, path_with_query: Option, - headers: impl Into>, + headers: impl Into, options: Option>, body: impl Into>, ) -> ( @@ -119,7 +119,7 @@ impl Request { scheme, authority, path_and_query, - headers, + FieldMap::new_immutable(headers), None, body.map_err(Into::into).boxed_unsync(), ) @@ -156,7 +156,7 @@ impl Request { scheme, authority, path_with_query, - headers, + mut headers, options, body, } = self; @@ -207,21 +207,21 @@ impl Request { } } }; - let mut headers = Arc::unwrap_or_clone(headers); let mut store = store.as_context_mut(); - let WasiHttpCtxView { ctx, .. } = getter(store.data_mut()); - if ctx.set_host_header() { + let WasiHttpCtxView { hooks, ctx, .. } = getter(store.data_mut()); + headers.set_mutable(ctx.field_size_limit); + if hooks.set_host_header() { let host = if let Some(authority) = authority.as_ref() { HeaderValue::try_from(authority.as_str()) .map_err(|err| ErrorCode::InternalError(Some(err.to_string())))? } else { HeaderValue::from_static("") }; - headers.insert(HOST, host); + headers.append(HOST, host).map_err(HttpError::trap)?; } let scheme = match scheme { - None => ctx.default_scheme().ok_or(ErrorCode::HttpProtocolError)?, - Some(scheme) if ctx.is_supported_scheme(&scheme) => scheme, + None => hooks.default_scheme().ok_or(ErrorCode::HttpProtocolError)?, + Some(scheme) if hooks.is_supported_scheme(&scheme) => scheme, Some(..) => return Err(ErrorCode::HttpProtocolError.into()), }; let mut uri = Uri::builder().scheme(scheme); @@ -236,7 +236,7 @@ impl Request { ErrorCode::HttpRequestUriInvalid })?; let mut req = http::Request::builder(); - *req.headers_mut().unwrap() = headers; + *req.headers_mut().unwrap() = headers.into(); let req = req .method(method) .uri(uri) @@ -478,7 +478,7 @@ pub async fn default_send_request( #[cfg(test)] mod tests { use super::*; - use crate::p3::DefaultWasiHttpCtx; + use crate::WasiHttpCtx; use core::future::Future; use core::pin::pin; use core::str::FromStr; @@ -491,7 +491,7 @@ mod tests { struct TestCtx { table: ResourceTable, wasi: WasiCtx, - http: DefaultWasiHttpCtx, + http: WasiHttpCtx, } impl TestCtx { @@ -499,7 +499,7 @@ mod tests { Self { table: ResourceTable::default(), wasi: WasiCtxBuilder::new().build(), - http: DefaultWasiHttpCtx, + http: Default::default(), } } } @@ -518,6 +518,7 @@ mod tests { WasiHttpCtxView { ctx: &mut self.http, table: &mut self.table, + hooks: crate::p3::default_hooks(), } } } @@ -533,7 +534,7 @@ mod tests { scheme.clone(), Some(Authority::from_static("example.com")), Some(PathAndQuery::from_static("/path?query=1")), - HeaderMap::new(), + FieldMap::default(), None, Full::new(Bytes::from_static(b"body")) .map_err(|x| match x {}) @@ -569,7 +570,7 @@ mod tests { Some(Scheme::HTTP), Some(Authority::from_static("example.com")), None, // <-- should fail, must be Some(_) when authority is set - HeaderMap::new(), + FieldMap::default(), None, Empty::new().map_err(|x| match x {}).boxed_unsync(), ); diff --git a/crates/wasi-http/src/p3/response.rs b/crates/wasi-http/src/p3/response.rs index 9c057138373a..6776faf0523f 100644 --- a/crates/wasi-http/src/p3/response.rs +++ b/crates/wasi-http/src/p3/response.rs @@ -1,12 +1,11 @@ -use crate::get_content_length; use crate::p3::bindings::http::types::ErrorCode; use crate::p3::body::{Body, GuestBody}; use crate::p3::{WasiHttpCtxView, WasiHttpView}; +use crate::{FieldMap, get_content_length}; use bytes::Bytes; -use http::{HeaderMap, StatusCode}; +use http::StatusCode; use http_body_util::BodyExt as _; use http_body_util::combinators::UnsyncBoxBody; -use std::sync::Arc; use wasmtime::AsContextMut; use wasmtime::error::Context as _; @@ -15,7 +14,7 @@ pub struct Response { /// The status of the response. pub status: StatusCode, /// The headers of the response. - pub headers: Arc, + pub headers: FieldMap, /// Response body. pub(crate) body: Body, } @@ -31,7 +30,7 @@ impl TryFrom for http::Response { }: Response, ) -> Result { let mut res = http::Response::builder().status(status); - *res.headers_mut().unwrap() = Arc::unwrap_or_clone(headers); + *res.headers_mut().unwrap() = headers.into(); res.body(body) } } @@ -106,7 +105,7 @@ impl Response { let wasi_response = Response { status: parts.status, - headers: Arc::new(parts.headers), + headers: FieldMap::new_immutable(parts.headers), body: Body::Host { body: body.map_err(Into::into).boxed_unsync(), result_tx, diff --git a/crates/wasi-http/src/p3/wit/deps/cli.wit b/crates/wasi-http/src/p3/wit/deps/cli.wit index 4f8bbf8a24a5..8ba52c5cd594 100644 --- a/crates/wasi-http/src/p3/wit/deps/cli.wit +++ b/crates/wasi-http/src/p3/wit/deps/cli.wit @@ -1,6 +1,6 @@ -package wasi:cli@0.3.0-rc-2026-02-09; +package wasi:cli@0.3.0-rc-2026-03-15; -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface environment { /// Get the POSIX-style environment variables. /// @@ -10,23 +10,23 @@ interface environment { /// Morally, these are a value import, but until value imports are available /// in the component model, this import function should return the same /// values each time it is called. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-environment: func() -> list>; /// Get the POSIX-style arguments to the program. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-arguments: func() -> list; /// Return a path that programs should use as their initial current working /// directory, interpreting `.` as shorthand for this. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-initial-cwd: func() -> option; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface exit { /// Exit the current instance and any linked instances. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) exit: func(status: result); /// Exit the current instance and any linked instances, reporting the @@ -41,16 +41,16 @@ interface exit { exit-with-code: func(status-code: u8); } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface run { /// Run the program. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) run: async func() -> result; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum error-code { /// Input/output error io, @@ -61,7 +61,7 @@ interface types { } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stdin { use types.{error-code}; @@ -78,11 +78,11 @@ interface stdin { /// /// Multiple streams may be active at the same time. The behavior of concurrent /// reads is implementation-specific. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-via-stream: func() -> tuple, future>>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stdout { use types.{error-code}; @@ -94,11 +94,11 @@ interface stdout { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream) -> future>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stderr { use types.{error-code}; @@ -110,7 +110,7 @@ interface stderr { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream) -> future>; } @@ -119,10 +119,10 @@ interface stderr { /// In the future, this may include functions for disabling echoing, /// disabling input buffering so that keyboard events are sent through /// immediately, querying supported features, and so on. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-input { /// The input side of a terminal. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource terminal-input; } @@ -131,126 +131,126 @@ interface terminal-input { /// In the future, this may include functions for querying the terminal /// size, being notified of terminal size changes, querying supported /// features, and so on. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-output { /// The output side of a terminal. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource terminal-output; } /// An interface providing an optional `terminal-input` for stdin as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stdin { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-input.{terminal-input}; /// If stdin is connected to a terminal, return a `terminal-input` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stdin: func() -> option; } /// An interface providing an optional `terminal-output` for stdout as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stdout { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-output.{terminal-output}; /// If stdout is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stdout: func() -> option; } /// An interface providing an optional `terminal-output` for stderr as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stderr { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-output.{terminal-output}; /// If stderr is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stderr: func() -> option; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import environment; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import exit; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stderr; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-input; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-output; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-02-09; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:filesystem/types@0.3.0-rc-2026-02-09; - import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; - import wasi:sockets/types@0.3.0-rc-2026-02-09; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:filesystem/types@0.3.0-rc-2026-03-15; + import wasi:filesystem/preopens@0.3.0-rc-2026-03-15; + import wasi:sockets/types@0.3.0-rc-2026-03-15; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world command { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import environment; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import exit; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stderr; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-input; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-output; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-02-09; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:filesystem/types@0.3.0-rc-2026-02-09; - import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; - import wasi:sockets/types@0.3.0-rc-2026-02-09; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:filesystem/types@0.3.0-rc-2026-03-15; + import wasi:filesystem/preopens@0.3.0-rc-2026-03-15; + import wasi:sockets/types@0.3.0-rc-2026-03-15; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) export run; } diff --git a/crates/wasi-http/src/p3/wit/deps/clocks.wit b/crates/wasi-http/src/p3/wit/deps/clocks.wit index 71986bab2bcc..19fc4bcd59e9 100644 --- a/crates/wasi-http/src/p3/wit/deps/clocks.wit +++ b/crates/wasi-http/src/p3/wit/deps/clocks.wit @@ -1,10 +1,10 @@ -package wasi:clocks@0.3.0-rc-2026-02-09; +package wasi:clocks@0.3.0-rc-2026-03-15; /// This interface common types used throughout wasi:clocks. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { /// A duration of time, in nanoseconds. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type duration = u64; } @@ -16,14 +16,14 @@ interface types { /// /// A monotonic clock is a clock which has an unspecified initial value, and /// successive reads of the clock will produce non-decreasing values. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface monotonic-clock { use types.{duration}; /// A mark on a monotonic clock is a number of nanoseconds since an /// unspecified initial value, and can only be compared to instances from /// the same monotonic-clock. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type mark = u64; /// Read the current value of the clock. @@ -35,20 +35,20 @@ interface monotonic-clock { /// the value of the clock in a `mark`. Consequently, implementations /// should ensure that the starting time is low enough to avoid the /// possibility of overflow in practice. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) now: func() -> mark; /// Query the resolution of the clock. Returns the duration of time /// corresponding to a clock tick. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-resolution: func() -> duration; /// Wait until the specified mark has occurred. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) wait-until: async func(when: mark); /// Wait for the specified duration to elapse. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) wait-for: async func(how-long: duration); } @@ -62,7 +62,7 @@ interface monotonic-clock { /// monotonic, making it unsuitable for measuring elapsed time. /// /// It is intended for reporting the current date and time for humans. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface system-clock { use types.{duration}; @@ -82,7 +82,7 @@ interface system-clock { /// /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record instant { seconds: s64, nanoseconds: u32, @@ -94,12 +94,12 @@ interface system-clock { /// will not necessarily produce a sequence of non-decreasing values. /// /// The nanoseconds field of the output is always less than 1000000000. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) now: func() -> instant; /// Query the resolution of the clock. Returns the smallest duration of time /// that the implementation permits distinguishing. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-resolution: func() -> duration; } @@ -148,13 +148,13 @@ interface timezone { to-debug-string: func() -> string; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import monotonic-clock; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import system-clock; @unstable(feature = clocks-timezone) import timezone; diff --git a/crates/wasi-http/src/p3/wit/deps/filesystem.wit b/crates/wasi-http/src/p3/wit/deps/filesystem.wit index 8fb83041a103..697681f30c5e 100644 --- a/crates/wasi-http/src/p3/wit/deps/filesystem.wit +++ b/crates/wasi-http/src/p3/wit/deps/filesystem.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.3.0-rc-2026-02-09; +package wasi:filesystem@0.3.0-rc-2026-03-15; /// WASI filesystem is a filesystem API primarily intended to let users run WASI /// programs that access their files on their existing filesystems, without @@ -35,23 +35,20 @@ package wasi:filesystem@0.3.0-rc-2026-02-09; /// store or a database instead. /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) - use wasi:clocks/system-clock@0.3.0-rc-2026-02-09.{instant}; + @since(version = 0.3.0-rc-2026-03-15) + use wasi:clocks/system-clock@0.3.0-rc-2026-03-15.{instant}; /// File size or length of a region within a file. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type filesize = u64; /// The type of a filesystem object referenced by a descriptor. /// /// Note: This was called `filetype` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) - enum descriptor-type { - /// The type of the descriptor or file is unknown or is different from - /// any of the other types specified. - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant descriptor-type { /// The descriptor refers to a block device inode. block-device, /// The descriptor refers to a character device inode. @@ -66,12 +63,15 @@ interface types { regular-file, /// The descriptor refers to a socket. socket, + /// The type of the descriptor or file is different from any of the + /// other types specified. + other(option), } /// Descriptor flags. /// /// Note: This was called `fdflags` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags descriptor-flags { /// Read mode: Data can be read. read, @@ -113,7 +113,7 @@ interface types { } /// Flags determining the method of how paths are resolved. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags path-flags { /// As long as the resolved path corresponds to a symbolic link, it is /// expanded. @@ -121,7 +121,7 @@ interface types { } /// Open flags used by `open-at`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags open-flags { /// Create file if it does not exist, similar to `O_CREAT` in POSIX. create, @@ -134,13 +134,13 @@ interface types { } /// Number of hard links to an inode. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type link-count = u64; /// File attributes. /// /// Note: This was called `filestat` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record descriptor-stat { /// File type. %type: descriptor-type, @@ -167,7 +167,7 @@ interface types { } /// When setting a timestamp, this gives the value to set it to. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant new-timestamp { /// Leave the timestamp set to its previous value. no-change, @@ -179,7 +179,7 @@ interface types { } /// A directory entry. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record directory-entry { /// The type of the file referred to by this directory entry. %type: descriptor-type, @@ -191,8 +191,8 @@ interface types { /// Not all of these error codes are returned by the functions provided by this /// API; some are used in higher-level library layers, and others are provided /// merely for alignment with POSIX. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Permission denied, similar to `EACCES` in POSIX. access, /// Connection already in progress, similar to `EALREADY` in POSIX. @@ -265,10 +265,14 @@ interface types { text-file-busy, /// Cross-device link, similar to `EXDEV` in POSIX. cross-device, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } /// File or memory access pattern advisory information. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum advice { /// The application has no advice to give on its behavior with respect /// to the specified data. @@ -292,7 +296,7 @@ interface types { /// A 128-bit hash value, split into parts because wasm doesn't have a /// 128-bit integer type. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record metadata-hash-value { /// 64 bits of a 128-bit hash value. lower: u64, @@ -303,7 +307,7 @@ interface types { /// A descriptor is a reference to a filesystem object, which may be a file, /// directory, named pipe, special file, or other object on which filesystem /// calls may be made. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource descriptor { /// Return a stream for reading from a file. /// @@ -321,7 +325,7 @@ interface types { /// resolves to `err` with an `error-code`. /// /// Note: This is similar to `pread` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-via-stream: func(offset: filesize) -> tuple, future>>; /// Return a stream for writing to a file, if available. /// @@ -335,7 +339,7 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `pwrite` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream, offset: filesize) -> future>; /// Return a stream for appending to a file, if available. /// @@ -345,12 +349,12 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `write` with `O_APPEND` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) append-via-stream: func(data: stream) -> future>; /// Provide file advisory information on a descriptor. /// /// This is similar to `posix_fadvise` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) advise: async func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; /// Synchronize the data of a file to disk. /// @@ -358,7 +362,7 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fdatasync` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) sync-data: async func() -> result<_, error-code>; /// Get flags associated with a descriptor. /// @@ -366,7 +370,7 @@ interface types { /// /// Note: This returns the value that was the `fs_flags` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-flags: async func() -> result; /// Get the dynamic type of a descriptor. /// @@ -378,20 +382,20 @@ interface types { /// /// Note: This returns the value that was the `fs_filetype` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-type: async func() -> result; /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-size: async func(size: filesize) -> result<_, error-code>; /// Adjust the timestamps of an open file or directory. /// /// Note: This is similar to `futimens` in POSIX. /// /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-times: async func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Read directory entries from a directory. /// @@ -405,7 +409,7 @@ interface types { /// /// This function returns a future, which will resolve to an error code if /// reading full contents of the directory fails. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-directory: func() -> tuple, future>>; /// Synchronize the data and metadata of a file to disk. /// @@ -413,12 +417,12 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fsync` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) sync: async func() -> result<_, error-code>; /// Create a directory. /// /// Note: This is similar to `mkdirat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create-directory-at: async func(path: string) -> result<_, error-code>; /// Return the attributes of an open file or directory. /// @@ -429,7 +433,7 @@ interface types { /// modified, use `metadata-hash`. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) stat: async func() -> result; /// Return the attributes of a file or directory. /// @@ -438,7 +442,7 @@ interface types { /// discussion of alternatives. /// /// Note: This was called `path_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) stat-at: async func(path-flags: path-flags, path: string) -> result; /// Adjust the timestamps of a file or directory. /// @@ -446,7 +450,7 @@ interface types { /// /// Note: This was called `path_filestat_set_times` in earlier versions of /// WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-times-at: async func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Create a hard link. /// @@ -455,7 +459,7 @@ interface types { /// `error-code::not-permitted` if the old path is not a file. /// /// Note: This is similar to `linkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) link-at: async func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Open a file or directory. /// @@ -469,7 +473,7 @@ interface types { /// `error-code::read-only`. /// /// Note: This is similar to `openat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) open-at: async func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; /// Read the contents of a symbolic link. /// @@ -477,19 +481,19 @@ interface types { /// filesystem, this function fails with `error-code::not-permitted`. /// /// Note: This is similar to `readlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) readlink-at: async func(path: string) -> result; /// Remove a directory. /// /// Return `error-code::not-empty` if the directory is not empty. /// /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) remove-directory-at: async func(path: string) -> result<_, error-code>; /// Rename a filesystem object. /// /// Note: This is similar to `renameat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) rename-at: async func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Create a symbolic link (also known as a "symlink"). /// @@ -497,13 +501,18 @@ interface types { /// `error-code::not-permitted`. /// /// Note: This is similar to `symlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) symlink-at: async func(old-path: string, new-path: string) -> result<_, error-code>; /// Unlink a filesystem object that is not a directory. /// - /// Return `error-code::is-directory` if the path refers to a directory. - /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + /// This is similar to `unlinkat(fd, path, 0)` in POSIX. + /// + /// Error returns are as specified by POSIX. + /// + /// If the filesystem object is a directory, `error-code::access` or + /// `error-code::is-directory` may be returned instead of the + /// POSIX-specified `error-code::not-permitted`. + @since(version = 0.3.0-rc-2026-03-15) unlink-file-at: async func(path: string) -> result<_, error-code>; /// Test whether two descriptors refer to the same filesystem object. /// @@ -511,7 +520,7 @@ interface types { /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. /// wasi-filesystem does not expose device and inode numbers, so this function /// may be used instead. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) is-same-object: async func(other: borrow) -> bool; /// Return a hash of the metadata associated with a filesystem object referred /// to by a descriptor. @@ -532,35 +541,35 @@ interface types { /// computed hash. /// /// However, none of these is required. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) metadata-hash: async func() -> result; /// Return a hash of the metadata associated with a filesystem object referred /// to by a directory descriptor and a relative path. /// /// This performs the same hash computation as `metadata-hash`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) metadata-hash-at: async func(path-flags: path-flags, path: string) -> result; } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface preopens { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use types.{descriptor}; /// Return the set of preopened directories, and their paths. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-directories: func() -> list>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/types@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/types@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import preopens; } diff --git a/crates/wasi-http/src/p3/wit/deps/http.wit b/crates/wasi-http/src/p3/wit/deps/http.wit index 442b18ea129f..c1c1e68e75ba 100644 --- a/crates/wasi-http/src/p3/wit/deps/http.wit +++ b/crates/wasi-http/src/p3/wit/deps/http.wit @@ -1,13 +1,13 @@ -package wasi:http@0.3.0-rc-2026-02-09; +package wasi:http@0.3.0-rc-2026-03-15; /// This interface defines all of the types and methods for implementing HTTP /// Requests and Responses, as well as their headers, trailers, and bodies. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - use wasi:clocks/types@0.3.0-rc-2026-02-09.{duration}; + use wasi:clocks/types@0.3.0-rc-2026-03-15.{duration}; /// This type corresponds to HTTP standard Methods. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant method { get, head, @@ -22,7 +22,7 @@ interface types { } /// This type corresponds to HTTP standard Related Schemes. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant scheme { HTTP, HTTPS, @@ -30,21 +30,21 @@ interface types { } /// Defines the case payload type for `DNS-error` above: - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record DNS-error-payload { rcode: option, info-code: option, } /// Defines the case payload type for `TLS-alert-received` above: - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record TLS-alert-received-payload { alert-id: option, alert-message: option, } /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record field-size-payload { field-name: option, field-size: option, @@ -52,7 +52,7 @@ interface types { /// These cases are inspired by the IANA HTTP Proxy Error Types: /// - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant error-code { DNS-timeout, DNS-error(DNS-error-payload), @@ -102,7 +102,7 @@ interface types { /// This type enumerates the different kinds of errors that may occur when /// setting or appending to a `fields` resource. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant header-error { /// This error indicates that a `field-name` or `field-value` was /// syntactically invalid when used with an operation that sets headers in a @@ -114,30 +114,49 @@ interface types { /// This error indicates that the operation on the `fields` was not /// permitted because the fields are immutable. immutable, + /// This error indicates that the operation would exceed an + /// implementation-defined limit on field sizes. This may apply to + /// an individual `field-value`, a single `field-name` plus all its + /// values, or the total aggregate size of all fields. + size-exceeded, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. Implementations can use this to extend the error + /// type without breaking existing code. It also includes an optional + /// string for an unstructured description of the error. Users should not + /// depend on the string for diagnosing errors, as it's not required to be + /// consistent between implementations. + other(option), } /// This type enumerates the different kinds of errors that may occur when /// setting fields of a `request-options` resource. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant request-options-error { /// Indicates the specified field is not supported by this implementation. not-supported, /// Indicates that the operation on the `request-options` was not permitted /// because it is immutable. immutable, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. Implementations can use this to extend the error + /// type without breaking existing code. It also includes an optional + /// string for an unstructured description of the error. Users should not + /// depend on the string for diagnosing errors, as it's not required to be + /// consistent between implementations. + other(option), } /// Field names are always strings. /// /// Field names should always be treated as case insensitive by the `fields` /// resource for the purposes of equality checking. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type field-name = string; /// Field values should always be ASCII strings. However, in /// reality, HTTP implementations often have to interpret malformed values, /// so they are provided as a list of bytes. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type field-value = list; /// This following block defines the `fields` resource which corresponds to @@ -155,7 +174,11 @@ interface types { /// original casing used to construct or mutate the `fields` resource. The `fields` /// resource should use that original casing when serializing the fields for /// transport or when returning them from a method. - @since(version = 0.3.0-rc-2026-02-09) + /// + /// Implementations may impose limits on individual field values and on total + /// aggregate field section size. Operations that would exceed these limits + /// fail with `header-error.size-exceeded` + @since(version = 0.3.0-rc-2026-03-15) resource fields { /// Construct an empty HTTP Fields. /// @@ -175,7 +198,8 @@ interface types { /// well-formed, so they are represented as a raw list of bytes. /// /// An error result will be returned if any header or value was - /// syntactically invalid, or if a header was forbidden. + /// syntactically invalid, if a header was forbidden, or if the + /// entries would exceed an implementation size limit. from-list: static func(entries: list>) -> result; /// Get all of the values corresponding to a name. If the name is not present /// in this `fields`, an empty list is returned. However, if the name is @@ -189,6 +213,9 @@ interface types { /// name, if they have been set. /// /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.size-exceeded` if the name or values would + /// exceed an implementation-defined size limit. set: func(name: field-name, value: list) -> result<_, header-error>; /// Delete all values for a name. Does nothing if no values for the name /// exist. @@ -206,6 +233,9 @@ interface types { /// values for that name. /// /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.size-exceeded` if the value would exceed + /// an implementation-defined size limit. append: func(name: field-name, value: field-value) -> result<_, header-error>; /// Retrieve the full set of names and values in the Fields. Like the /// constructor, the list represents each name-value pair. @@ -224,15 +254,15 @@ interface types { } /// Headers is an alias for Fields. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type headers = fields; /// Trailers is an alias for Fields. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type trailers = fields; /// Represents an HTTP Request. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource request { /// Construct a new `request` with a default `method` of `GET`, and /// `none` values for `path-with-query`, `scheme`, and `authority`. @@ -319,7 +349,7 @@ interface types { /// /// These timeouts are separate from any the user may use to bound an /// asynchronous call. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource request-options { /// Construct a default `request-options` value. constructor(); @@ -348,11 +378,11 @@ interface types { } /// This type corresponds to the HTTP standard Status Code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type status-code = u16; /// Represents an HTTP Response. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource response { /// Construct a new `response`, with a default `status-code` of `200`. /// If a different `status-code` is needed, it must be set via the @@ -401,7 +431,7 @@ interface types { /// /// In `wasi:http/middleware` this interface is both exported and imported as /// the "downstream" and "upstream" directions of the middleware chain. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface handler { use types.{request, response, error-code}; @@ -420,7 +450,7 @@ interface handler { /// (including WIT itself) is unable to represent a component importing two /// instances of the same interface. A `client.send` import may be linked /// directly to a `handler.handle` export to bypass the network. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface client { use types.{request, response, error-code}; @@ -432,22 +462,22 @@ interface client { /// The `wasi:http/service` world captures a broad category of HTTP services /// including web applications, API servers, and proxies. It may be `include`d /// in more specific worlds such as `wasi:http/middleware`. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world service { - import wasi:cli/types@0.3.0-rc-2026-02-09; - import wasi:cli/stdout@0.3.0-rc-2026-02-09; - import wasi:cli/stderr@0.3.0-rc-2026-02-09; - import wasi:cli/stdin@0.3.0-rc-2026-02-09; - import wasi:clocks/types@0.3.0-rc-2026-02-09; + import wasi:cli/types@0.3.0-rc-2026-03-15; + import wasi:cli/stdout@0.3.0-rc-2026-03-15; + import wasi:cli/stderr@0.3.0-rc-2026-03-15; + import wasi:cli/stdin@0.3.0-rc-2026-03-15; + import wasi:clocks/types@0.3.0-rc-2026-03-15; import types; import client; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; export handler; } @@ -457,23 +487,23 @@ world service { /// Components may implement this world to allow them to participate in handler /// "chains" where a `request` flows through handlers on its way to some terminal /// `service` and corresponding `response` flows in the opposite direction. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world middleware { - import wasi:clocks/types@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; import types; import handler; - import wasi:cli/types@0.3.0-rc-2026-02-09; - import wasi:cli/stdout@0.3.0-rc-2026-02-09; - import wasi:cli/stderr@0.3.0-rc-2026-02-09; - import wasi:cli/stdin@0.3.0-rc-2026-02-09; + import wasi:cli/types@0.3.0-rc-2026-03-15; + import wasi:cli/stdout@0.3.0-rc-2026-03-15; + import wasi:cli/stderr@0.3.0-rc-2026-03-15; + import wasi:cli/stdin@0.3.0-rc-2026-03-15; import client; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; export handler; } diff --git a/crates/wasi-http/src/p3/wit/deps/random.wit b/crates/wasi-http/src/p3/wit/deps/random.wit index 521df6e50fc5..026f44a10949 100644 --- a/crates/wasi-http/src/p3/wit/deps/random.wit +++ b/crates/wasi-http/src/p3/wit/deps/random.wit @@ -1,10 +1,10 @@ -package wasi:random@0.3.0-rc-2026-02-09; +package wasi:random@0.3.0-rc-2026-03-15; /// The insecure-seed interface for seeding hash-map DoS resistance. /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface insecure-seed { /// Return a 128-bit value that may contain a pseudo-random value. /// @@ -23,7 +23,7 @@ interface insecure-seed { /// This will likely be changed to a value import, to prevent it from being /// called multiple times and potentially used for purposes other than DoS /// protection. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-insecure-seed: func() -> tuple; } @@ -31,9 +31,9 @@ interface insecure-seed { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface insecure { - /// Return `len` insecure pseudo-random bytes. + /// Return up to `max-len` insecure pseudo-random bytes. /// /// This function is not cryptographically secure. Do not use it for /// anything related to security. @@ -41,14 +41,21 @@ interface insecure { /// There are no requirements on the values of the returned bytes, however /// implementations are encouraged to return evenly distributed values with /// a long period. - @since(version = 0.3.0-rc-2026-02-09) - get-insecure-random-bytes: func(len: u64) -> list; + /// + /// Implementations MAY return fewer bytes than requested (a short read). + /// Callers that require exactly `max-len` bytes MUST call this function in + /// a loop until the desired number of bytes has been accumulated. + /// Implementations MUST return at least 1 byte when `max-len` is greater + /// than zero. When `max-len` is zero, implementations MUST return an empty + /// list without trapping. + @since(version = 0.3.0-rc-2026-03-15) + get-insecure-random-bytes: func(max-len: u64) -> list; /// Return an insecure pseudo-random `u64` value. /// /// This function returns the same type of pseudo-random data as /// `get-insecure-random-bytes`, represented as a `u64`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-insecure-random-u64: func() -> u64; } @@ -56,9 +63,10 @@ interface insecure { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface random { - /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// Return up to `max-len` cryptographically-secure random or pseudo-random + /// bytes. /// /// This function must produce data at least as cryptographically secure and /// fast as an adequately seeded cryptographically-secure pseudo-random @@ -67,26 +75,33 @@ interface random { /// request and on requests for numbers of bytes. The returned data must /// always be unpredictable. /// + /// Implementations MAY return fewer bytes than requested (a short read). + /// Callers that require exactly `max-len` bytes MUST call this function in + /// a loop until the desired number of bytes has been accumulated. + /// Implementations MUST return at least 1 byte when `max-len` is greater + /// than zero. When `max-len` is zero, implementations MUST return an empty + /// list without trapping. + /// /// This function must always return fresh data. Deterministic environments /// must omit this function, rather than implementing it with deterministic /// data. - @since(version = 0.3.0-rc-2026-02-09) - get-random-bytes: func(len: u64) -> list; + @since(version = 0.3.0-rc-2026-03-15) + get-random-bytes: func(max-len: u64) -> list; /// Return a cryptographically-secure random or pseudo-random `u64` value. /// /// This function returns the same type of data as `get-random-bytes`, /// represented as a `u64`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-random-u64: func() -> u64; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import random; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import insecure; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import insecure-seed; } diff --git a/crates/wasi-http/src/p3/wit/deps/sockets.wit b/crates/wasi-http/src/p3/wit/deps/sockets.wit index aa9d4d7f074e..cde2e4d6ebe0 100644 --- a/crates/wasi-http/src/p3/wit/deps/sockets.wit +++ b/crates/wasi-http/src/p3/wit/deps/sockets.wit @@ -1,63 +1,86 @@ -package wasi:sockets@0.3.0-rc-2026-02-09; +package wasi:sockets@0.3.0-rc-2026-03-15; -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) - use wasi:clocks/types@0.3.0-rc-2026-02-09.{duration}; + @since(version = 0.3.0-rc-2026-03-15) + use wasi:clocks/types@0.3.0-rc-2026-03-15.{duration}; /// Error codes. /// /// In theory, every API can return any error code. /// In practice, API's typically only return the errors documented per API /// combined with a couple of errors that are always possible: - /// - `unknown` + /// - `other` /// - `access-denied` /// - `not-supported` /// - `out-of-memory` /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { - /// Unknown error - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Access denied. /// /// POSIX equivalent: EACCES, EPERM access-denied, /// The operation is not supported. /// - /// POSIX equivalent: EOPNOTSUPP + /// POSIX equivalent: EOPNOTSUPP, ENOPROTOOPT, EPFNOSUPPORT, EPROTONOSUPPORT, ESOCKTNOSUPPORT not-supported, /// One of the arguments is invalid. /// - /// POSIX equivalent: EINVAL + /// POSIX equivalent: EINVAL, EDESTADDRREQ, EAFNOSUPPORT invalid-argument, /// Not enough memory to complete the operation. /// - /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + /// POSIX equivalent: ENOMEM, ENOBUFS out-of-memory, /// The operation timed out before it could finish completely. + /// + /// POSIX equivalent: ETIMEDOUT timeout, /// The operation is not valid in the socket's current state. invalid-state, - /// A bind operation failed because the provided address is not an address that the `network` can bind to. + /// The local address is not available. + /// + /// POSIX equivalent: EADDRNOTAVAIL address-not-bindable, - /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + /// A bind operation failed because the provided address is already in + /// use or because there are no ephemeral ports available. + /// + /// POSIX equivalent: EADDRINUSE address-in-use, - /// The remote address is not reachable + /// The remote address is not reachable. + /// + /// POSIX equivalent: EHOSTUNREACH, EHOSTDOWN, ENETDOWN, ENETUNREACH, ENONET remote-unreachable, - /// The TCP connection was forcefully rejected + /// The connection was forcefully rejected. + /// + /// POSIX equivalent: ECONNREFUSED connection-refused, - /// The TCP connection was reset. + /// A write failed because the connection was broken. + /// + /// POSIX equivalent: EPIPE + connection-broken, + /// The connection was reset. + /// + /// POSIX equivalent: ECONNRESET connection-reset, - /// A TCP connection was aborted. + /// The connection was aborted. + /// + /// POSIX equivalent: ECONNABORTED connection-aborted, /// The size of a datagram sent to a UDP socket exceeded the maximum /// supported size. + /// + /// POSIX equivalent: EMSGSIZE datagram-too-large, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum ip-address-family { /// Similar to `AF_INET` in POSIX. ipv4, @@ -65,19 +88,19 @@ interface types { ipv6, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type ipv4-address = tuple; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type ipv6-address = tuple; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant ip-address { ipv4(ipv4-address), ipv6(ipv6-address), } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record ipv4-socket-address { /// sin_port port: u16, @@ -85,7 +108,7 @@ interface types { address: ipv4-address, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record ipv6-socket-address { /// sin6_port port: u16, @@ -97,7 +120,7 @@ interface types { scope-id: u32, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant ip-socket-address { ipv4(ipv4-socket-address), ipv6(ipv6-socket-address), @@ -112,39 +135,57 @@ interface types { /// - `connecting` /// - `connected` /// - `closed` - /// See + /// See /// for more information. /// /// Note: Except where explicitly mentioned, whenever this documentation uses /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. /// (i.e. `bound`, `listening`, `connecting` or `connected`) /// + /// WASI uses shared ownership semantics: the `tcp-socket` handle and all + /// derived `stream` and `future` values reference a single underlying OS + /// socket: + /// - Send/receive streams remain functional after the original `tcp-socket` + /// handle is dropped. + /// - The stream returned by `listen` behaves similarly. + /// - Client sockets returned by `tcp-socket::listen` are independent and do + /// not keep the listening socket alive. + /// + /// The OS socket is closed only after the last handle is dropped. This + /// model has observable effects; for example, it affects when the local + /// port binding is released. + /// /// In addition to the general error codes documented on the /// `types::error-code` type, TCP socket methods may always return /// `error(invalid-state)` when in the `closed` state. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource tcp-socket { /// Create a new TCP socket. /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. - /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` + /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and + /// can't be configured otherwise. /// /// Unlike POSIX, WASI sockets have no notion of a socket-level /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's /// async support. /// + /// # Typical errors + /// - `not-supported`: The `address-family` is not supported. (EAFNOSUPPORT) + /// /// # References /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is + /// left to the implementation to decide which network interface(s) to + /// bind to. If the TCP/UDP port is zero, the socket will be bound to a + /// random free port. /// /// Bind can be attempted multiple times on the same socket, even with /// different arguments on each iteration. But never concurrently and @@ -161,21 +202,27 @@ interface types { /// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL) /// /// # Implementors note - /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT - /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR - /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior - /// and SO_REUSEADDR performs something different entirely. + /// The bind operation shouldn't be affected by the TIME_WAIT state of a + /// recently closed socket on the same local address. In practice this + /// means that the SO_REUSEADDR socket option should be set implicitly + /// on all platforms, except on Windows where this is the default + /// behavior and SO_REUSEADDR performs something different. /// /// # References /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Connect to a remote endpoint. /// - /// On success, the socket is transitioned into the `connected` state and this function returns a connection resource. + /// On success, the socket is transitioned into the `connected` state + /// and the `remote-address` of the socket is updated. + /// The `local-address` may be updated as well, based on the best network + /// path to `remote-address`. If the socket was not already explicitly + /// bound, this function will implicitly bind the socket to a random + /// free port. /// /// After a failed connection attempt, the socket will be in the `closed` /// state and the only valid action left is to `drop` the socket. A single @@ -202,7 +249,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) connect: async func(remote-address: ip-socket-address) -> result<_, error-code>; /// Start listening and return a stream of new inbound connections. /// @@ -274,7 +321,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) listen: func() -> result, error-code>; /// Transmit data to peer. /// @@ -288,6 +335,8 @@ interface types { /// /// # Typical errors /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `invalid-state`: `send` has already been called on this socket. + /// - `connection-broken`: The connection is not writable anymore. (EPIPE, ECONNABORTED on Windows) /// - `connection-reset`: The connection was reset. (ECONNRESET) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// @@ -296,31 +345,27 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) send: func(data: stream) -> future>; /// Read data from peer. /// - /// This function returns a `stream` which provides the data received from the - /// socket, and a `future` providing additional error information in case the - /// socket is closed abnormally. + /// Returns a `stream` of data sent by the peer. The implementation + /// drops the stream once no more data is available. At that point, the + /// returned `future` resolves to: + /// - `ok` after a graceful shutdown from the peer (i.e. a FIN packet), or + /// - `err` if the socket was closed abnormally. /// - /// If the socket is closed normally, `stream.read` on the `stream` will return - /// `read-status::closed` with no `error-context` and the future resolves to - /// the value `ok`. If the socket is closed abnormally, `stream.read` on the - /// `stream` returns `read-status::closed` with an `error-context` and the future - /// resolves to `err` with an `error-code`. + /// `receive` may be called only once per socket. Subsequent calls return + /// a closed stream and a future resolved to `err(invalid-state)`. /// - /// `receive` is meant to be called only once per socket. If it is called more - /// than once, the subsequent calls return a new `stream` that fails as if it - /// were closed abnormally. - /// - /// If the caller is not expecting to receive any data from the peer, - /// they may drop the stream. Any data still in the receive queue + /// If the caller is not expecting to receive any more data from the peer, + /// they should drop the stream. Any data still in the receive queue /// will be discarded. This is equivalent to calling `shutdown(SHUT_RD)` /// in POSIX. /// /// # Typical errors /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `invalid-state`: `receive` has already been called on this socket. /// - `connection-reset`: The connection was reset. (ECONNRESET) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// @@ -329,7 +374,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) receive: func() -> tuple, future>>; /// Get the bound local address. /// @@ -337,7 +382,8 @@ interface types { /// > If the socket has not been bound to a local name, the value /// > stored in the object pointed to by `address` is unspecified. /// - /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// WASI is stricter and requires `get-local-address` to return + /// `invalid-state` when the socket hasn't been bound yet. /// /// # Typical errors /// - `invalid-state`: The socket is not bound to any local address. @@ -347,7 +393,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-local-address: func() -> result; /// Get the remote address. /// @@ -359,30 +405,32 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-remote-address: func() -> result; /// Whether the socket is in the `listening` state. /// /// Equivalent to the SO_ACCEPTCONN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-is-listening: func() -> bool; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-address-family: func() -> ip-address-family; - /// Hints the desired listen queue size. Implementations are free to ignore this. + /// Hints the desired listen queue size. Implementations are free to + /// ignore this. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// Any other value will never cause an error, but it might be silently + /// clamped and/or rounded. /// /// # Typical errors /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. /// - `invalid-argument`: (set) The provided value was 0. /// - `invalid-state`: (set) The socket is in the `connecting` or `connected` state. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-listen-backlog-size: func(value: u64) -> result<_, error-code>; /// Enables or disables keepalive. /// @@ -390,54 +438,60 @@ interface types { /// - `keep-alive-idle-time` /// - `keep-alive-interval` /// - `keep-alive-count` - /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// These properties can be configured while `keep-alive-enabled` is + /// false, but only come into effect when `keep-alive-enabled` is true. /// /// Equivalent to the SO_KEEPALIVE socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-enabled: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; - /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// Amount of time the connection has to be idle before TCP starts + /// sending keepalive packets. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-idle-time: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; /// The time between keepalive packets. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPINTVL socket option. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-interval: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-interval: func(value: duration) -> result<_, error-code>; - /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// The maximum amount of keepalive packets TCP should send before + /// aborting the connection. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPCNT socket option. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-count: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-count: func(value: u32) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -445,37 +499,49 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-hop-limit: func(value: u8) -> result<_, error-code>; - /// The kernel buffer space reserved for sends/receives on this socket. + /// Kernel buffer space reserved for sending/receiving on this socket. + /// Implementations usually treat this as a cap the buffer can grow to, + /// rather than allocating the full amount immediately. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. + /// + /// This is only a performance hint. The implementation may ignore it or + /// tweak it based on real traffic patterns. + /// Linux and macOS appear to behave differently depending on whether a + /// buffer size was explicitly set. When set, they tend to honor it; when + /// not set, they dynamically adjust the buffer size as the connection + /// progresses. This is especially noticeable when comparing the values + /// from before and after connection establishment. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } /// A UDP socket handle. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource udp-socket { /// Create a new UDP socket. /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. - /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` + /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and + /// can't be configured otherwise. /// /// Unlike POSIX, WASI sockets have no notion of a socket-level /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's @@ -486,13 +552,14 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the port is zero, the socket will be bound to a random free port. + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is + /// left to the implementation to decide which network interface(s) to + /// bind to. If the port is zero, the socket will be bound to a random + /// free port. /// /// # Typical errors /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) @@ -506,7 +573,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Associate this socket with a specific peer address. /// @@ -544,12 +611,12 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) connect: func(remote-address: ip-socket-address) -> result<_, error-code>; /// Dissociate this socket from its peer address. /// /// After calling this method, `send` & `receive` are free to communicate - /// with any address again. + /// with any remote address again. /// /// The POSIX equivalent of this is calling `connect` with an `AF_UNSPEC` address. /// @@ -561,7 +628,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) disconnect: func() -> result<_, error-code>; /// Send a message on the socket to a particular peer. /// @@ -573,6 +640,9 @@ interface types { /// _may_ be provided but then it must be identical to the address /// passed to `connect`. /// + /// If the socket has not been explicitly bound, it will be + /// implicitly bound to a random free port. + /// /// Implementations may trap if the `data` length exceeds 64 KiB. /// /// # Typical errors @@ -584,6 +654,14 @@ interface types { /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// - `connection-refused`: The connection was refused. (ECONNREFUSED) /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// + /// # Implementors note + /// WASI requires `send` to perform an implicit bind if the socket + /// has not been bound. Not all platforms (notably Windows) exhibit + /// this behavior natively. On such platforms, the WASI implementation + /// should emulate it by performing the bind if the guest has not + /// already done so. /// /// # References /// - @@ -594,7 +672,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) send: async func(data: list, remote-address: option) -> result<_, error-code>; /// Receive a message on the socket. /// @@ -619,7 +697,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) receive: async func() -> result, ip-socket-address>, error-code>; /// Get the current bound address. /// @@ -627,7 +705,8 @@ interface types { /// > If the socket has not been bound to a local name, the value /// > stored in the object pointed to by `address` is unspecified. /// - /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// WASI is stricter and requires `get-local-address` to return + /// `invalid-state` when the socket hasn't been bound yet. /// /// # Typical errors /// - `invalid-state`: The socket is not bound to any local address. @@ -637,7 +716,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-local-address: func() -> result; /// Get the address the socket is currently "connected" to. /// @@ -649,14 +728,14 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-remote-address: func() -> result; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-address-family: func() -> ip-address-family; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -664,41 +743,42 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-unicast-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; - /// The kernel buffer space reserved for sends/receives on this socket. + /// Kernel buffer space reserved for sending/receiving on this socket. + /// Implementations usually treat this as a cap the buffer can grow to, + /// rather than allocating the full amount immediately. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface ip-name-lookup { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use types.{ip-address}; /// Lookup error codes. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { - /// Unknown error - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Access denied. /// /// POSIX equivalent: EACCES, EPERM @@ -719,13 +799,17 @@ interface ip-name-lookup { /// /// POSIX equivalent: EAI_FAIL permanent-resolver-failure, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } /// Resolve an internet host name to a list of IP addresses. /// - /// Unicode domain names are automatically converted to ASCII using IDNA encoding. - /// If the input is an IP address string, the address is parsed and returned - /// as-is without making any external requests. + /// Unicode domain names are automatically converted to ASCII using IDNA + /// encoding. If the input is an IP address string, the address is parsed + /// and returned as-is without making any external requests. /// /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. /// @@ -735,24 +819,21 @@ interface ip-name-lookup { /// with at least one address. Additionally, this function never returns /// IPv4-mapped IPv6 addresses. /// - /// The returned future will resolve to an error code in case of failure. - /// It will resolve to success once the returned stream is exhausted. - /// /// # References: /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resolve-addresses: async func(name: string) -> result, error-code>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/types@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/types@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import ip-name-lookup; } diff --git a/crates/wasi-http/src/p3/wit/world.wit b/crates/wasi-http/src/p3/wit/world.wit index f95ca2ba765d..8e7c32424586 100644 --- a/crates/wasi-http/src/p3/wit/world.wit +++ b/crates/wasi-http/src/p3/wit/world.wit @@ -2,5 +2,5 @@ package wasmtime:wasi-http; world bindings { - include wasi:http/service@0.3.0-rc-2026-02-09; + include wasi:http/service@0.3.0-rc-2026-03-15; } diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs deleted file mode 100644 index d120e0555410..000000000000 --- a/crates/wasi-http/src/types.rs +++ /dev/null @@ -1,889 +0,0 @@ -//! Implements the base structure (i.e. [WasiHttpCtx]) that will provide the -//! implementation of the wasi-http API. - -use crate::{ - bindings::http::types::{self, ErrorCode, Method, Scheme}, - body::{HostIncomingBody, HyperIncomingBody, HyperOutgoingBody}, -}; -use bytes::Bytes; -use http::header::{HeaderMap, HeaderName, HeaderValue}; -use http_body_util::BodyExt; -use hyper::body::Body; -use std::any::Any; -use std::fmt; -use std::time::Duration; -use wasmtime::component::{Resource, ResourceTable}; -use wasmtime::{Result, bail}; -use wasmtime_wasi::p2::Pollable; -use wasmtime_wasi::runtime::AbortOnDropJoinHandle; - -#[cfg(feature = "default-send-request")] -use { - crate::io::TokioIo, - crate::{error::dns_error, hyper_request_error}, - tokio::net::TcpStream, - tokio::time::timeout, -}; - -/// Default maximum size for the contents of a fields resource. -/// -/// Typically, HTTP proxies limit headers to 8k. This number is higher than that -/// because it not only includes the wire-size of headers but it additionally -/// includes factors for the in-memory representation of `HeaderMap`. This is in -/// theory high enough that no one runs into it but low enough such that a -/// completely full `HeaderMap` doesn't break the bank in terms of memory -/// consumption. -const DEFAULT_FIELD_SIZE_LIMIT: usize = 128 * 1024; - -/// Capture the state necessary for use in the wasi-http API implementation. -#[derive(Debug)] -pub struct WasiHttpCtx { - pub(crate) field_size_limit: usize, -} - -impl WasiHttpCtx { - /// Create a new context. - pub fn new() -> Self { - Self { - field_size_limit: DEFAULT_FIELD_SIZE_LIMIT, - } - } - - /// Set the maximum size for any fields resources created by this context. - /// - /// The limit specified here is roughly a byte limit for the size of the - /// in-memory representation of headers. This means that the limit needs to - /// be larger than the literal representation of headers on the wire to - /// account for in-memory Rust-side data structures representing the header - /// names/values/etc. - pub fn set_field_size_limit(&mut self, limit: usize) { - self.field_size_limit = limit; - } -} - -/// A trait which provides internal WASI HTTP state. -/// -/// # Example -/// -/// ``` -/// use wasmtime::component::ResourceTable; -/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; -/// use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; -/// -/// struct MyState { -/// ctx: WasiCtx, -/// http_ctx: WasiHttpCtx, -/// table: ResourceTable, -/// } -/// -/// impl WasiHttpView for MyState { -/// fn ctx(&mut self) -> &mut WasiHttpCtx { &mut self.http_ctx } -/// fn table(&mut self) -> &mut ResourceTable { &mut self.table } -/// } -/// -/// impl WasiView for MyState { -/// fn ctx(&mut self) -> WasiCtxView<'_> { -/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table } -/// } -/// } -/// -/// impl MyState { -/// fn new() -> MyState { -/// let mut wasi = WasiCtx::builder(); -/// wasi.arg("./foo.wasm"); -/// wasi.arg("--help"); -/// wasi.env("FOO", "bar"); -/// -/// MyState { -/// ctx: wasi.build(), -/// table: ResourceTable::new(), -/// http_ctx: WasiHttpCtx::new(), -/// } -/// } -/// } -/// ``` -pub trait WasiHttpView { - /// Returns a mutable reference to the WASI HTTP context. - fn ctx(&mut self) -> &mut WasiHttpCtx; - - /// Returns the table used to manage resources. - fn table(&mut self) -> &mut ResourceTable; - - /// Create a new incoming request resource. - fn new_incoming_request( - &mut self, - scheme: Scheme, - req: hyper::Request, - ) -> wasmtime::Result> - where - B: Body + Send + 'static, - B::Error: Into, - Self: Sized, - { - let field_size_limit = self.ctx().field_size_limit; - let (parts, body) = req.into_parts(); - let body = body.map_err(Into::into).boxed_unsync(); - let body = HostIncomingBody::new( - body, - // TODO: this needs to be plumbed through - std::time::Duration::from_millis(600 * 1000), - field_size_limit, - ); - let incoming_req = - HostIncomingRequest::new(self, parts, scheme, Some(body), field_size_limit)?; - Ok(self.table().push(incoming_req)?) - } - - /// Create a new outgoing response resource. - fn new_response_outparam( - &mut self, - result: tokio::sync::oneshot::Sender< - Result, types::ErrorCode>, - >, - ) -> wasmtime::Result> { - let id = self.table().push(HostResponseOutparam { result })?; - Ok(id) - } - - /// Send an outgoing request. - #[cfg(feature = "default-send-request")] - fn send_request( - &mut self, - request: hyper::Request, - config: OutgoingRequestConfig, - ) -> crate::HttpResult { - Ok(default_send_request(request, config)) - } - - /// Send an outgoing request. - #[cfg(not(feature = "default-send-request"))] - fn send_request( - &mut self, - request: hyper::Request, - config: OutgoingRequestConfig, - ) -> crate::HttpResult; - - /// Whether a given header should be considered forbidden and not allowed. - fn is_forbidden_header(&mut self, name: &HeaderName) -> bool { - DEFAULT_FORBIDDEN_HEADERS.contains(name) - } - - /// Number of distinct write calls to the outgoing body's output-stream - /// that the implementation will buffer. - /// Default: 1. - fn outgoing_body_buffer_chunks(&mut self) -> usize { - DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS - } - - /// Maximum size allowed in a write call to the outgoing body's output-stream. - /// Default: 1024 * 1024. - fn outgoing_body_chunk_size(&mut self) -> usize { - DEFAULT_OUTGOING_BODY_CHUNK_SIZE - } -} - -/// The default value configured for [`WasiHttpView::outgoing_body_buffer_chunks`] in [`WasiHttpView`]. -pub const DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS: usize = 1; -/// The default value configured for [`WasiHttpView::outgoing_body_chunk_size`] in [`WasiHttpView`]. -pub const DEFAULT_OUTGOING_BODY_CHUNK_SIZE: usize = 1024 * 1024; - -impl WasiHttpView for &mut T { - fn ctx(&mut self) -> &mut WasiHttpCtx { - T::ctx(self) - } - - fn table(&mut self) -> &mut ResourceTable { - T::table(self) - } - - fn new_response_outparam( - &mut self, - result: tokio::sync::oneshot::Sender< - Result, types::ErrorCode>, - >, - ) -> wasmtime::Result> { - T::new_response_outparam(self, result) - } - - fn send_request( - &mut self, - request: hyper::Request, - config: OutgoingRequestConfig, - ) -> crate::HttpResult { - T::send_request(self, request, config) - } - - fn is_forbidden_header(&mut self, name: &HeaderName) -> bool { - T::is_forbidden_header(self, name) - } - - fn outgoing_body_buffer_chunks(&mut self) -> usize { - T::outgoing_body_buffer_chunks(self) - } - - fn outgoing_body_chunk_size(&mut self) -> usize { - T::outgoing_body_chunk_size(self) - } -} - -impl WasiHttpView for Box { - fn ctx(&mut self) -> &mut WasiHttpCtx { - T::ctx(self) - } - - fn table(&mut self) -> &mut ResourceTable { - T::table(self) - } - - fn new_response_outparam( - &mut self, - result: tokio::sync::oneshot::Sender< - Result, types::ErrorCode>, - >, - ) -> wasmtime::Result> { - T::new_response_outparam(self, result) - } - - fn send_request( - &mut self, - request: hyper::Request, - config: OutgoingRequestConfig, - ) -> crate::HttpResult { - T::send_request(self, request, config) - } - - fn is_forbidden_header(&mut self, name: &HeaderName) -> bool { - T::is_forbidden_header(self, name) - } - - fn outgoing_body_buffer_chunks(&mut self) -> usize { - T::outgoing_body_buffer_chunks(self) - } - - fn outgoing_body_chunk_size(&mut self) -> usize { - T::outgoing_body_chunk_size(self) - } -} - -/// A concrete structure that all generated `Host` traits are implemented for. -/// -/// This type serves as a small newtype wrapper to implement all of the `Host` -/// traits for `wasi:http`. This type is internally used and is only needed if -/// you're interacting with `add_to_linker` functions generated by bindings -/// themselves (or `add_to_linker_get_host`). -/// -/// This type is automatically used when using -/// [`add_to_linker_async`](crate::add_to_linker_async) -/// or -/// [`add_to_linker_sync`](crate::add_to_linker_sync) -/// and doesn't need to be manually configured. -#[repr(transparent)] -pub struct WasiHttpImpl(pub T); - -impl WasiHttpView for WasiHttpImpl { - fn ctx(&mut self) -> &mut WasiHttpCtx { - self.0.ctx() - } - - fn table(&mut self) -> &mut ResourceTable { - self.0.table() - } - - fn new_response_outparam( - &mut self, - result: tokio::sync::oneshot::Sender< - Result, types::ErrorCode>, - >, - ) -> wasmtime::Result> { - self.0.new_response_outparam(result) - } - - fn send_request( - &mut self, - request: hyper::Request, - config: OutgoingRequestConfig, - ) -> crate::HttpResult { - self.0.send_request(request, config) - } - - fn is_forbidden_header(&mut self, name: &HeaderName) -> bool { - self.0.is_forbidden_header(name) - } - - fn outgoing_body_buffer_chunks(&mut self) -> usize { - self.0.outgoing_body_buffer_chunks() - } - - fn outgoing_body_chunk_size(&mut self) -> usize { - self.0.outgoing_body_chunk_size() - } -} - -/// Set of [http::header::HeaderName], that are forbidden by default -/// for requests and responses originating in the guest. -pub const DEFAULT_FORBIDDEN_HEADERS: [http::header::HeaderName; 9] = [ - hyper::header::CONNECTION, - HeaderName::from_static("keep-alive"), - hyper::header::PROXY_AUTHENTICATE, - hyper::header::PROXY_AUTHORIZATION, - HeaderName::from_static("proxy-connection"), - hyper::header::TRANSFER_ENCODING, - hyper::header::UPGRADE, - hyper::header::HOST, - HeaderName::from_static("http2-settings"), -]; - -/// Removes forbidden headers from a [`FieldMap`]. -pub(crate) fn remove_forbidden_headers(view: &mut dyn WasiHttpView, headers: &mut FieldMap) { - let forbidden_keys = Vec::from_iter(headers.as_ref().keys().filter_map(|name| { - if view.is_forbidden_header(name) { - Some(name.clone()) - } else { - None - } - })); - - for name in forbidden_keys { - headers.remove_all(&name); - } -} - -/// Configuration for an outgoing request. -pub struct OutgoingRequestConfig { - /// Whether to use TLS for the request. - pub use_tls: bool, - /// The timeout for connecting. - pub connect_timeout: Duration, - /// The timeout until the first byte. - pub first_byte_timeout: Duration, - /// The timeout between chunks of a streaming body - pub between_bytes_timeout: Duration, -} - -/// The default implementation of how an outgoing request is sent. -/// -/// This implementation is used by the `wasi:http/outgoing-handler` interface -/// default implementation. -#[cfg(feature = "default-send-request")] -pub fn default_send_request( - request: hyper::Request, - config: OutgoingRequestConfig, -) -> HostFutureIncomingResponse { - let handle = wasmtime_wasi::runtime::spawn(async move { - Ok(default_send_request_handler(request, config).await) - }); - HostFutureIncomingResponse::pending(handle) -} - -/// The underlying implementation of how an outgoing request is sent. This should likely be spawned -/// in a task. -/// -/// This is called from [default_send_request] to actually send the request. -#[cfg(feature = "default-send-request")] -pub async fn default_send_request_handler( - mut request: hyper::Request, - OutgoingRequestConfig { - use_tls, - connect_timeout, - first_byte_timeout, - between_bytes_timeout, - }: OutgoingRequestConfig, -) -> Result { - let authority = if let Some(authority) = request.uri().authority() { - if authority.port().is_some() { - authority.to_string() - } else { - let port = if use_tls { 443 } else { 80 }; - format!("{}:{port}", authority.to_string()) - } - } else { - return Err(types::ErrorCode::HttpRequestUriInvalid); - }; - let tcp_stream = timeout(connect_timeout, TcpStream::connect(&authority)) - .await - .map_err(|_| types::ErrorCode::ConnectionTimeout)? - .map_err(|e| match e.kind() { - std::io::ErrorKind::AddrNotAvailable => { - dns_error("address not available".to_string(), 0) - } - - _ => { - if e.to_string() - .starts_with("failed to lookup address information") - { - dns_error("address not available".to_string(), 0) - } else { - types::ErrorCode::ConnectionRefused - } - } - })?; - - let (mut sender, worker) = if use_tls { - use rustls::pki_types::ServerName; - - // derived from https://github.com/rustls/rustls/blob/main/examples/src/bin/simpleclient.rs - let root_cert_store = rustls::RootCertStore { - roots: webpki_roots::TLS_SERVER_ROOTS.into(), - }; - let config = rustls::ClientConfig::builder() - .with_root_certificates(root_cert_store) - .with_no_client_auth(); - let connector = tokio_rustls::TlsConnector::from(std::sync::Arc::new(config)); - let mut parts = authority.split(":"); - let host = parts.next().unwrap_or(&authority); - let domain = ServerName::try_from(host) - .map_err(|e| { - tracing::warn!("dns lookup error: {e:?}"); - dns_error("invalid dns name".to_string(), 0) - })? - .to_owned(); - let stream = connector.connect(domain, tcp_stream).await.map_err(|e| { - tracing::warn!("tls protocol error: {e:?}"); - types::ErrorCode::TlsProtocolError - })?; - let stream = TokioIo::new(stream); - - let (sender, conn) = timeout( - connect_timeout, - hyper::client::conn::http1::handshake(stream), - ) - .await - .map_err(|_| types::ErrorCode::ConnectionTimeout)? - .map_err(hyper_request_error)?; - - let worker = wasmtime_wasi::runtime::spawn(async move { - match conn.await { - Ok(()) => {} - // TODO: shouldn't throw away this error and ideally should - // surface somewhere. - Err(e) => tracing::warn!("dropping error {e}"), - } - }); - - (sender, worker) - } else { - let tcp_stream = TokioIo::new(tcp_stream); - let (sender, conn) = timeout( - connect_timeout, - // TODO: we should plumb the builder through the http context, and use it here - hyper::client::conn::http1::handshake(tcp_stream), - ) - .await - .map_err(|_| types::ErrorCode::ConnectionTimeout)? - .map_err(hyper_request_error)?; - - let worker = wasmtime_wasi::runtime::spawn(async move { - match conn.await { - Ok(()) => {} - // TODO: same as above, shouldn't throw this error away. - Err(e) => tracing::warn!("dropping error {e}"), - } - }); - - (sender, worker) - }; - - // at this point, the request contains the scheme and the authority, but - // the http packet should only include those if addressing a proxy, so - // remove them here, since SendRequest::send_request does not do it for us - *request.uri_mut() = http::Uri::builder() - .path_and_query( - request - .uri() - .path_and_query() - .map(|p| p.as_str()) - .unwrap_or("/"), - ) - .build() - .expect("comes from valid request"); - - let resp = timeout(first_byte_timeout, sender.send_request(request)) - .await - .map_err(|_| types::ErrorCode::ConnectionReadTimeout)? - .map_err(hyper_request_error)? - .map(|body| body.map_err(hyper_request_error).boxed_unsync()); - - Ok(IncomingResponse { - resp, - worker: Some(worker), - between_bytes_timeout, - }) -} - -impl From for types::Method { - fn from(method: http::Method) -> Self { - if method == http::Method::GET { - types::Method::Get - } else if method == hyper::Method::HEAD { - types::Method::Head - } else if method == hyper::Method::POST { - types::Method::Post - } else if method == hyper::Method::PUT { - types::Method::Put - } else if method == hyper::Method::DELETE { - types::Method::Delete - } else if method == hyper::Method::CONNECT { - types::Method::Connect - } else if method == hyper::Method::OPTIONS { - types::Method::Options - } else if method == hyper::Method::TRACE { - types::Method::Trace - } else if method == hyper::Method::PATCH { - types::Method::Patch - } else { - types::Method::Other(method.to_string()) - } - } -} - -impl TryInto for types::Method { - type Error = http::method::InvalidMethod; - - fn try_into(self) -> Result { - match self { - Method::Get => Ok(http::Method::GET), - Method::Head => Ok(http::Method::HEAD), - Method::Post => Ok(http::Method::POST), - Method::Put => Ok(http::Method::PUT), - Method::Delete => Ok(http::Method::DELETE), - Method::Connect => Ok(http::Method::CONNECT), - Method::Options => Ok(http::Method::OPTIONS), - Method::Trace => Ok(http::Method::TRACE), - Method::Patch => Ok(http::Method::PATCH), - Method::Other(s) => http::Method::from_bytes(s.as_bytes()), - } - } -} - -/// The concrete type behind a `wasi:http/types.incoming-request` resource. -#[derive(Debug)] -pub struct HostIncomingRequest { - pub(crate) method: http::method::Method, - pub(crate) uri: http::uri::Uri, - pub(crate) headers: FieldMap, - pub(crate) scheme: Scheme, - pub(crate) authority: String, - /// The body of the incoming request. - pub body: Option, -} - -impl HostIncomingRequest { - /// Create a new `HostIncomingRequest`. - pub fn new( - view: &mut dyn WasiHttpView, - parts: http::request::Parts, - scheme: Scheme, - body: Option, - field_size_limit: usize, - ) -> wasmtime::Result { - let authority = match parts.uri.authority() { - Some(authority) => authority.to_string(), - None => match parts.headers.get(http::header::HOST) { - Some(host) => host.to_str()?.to_string(), - None => bail!("invalid HTTP request missing authority in URI and host header"), - }, - }; - - let mut headers = FieldMap::new(parts.headers, field_size_limit); - remove_forbidden_headers(view, &mut headers); - - Ok(Self { - method: parts.method, - uri: parts.uri, - headers, - authority, - scheme, - body, - }) - } -} - -/// The concrete type behind a `wasi:http/types.response-outparam` resource. -pub struct HostResponseOutparam { - /// The sender for sending a response. - pub result: - tokio::sync::oneshot::Sender, types::ErrorCode>>, -} - -/// The concrete type behind a `wasi:http/types.outgoing-response` resource. -pub struct HostOutgoingResponse { - /// The status of the response. - pub status: http::StatusCode, - /// The headers of the response. - pub headers: FieldMap, - /// The body of the response. - pub body: Option, -} - -impl TryFrom for hyper::Response { - type Error = http::Error; - - fn try_from( - resp: HostOutgoingResponse, - ) -> Result, Self::Error> { - use http_body_util::Empty; - - let mut builder = hyper::Response::builder().status(resp.status); - - *builder.headers_mut().unwrap() = resp.headers.map; - - match resp.body { - Some(body) => builder.body(body), - None => builder.body( - Empty::::new() - .map_err(|_| unreachable!("Infallible error")) - .boxed_unsync(), - ), - } - } -} - -/// The concrete type behind a `wasi:http/types.outgoing-request` resource. -#[derive(Debug)] -pub struct HostOutgoingRequest { - /// The method of the request. - pub method: Method, - /// The scheme of the request. - pub scheme: Option, - /// The authority of the request. - pub authority: Option, - /// The path and query of the request. - pub path_with_query: Option, - /// The request headers. - pub headers: FieldMap, - /// The request body. - pub body: Option, -} - -/// The concrete type behind a `wasi:http/types.request-options` resource. -#[derive(Debug, Default)] -pub struct HostRequestOptions { - /// How long to wait for a connection to be established. - pub connect_timeout: Option, - /// How long to wait for the first byte of the response body. - pub first_byte_timeout: Option, - /// How long to wait between frames of the response body. - pub between_bytes_timeout: Option, -} - -/// The concrete type behind a `wasi:http/types.incoming-response` resource. -#[derive(Debug)] -pub struct HostIncomingResponse { - /// The response status - pub status: u16, - /// The response headers - pub headers: FieldMap, - /// The response body - pub body: Option, -} - -/// The concrete type behind a `wasi:http/types.fields` resource. -#[derive(Debug)] -pub enum HostFields { - /// A reference to the fields of a parent entry. - Ref { - /// The parent resource rep. - parent: u32, - - /// The function to get the fields from the parent. - // NOTE: there's not failure in the result here because we assume that HostFields will - // always be registered as a child of the entry with the `parent` id. This ensures that the - // entry will always exist while this `HostFields::Ref` entry exists in the table, thus we - // don't need to account for failure when fetching the fields ref from the parent. - get_fields: for<'a> fn(elem: &'a mut (dyn Any + 'static)) -> &'a mut FieldMap, - }, - /// An owned version of the fields. - Owned { - /// The fields themselves. - fields: FieldMap, - }, -} - -/// An owned version of `HostFields`. A wrapper on http `HeaderMap` that -/// keeps a running tally of memory consumed by header names and values. -#[derive(Debug, Clone)] -pub struct FieldMap { - map: HeaderMap, - limit: usize, - size: usize, -} - -/// Error given when a `FieldMap` has exceeded the size limit. -#[derive(Debug)] -pub struct FieldSizeLimitError { - /// The erroring `FieldMap` operation would require this content size - pub(crate) size: usize, - /// The limit set on `FieldMap` content size - pub(crate) limit: usize, -} -impl fmt::Display for FieldSizeLimitError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Field size limit {} exceeded: {}", self.limit, self.size) - } -} -impl std::error::Error for FieldSizeLimitError {} - -impl FieldMap { - /// Construct a `FieldMap` from a `HeaderMap` and a size limit. - /// - /// Construction with a `HeaderMap` which exceeds the size limit is - /// allowed, but subsequent operations to expand the resource use will - /// fail. - pub fn new(map: HeaderMap, limit: usize) -> Self { - let size = Self::content_size(&map); - Self { map, size, limit } - } - /// Construct an empty `FieldMap` - pub fn empty(limit: usize) -> Self { - Self { - map: HeaderMap::new(), - size: 0, - limit, - } - } - /// Get the `HeaderMap` out of the `FieldMap` - pub fn into_inner(self) -> HeaderMap { - self.map - } - /// Calculate the content size of a `HeaderMap`. This is a sum of the size - /// of all of the keys and all of the values. - pub(crate) fn content_size(map: &HeaderMap) -> usize { - let mut sum = 0; - for key in map.keys() { - sum += header_name_size(key); - } - for value in map.values() { - sum += header_value_size(value); - } - sum - } - /// Remove all values associated with a key in a map. - /// - /// Returns an empty list if the key is not already present within the map. - pub fn remove_all(&mut self, key: &HeaderName) -> Vec { - use http::header::Entry; - match self.map.try_entry(key) { - Ok(Entry::Vacant { .. }) | Err(_) => Vec::new(), - Ok(Entry::Occupied(e)) => { - let (name, value_drain) = e.remove_entry_mult(); - let mut removed = header_name_size(&name); - let values = value_drain.collect::>(); - for v in values.iter() { - removed += header_value_size(v); - } - self.size -= removed; - values - } - } - } - /// Add a value associated with a key to the map. - /// - /// If `key` is already present within the map then `value` is appended to - /// the list of values it already has. - pub fn append(&mut self, key: &HeaderName, value: HeaderValue) -> Result { - let key_size = header_name_size(key); - let val_size = header_value_size(&value); - let new_size = if !self.map.contains_key(key) { - self.size + key_size + val_size - } else { - self.size + val_size - }; - if new_size > self.limit { - bail!(FieldSizeLimitError { - limit: self.limit, - size: new_size - }) - } - self.size = new_size; - Ok(self.map.try_append(key, value)?) - } -} - -/// Returns the size, in accounting cost, to consider for `name`. -/// -/// This includes both the byte length of the `name` itself as well as the size -/// of the data structure itself as it'll reside within a `HeaderMap`. -fn header_name_size(name: &HeaderName) -> usize { - name.as_str().len() + size_of::() -} - -/// Same as `header_name_size`, but for values. -/// -/// This notably includes the size of `HeaderValue` itself to ensure that all -/// headers have a nonzero size as otherwise this would never limit addition of -/// an empty header value. -fn header_value_size(value: &HeaderValue) -> usize { - value.len() + size_of::() -} - -// We impl AsRef, but not AsMut, because any modifications of the -// underlying HeaderMap must account for changes in size -impl AsRef for FieldMap { - fn as_ref(&self) -> &HeaderMap { - &self.map - } -} - -/// A handle to a future incoming response. -pub type FutureIncomingResponseHandle = - AbortOnDropJoinHandle>>; - -/// A response that is in the process of being received. -#[derive(Debug)] -pub struct IncomingResponse { - /// The response itself. - pub resp: hyper::Response, - /// Optional worker task that continues to process the response. - pub worker: Option>, - /// The timeout between chunks of the response. - pub between_bytes_timeout: std::time::Duration, -} - -/// The concrete type behind a `wasi:http/types.future-incoming-response` resource. -#[derive(Debug)] -pub enum HostFutureIncomingResponse { - /// A pending response - Pending(FutureIncomingResponseHandle), - /// The response is ready. - /// - /// An outer error will trap while the inner error gets returned to the guest. - Ready(wasmtime::Result>), - /// The response has been consumed. - Consumed, -} - -impl HostFutureIncomingResponse { - /// Create a new `HostFutureIncomingResponse` that is pending on the provided task handle. - pub fn pending(handle: FutureIncomingResponseHandle) -> Self { - Self::Pending(handle) - } - - /// Create a new `HostFutureIncomingResponse` that is ready. - pub fn ready(result: wasmtime::Result>) -> Self { - Self::Ready(result) - } - - /// Returns `true` if the response is ready. - pub fn is_ready(&self) -> bool { - matches!(self, Self::Ready(_)) - } - - /// Unwrap the response, panicking if it is not ready. - pub fn unwrap_ready(self) -> wasmtime::Result> { - match self { - Self::Ready(res) => res, - Self::Pending(_) | Self::Consumed => { - panic!("unwrap_ready called on a pending HostFutureIncomingResponse") - } - } - } -} - -#[async_trait::async_trait] -impl Pollable for HostFutureIncomingResponse { - async fn ready(&mut self) { - if let Self::Pending(handle) = self { - *self = Self::Ready(handle.await); - } - } -} diff --git a/crates/wasi-http/tests/all/p2.rs b/crates/wasi-http/tests/all/p2.rs index 9d070789d027..382cf0c03965 100644 --- a/crates/wasi-http/tests/all/p2.rs +++ b/crates/wasi-http/tests/all/p2.rs @@ -15,11 +15,12 @@ use wasmtime::{ }; use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView, p2::pipe::MemoryOutputPipe}; use wasmtime_wasi_http::{ - HttpResult, WasiHttpCtx, WasiHttpView, - bindings::http::types::{ErrorCode, Scheme}, - body::HyperOutgoingBody, + WasiHttpCtx, io::TokioIo, - types::{self, HostFutureIncomingResponse, IncomingResponse, OutgoingRequestConfig}, + p2::bindings::http::types::{ErrorCode, Scheme}, + p2::body::HyperOutgoingBody, + p2::types::{HostFutureIncomingResponse, IncomingResponse, OutgoingRequestConfig}, + p2::{HttpResult, WasiHttpCtxView, WasiHttpHooks, WasiHttpView}, }; type RequestSender = Arc< @@ -34,6 +35,10 @@ struct Ctx { http: WasiHttpCtx, stdout: MemoryOutputPipe, stderr: MemoryOutputPipe, + hooks: MyHttpHooks, +} + +struct MyHttpHooks { send_request: Option, rejected_authority: Option, } @@ -48,14 +53,16 @@ impl WasiView for Ctx { } impl WasiHttpView for Ctx { - fn ctx(&mut self) -> &mut WasiHttpCtx { - &mut self.http - } - - fn table(&mut self) -> &mut ResourceTable { - &mut self.table + fn http(&mut self) -> WasiHttpCtxView<'_> { + WasiHttpCtxView { + ctx: &mut self.http, + table: &mut self.table, + hooks: &mut self.hooks, + } } +} +impl WasiHttpHooks for MyHttpHooks { fn send_request( &mut self, request: hyper::Request, @@ -70,12 +77,14 @@ impl WasiHttpView for Ctx { if let Some(send_request) = self.send_request.clone() { Ok(send_request(request, config)) } else { - Ok(types::default_send_request(request, config)) + Ok(wasmtime_wasi_http::p2::default_send_request( + request, config, + )) } } fn is_forbidden_header(&mut self, name: &hyper::header::HeaderName) -> bool { - types::DEFAULT_FORBIDDEN_HEADERS.contains(name) + wasmtime_wasi_http::DEFAULT_FORBIDDEN_HEADERS.contains(name) || name.as_str() == "custom-forbidden-header" } } @@ -95,8 +104,10 @@ fn store(engine: &Engine, server: &Server) -> Store { http: WasiHttpCtx::new(), stderr, stdout, - send_request: None, - rejected_authority: None, + hooks: MyHttpHooks { + send_request: None, + rejected_authority: None, + }, }; Store::new(&engine, ctx) @@ -152,26 +163,30 @@ async fn run_wasi_http( http, stderr, stdout, - send_request, - rejected_authority, + hooks: MyHttpHooks { + send_request, + rejected_authority, + }, }; let mut store = Store::new(&engine, ctx); let mut linker = Linker::new(&engine); - wasmtime_wasi_http::add_to_linker_async(&mut linker).context("add crate to linker")?; + wasmtime_wasi_http::p2::add_to_linker_async(&mut linker).context("add crate to linker")?; let proxy = - wasmtime_wasi_http::bindings::Proxy::instantiate_async(&mut store, &component, &linker) + wasmtime_wasi_http::p2::bindings::Proxy::instantiate_async(&mut store, &component, &linker) .await .context("instantiate proxy")?; let req = store .data_mut() + .http() .new_incoming_request(Scheme::Http, req) .context("new incoming request")?; let (sender, receiver) = tokio::sync::oneshot::channel(); let out = store .data_mut() + .http() .new_response_outparam(sender) .context("new response outparam")?; @@ -298,7 +313,7 @@ async fn do_wasi_http_hash_all(override_send_request: bool) -> Result<()> { let response = handle(request.into_parts().0).map(|resp| { Ok(IncomingResponse { resp: resp.map(|body| { - body.map_err(wasmtime_wasi_http::hyper_response_error) + body.map_err(wasmtime_wasi_http::p2::hyper_response_error) .boxed_unsync() }), worker: None, @@ -605,10 +620,10 @@ async fn wasi_http_no_trap_on_early_drop() -> Result<()> { #[test_log::test(tokio::test)] async fn wasi_http_fields_limit_incoming_request() -> Result<()> { - use crate::p2::types::FieldSizeLimitError; use http::{HeaderName, HeaderValue, Request}; use http_body_util::combinators::BoxBody; use hyper::Error; + use wasmtime_wasi_http::FieldMapError; fn request_with_header_size(uri: &str, total: usize) -> Request> { let mut builder = hyper::Request::builder().uri(uri).method(http::Method::GET); @@ -669,7 +684,7 @@ async fn wasi_http_fields_limit_incoming_request() -> Result<()> { .await .err() .expect("new_fields exceeding the size limit"); - assert!(err.downcast_ref::().is_some()); + assert!(err.downcast_ref::().is_some()); let resp = run_wasi_http( test_programs_artifacts::P2_API_PROXY_COMPONENT, @@ -693,7 +708,7 @@ async fn wasi_http_fields_limit_incoming_request() -> Result<()> { .await .err() .expect("run_wasi_http should give error"); - assert!(err.downcast_ref::().is_some()); + assert!(err.downcast_ref::().is_some()); Ok(()) } diff --git a/crates/wasi-http/tests/all/p2/async_.rs b/crates/wasi-http/tests/all/p2/async_.rs index f5a8674255e5..176a713cd67b 100644 --- a/crates/wasi-http/tests/all/p2/async_.rs +++ b/crates/wasi-http/tests/all/p2/async_.rs @@ -12,7 +12,7 @@ async fn run(path: &str, server: &Server) -> Result<()> { let mut store = store(&engine, server); let mut linker = Linker::new(&engine); wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; - wasmtime_wasi_http::add_only_http_to_linker_async(&mut linker)?; + wasmtime_wasi_http::p2::add_only_http_to_linker_async(&mut linker)?; let command = Command::instantiate_async(&mut store, &component, &linker).await?; let result = command.wasi_cli_run().call_run(&mut store).await?; result.map_err(|()| wasmtime::format_err!("run returned an error")) diff --git a/crates/wasi-http/tests/all/p2/sync.rs b/crates/wasi-http/tests/all/p2/sync.rs index 5ac57caa427e..a715c4ba31d2 100644 --- a/crates/wasi-http/tests/all/p2/sync.rs +++ b/crates/wasi-http/tests/all/p2/sync.rs @@ -12,7 +12,7 @@ fn run(path: &str, server: &Server) -> Result<()> { let mut store = store(&engine, server); let mut linker = Linker::new(&engine); wasmtime_wasi::p2::add_to_linker_sync(&mut linker)?; - wasmtime_wasi_http::add_only_http_to_linker_sync(&mut linker)?; + wasmtime_wasi_http::p2::add_only_http_to_linker_sync(&mut linker)?; let command = Command::instantiate(&mut store, &component, &linker)?; let result = command.wasi_cli_run().call_run(&mut store)?; result.map_err(|()| wasmtime::format_err!("run returned an error")) diff --git a/crates/wasi-http/tests/all/p3/mod.rs b/crates/wasi-http/tests/all/p3/mod.rs index 9d11a50ecd39..af3fdb0b9fcd 100644 --- a/crates/wasi-http/tests/all/p3/mod.rs +++ b/crates/wasi-http/tests/all/p3/mod.rs @@ -22,17 +22,17 @@ use wasmtime_wasi::{TrappableError, WasiCtx, WasiCtxBuilder, WasiCtxView, WasiVi use wasmtime_wasi_http::p3::bindings::Service; use wasmtime_wasi_http::p3::bindings::http::types::ErrorCode; use wasmtime_wasi_http::p3::{ - self, Request, RequestOptions, WasiHttpCtx, WasiHttpCtxView, WasiHttpView, + self, Request, RequestOptions, WasiHttpCtxView, WasiHttpHooks, WasiHttpView, }; -use wasmtime_wasi_http::types::DEFAULT_FORBIDDEN_HEADERS; +use wasmtime_wasi_http::{DEFAULT_FORBIDDEN_HEADERS, WasiHttpCtx}; foreach_p3_http!(assert_test_exists); -struct TestHttpCtx { +struct TestHooks { request_body_tx: Option>>, } -impl WasiHttpCtx for TestHttpCtx { +impl WasiHttpHooks for TestHooks { fn is_forbidden_header(&mut self, name: &http::header::HeaderName) -> bool { name.as_str() == "custom-forbidden-header" || DEFAULT_FORBIDDEN_HEADERS.contains(name) } @@ -83,7 +83,8 @@ impl WasiHttpCtx for TestHttpCtx { struct Ctx { table: ResourceTable, wasi: WasiCtx, - http: TestHttpCtx, + http: WasiHttpCtx, + hooks: TestHooks, } impl Ctx { @@ -91,7 +92,8 @@ impl Ctx { Self { table: ResourceTable::default(), wasi: WasiCtxBuilder::new().inherit_stdio().build(), - http: TestHttpCtx { + http: WasiHttpCtx::new(), + hooks: TestHooks { request_body_tx: Some(request_body_tx), }, } @@ -112,6 +114,7 @@ impl WasiHttpView for Ctx { WasiHttpCtxView { ctx: &mut self.http, table: &mut self.table, + hooks: &mut self.hooks, } } } @@ -319,7 +322,8 @@ async fn p3_http_middleware() -> Result<()> { async fn p3_http_middleware_host_to_host() { let error = format!("{:?}", test_http_middleware(true).await.unwrap_err()); - let expected = "cannot read from and write to intra-component future with non-numeric payload"; + let expected = + "cannot read from and write to intra-component future/stream with non-numeric payload"; assert!( error.contains(expected), @@ -401,7 +405,7 @@ async fn test_http_middleware_with_chain(host_to_host: bool) -> Result<()> { "local:local/chain-http".to_owned(), InstantiationArg { instance: "local:local/chain-http".into(), - export: Some("wasi:http/handler@0.3.0-rc-2026-02-09".into()), + export: Some("wasi:http/handler@0.3.0-rc-2026-03-15".into()), }, )] .into_iter() @@ -727,3 +731,93 @@ async fn p3_http_data_frame_at_end_of_stream() -> Result<()> { assert_eq!(collected_body, expected.as_slice()); Ok(()) } + +/// Body wrapper that interleaves zero-length data frames with real data. +/// +/// Zero-length data frames are valid per the `http_body::Body` trait contract and +/// RFC 9113 §6.1. This wrapper injects `empty_per_frame` empty frames before each +/// real frame from the inner body, testing that `HostBodyStreamProducer` tolerates them. +struct BodyWithEmptyFrames { + inner: http_body_util::StreamBody< + futures::channel::mpsc::Receiver, ErrorCode>>, + >, + /// Number of empty frames to inject before each real frame + empty_per_frame: usize, + /// Number of empty frames left to inject before the next real frame + empty_remaining: usize, +} + +impl http_body::Body for BodyWithEmptyFrames { + type Data = Bytes; + type Error = ErrorCode; + + fn poll_frame( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + if self.empty_remaining > 0 { + self.empty_remaining -= 1; + return Poll::Ready(Some(Ok(http_body::Frame::data(Bytes::new())))); + } + let this = &mut *self; + let result = Pin::new(&mut this.inner).poll_frame(cx); + if matches!(&result, Poll::Ready(Some(Ok(_)))) { + // Reset counter so empty frames are injected before the next real frame + this.empty_remaining = this.empty_per_frame; + } + result + } + + fn is_end_stream(&self) -> bool { + false + } +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn p3_http_empty_frames_interleaved() -> Result<()> { + _ = env_logger::try_init(); + + // Verifies that zero-length data frames interleaved with real data do not cause + // poll_produce to return Completed with 0 items produced. + // Pattern: empty, empty, data("hello "), empty, empty, data("world") + + let (mut body_tx, body_rx) = futures::channel::mpsc::channel::>(2); + + let wrapped_body = BodyWithEmptyFrames { + inner: http_body_util::StreamBody::new(body_rx), + empty_per_frame: 2, + empty_remaining: 2, + }; + + let request = http::Request::builder() + .uri("http://localhost/") + .method(http::Method::GET); + + let response = futures::join!( + run_http( + P3_HTTP_ECHO_COMPONENT, + request.body(wrapped_body)?, + oneshot::channel().0 + ), + async { + body_tx + .send(Ok(http_body::Frame::data(Bytes::from_static(b"hello ")))) + .await + .unwrap(); + body_tx + .send(Ok(http_body::Frame::data(Bytes::from_static(b"world")))) + .await + .unwrap(); + drop(body_tx); + } + ) + .0? + .unwrap(); + + assert_eq!(response.status().as_u16(), 200); + + let (_, collected_body) = response.into_parts(); + let collected_body = collected_body.to_bytes(); + assert_eq!(collected_body, b"hello world".as_slice()); + Ok(()) +} diff --git a/crates/wasi-io/src/impls.rs b/crates/wasi-io/src/impls.rs index bd60985cc770..a5a65b67b141 100644 --- a/crates/wasi-io/src/impls.rs +++ b/crates/wasi-io/src/impls.rs @@ -46,10 +46,10 @@ impl poll::Host for ResourceTable { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut any_ready = false; let mut results = Vec::new(); - for (fut, readylist_indicies) in self.futures.iter_mut() { + for (fut, readylist_indices) in self.futures.iter_mut() { match fut.as_mut().poll(cx) { Poll::Ready(()) => { - results.extend_from_slice(readylist_indicies); + results.extend_from_slice(readylist_indices); any_ready = true; } Poll::Pending => {} diff --git a/crates/wasi-preview1-component-adapter/build.rs b/crates/wasi-preview1-component-adapter/build.rs index e4b87e021333..2b1f7f95d0ef 100644 --- a/crates/wasi-preview1-component-adapter/build.rs +++ b/crates/wasi-preview1-component-adapter/build.rs @@ -239,7 +239,7 @@ fn build_raw_intrinsics() -> Vec { /// Like above this is still tricky, mainly around the production of the symbol /// table. fn build_archive(wasm: &[u8]) -> Vec { - use object::{U32Bytes, bytes_of, endian::BigEndian}; + use object::{U32, bytes_of, endian::BigEndian}; let mut archive = Vec::new(); archive.extend_from_slice(&object::archive::MAGIC); @@ -265,9 +265,9 @@ fn build_archive(wasm: &[u8]) -> Vec { ]; let mut symbol_table = Vec::new(); - symbol_table.extend_from_slice(bytes_of(&U32Bytes::new(BigEndian, syms.len() as u32))); + symbol_table.extend_from_slice(bytes_of(&U32::new(BigEndian, syms.len() as u32))); for _ in syms.iter() { - symbol_table.extend_from_slice(bytes_of(&U32Bytes::new(BigEndian, 0))); + symbol_table.extend_from_slice(bytes_of(&U32::new(BigEndian, 0))); } for s in syms.iter() { symbol_table.extend_from_slice(&std::ffi::CString::new(*s).unwrap().into_bytes_with_nul()); @@ -298,7 +298,7 @@ fn build_archive(wasm: &[u8]) -> Vec { let member_offset = archive.len(); for (index, _) in syms.iter().enumerate() { let index = index + 1; - archive[symtab_offset + (index * 4)..][..4].copy_from_slice(bytes_of(&U32Bytes::new( + archive[symtab_offset + (index * 4)..][..4].copy_from_slice(bytes_of(&U32::new( BigEndian, member_offset.try_into().unwrap(), ))); diff --git a/crates/wasi-tls-nativetls/Cargo.toml b/crates/wasi-tls-nativetls/Cargo.toml deleted file mode 100644 index dd7bf05e09c2..000000000000 --- a/crates/wasi-tls-nativetls/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "wasmtime-wasi-tls-nativetls" -version.workspace = true -authors.workspace = true -edition.workspace = true -rust-version.workspace = true -repository = "https://github.com/bytecodealliance/wasmtime" -license = "Apache-2.0 WITH LLVM-exception" -description = "Wasmtime implementation of the wasi-tls API, using native-tls for TLS support." - -[lints] -workspace = true - -[dependencies] -wasmtime-wasi-tls = { workspace = true } -tokio = { workspace = true } -tokio-native-tls = { workspace = true } -native-tls = { workspace = true } - -[dev-dependencies] -test-programs-artifacts = { workspace = true } -wasmtime = { workspace = true, features = ["runtime", "component-model"] } -wasmtime-wasi = { workspace = true } -tokio = { workspace = true, features = ["macros"] } -futures = { workspace = true } diff --git a/crates/wasi-tls-nativetls/tests/main.rs b/crates/wasi-tls-nativetls/tests/main.rs deleted file mode 100644 index 0aa344a815ec..000000000000 --- a/crates/wasi-tls-nativetls/tests/main.rs +++ /dev/null @@ -1,68 +0,0 @@ -use wasmtime::{ - Result, Store, - component::{Component, Linker, ResourceTable}, - format_err, -}; -use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView, p2::bindings::Command}; -use wasmtime_wasi_tls::{LinkOptions, WasiTls, WasiTlsCtx, WasiTlsCtxBuilder}; - -struct Ctx { - table: ResourceTable, - wasi_ctx: WasiCtx, - wasi_tls_ctx: WasiTlsCtx, -} - -impl WasiView for Ctx { - fn ctx(&mut self) -> WasiCtxView<'_> { - WasiCtxView { - ctx: &mut self.wasi_ctx, - table: &mut self.table, - } - } -} - -async fn run_test(path: &str) -> Result<()> { - let provider = Box::new(wasmtime_wasi_tls_nativetls::NativeTlsProvider::default()); - let ctx = Ctx { - table: ResourceTable::new(), - wasi_ctx: WasiCtx::builder() - .inherit_stderr() - .inherit_network() - .allow_ip_name_lookup(true) - .build(), - wasi_tls_ctx: WasiTlsCtxBuilder::new().provider(provider).build(), - }; - - let engine = test_programs_artifacts::engine(|_config| {}); - let mut store = Store::new(&engine, ctx); - let component = Component::from_file(&engine, path)?; - - let mut linker = Linker::new(&engine); - wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; - let mut opts = LinkOptions::default(); - opts.tls(true); - wasmtime_wasi_tls::add_to_linker(&mut linker, &mut opts, |h: &mut Ctx| { - WasiTls::new(&h.wasi_tls_ctx, &mut h.table) - })?; - - let command = Command::instantiate_async(&mut store, &component, &linker).await?; - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| format_err!("command returned with failing exit status")) -} - -macro_rules! assert_test_exists { - ($name:ident) => { - #[expect(unused_imports, reason = "just here to assert it exists")] - use self::$name as _; - }; -} - -test_programs_artifacts::foreach_tls!(assert_test_exists); - -#[tokio::test(flavor = "multi_thread")] -async fn tls_sample_application() -> Result<()> { - run_test(test_programs_artifacts::TLS_SAMPLE_APPLICATION_COMPONENT).await -} diff --git a/crates/wasi-tls-openssl/Cargo.toml b/crates/wasi-tls-openssl/Cargo.toml deleted file mode 100644 index ec7f02552bdc..000000000000 --- a/crates/wasi-tls-openssl/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "wasmtime-wasi-tls-openssl" -version.workspace = true -authors.workspace = true -edition.workspace = true -rust-version.workspace = true -repository = "https://github.com/bytecodealliance/wasmtime" -license = "Apache-2.0 WITH LLVM-exception" -description = "Wasmtime implementation of the wasi-tls API, using OpenSSL for TLS support." - -[lints] -workspace = true - -[dependencies] -wasmtime-wasi-tls = { workspace = true } -tokio = { workspace = true } -tokio-openssl = { workspace = true } -openssl = { workspace = true } - -[dev-dependencies] -test-programs-artifacts = { workspace = true } -wasmtime = { workspace = true, features = ["runtime", "component-model"] } -wasmtime-wasi = { workspace = true } -tokio = { workspace = true, features = ["macros"] } -futures = { workspace = true } diff --git a/crates/wasi-tls-openssl/tests/main.rs b/crates/wasi-tls-openssl/tests/main.rs deleted file mode 100644 index d09c0939aacb..000000000000 --- a/crates/wasi-tls-openssl/tests/main.rs +++ /dev/null @@ -1,68 +0,0 @@ -use wasmtime::{ - Result, Store, - component::{Component, Linker, ResourceTable}, - format_err, -}; -use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView, p2::bindings::Command}; -use wasmtime_wasi_tls::{LinkOptions, WasiTls, WasiTlsCtx, WasiTlsCtxBuilder}; - -struct Ctx { - table: ResourceTable, - wasi_ctx: WasiCtx, - wasi_tls_ctx: WasiTlsCtx, -} - -impl WasiView for Ctx { - fn ctx(&mut self) -> WasiCtxView<'_> { - WasiCtxView { - ctx: &mut self.wasi_ctx, - table: &mut self.table, - } - } -} - -async fn run_test(path: &str) -> Result<()> { - let provider = Box::new(wasmtime_wasi_tls_openssl::OpenSslProvider::default()); - let ctx = Ctx { - table: ResourceTable::new(), - wasi_ctx: WasiCtx::builder() - .inherit_stderr() - .inherit_network() - .allow_ip_name_lookup(true) - .build(), - wasi_tls_ctx: WasiTlsCtxBuilder::new().provider(provider).build(), - }; - - let engine = test_programs_artifacts::engine(|_config| {}); - let mut store = Store::new(&engine, ctx); - let component = Component::from_file(&engine, path)?; - - let mut linker = Linker::new(&engine); - wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; - let mut opts = LinkOptions::default(); - opts.tls(true); - wasmtime_wasi_tls::add_to_linker(&mut linker, &mut opts, |h: &mut Ctx| { - WasiTls::new(&h.wasi_tls_ctx, &mut h.table) - })?; - - let command = Command::instantiate_async(&mut store, &component, &linker).await?; - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| format_err!("command returned with failing exit status")) -} - -macro_rules! assert_test_exists { - ($name:ident) => { - #[expect(unused_imports, reason = "just here to assert it exists")] - use self::$name as _; - }; -} - -test_programs_artifacts::foreach_tls!(assert_test_exists); - -#[tokio::test(flavor = "multi_thread")] -async fn tls_sample_application() -> Result<()> { - run_test(test_programs_artifacts::TLS_SAMPLE_APPLICATION_COMPONENT).await -} diff --git a/crates/wasi-tls/Cargo.toml b/crates/wasi-tls/Cargo.toml index a94af76ca3c5..030d7801fc36 100644 --- a/crates/wasi-tls/Cargo.toml +++ b/crates/wasi-tls/Cargo.toml @@ -11,6 +11,17 @@ description = "Wasmtime implementation of the wasi-tls API" [lints] workspace = true +[package.metadata.docs.rs] +all-features = true + +[features] +default = ["p2", "rustls"] +p2 = ["wasmtime-wasi/p2"] +p3 = ["wasmtime-wasi/p3", "wasmtime/component-model-async", "tracing"] +rustls = ["dep:rustls", "dep:tokio-rustls", "dep:webpki-roots"] +nativetls = ["dep:native-tls", "dep:tokio-native-tls"] +openssl = ["dep:openssl", "dep:tokio-openssl"] + [dependencies] bytes = { workspace = true } tokio = { workspace = true, features = [ @@ -21,10 +32,15 @@ tokio = { workspace = true, features = [ ] } wasmtime = { workspace = true, features = ["runtime", "component-model"] } wasmtime-wasi = { workspace = true } - -tokio-rustls = { workspace = true } -rustls = { workspace = true } -webpki-roots = { workspace = true } +tracing = { workspace = true, optional = true } +cfg-if = { workspace = true } +tokio-rustls = { workspace = true, optional = true } +rustls = { workspace = true, optional = true } +webpki-roots = { workspace = true, optional = true } +tokio-native-tls = { workspace = true, optional = true } +native-tls = { workspace = true, optional = true } +tokio-openssl = { workspace = true, optional = true } +openssl = { workspace = true, optional = true } [dev-dependencies] test-programs-artifacts = { workspace = true } diff --git a/crates/wasi-tls/src/error.rs b/crates/wasi-tls/src/error.rs new file mode 100644 index 000000000000..d37ee2215459 --- /dev/null +++ b/crates/wasi-tls/src/error.rs @@ -0,0 +1,44 @@ +use std::sync::Arc; + +/// TLS error +pub struct Error(Arc); + +impl Error { + /// Creates a new error with the given message. + pub fn msg(message: M) -> Self + where + M: ToString, + { + Self(Arc::new(message.to_string())) + } +} +impl Clone for Error { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } +} +impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } +} +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } +} +impl std::error::Error for Error {} +impl From for Error { + fn from(err: std::io::Error) -> Self { + // Try to recover the original error: + match err.downcast::() { + Ok(e) => e, + Err(io_err) => Self::msg(io_err), + } + } +} +impl From for std::io::Error { + fn from(err: Error) -> Self { + std::io::Error::other(err) + } +} diff --git a/crates/wasi-tls/src/lib.rs b/crates/wasi-tls/src/lib.rs index 0f011f0bde29..94aab18b9c18 100644 --- a/crates/wasi-tls/src/lib.rs +++ b/crates/wasi-tls/src/lib.rs @@ -13,7 +13,8 @@ //! component::{Linker, ResourceTable}, //! Store, Engine, Result, //! }; -//! use wasmtime_wasi_tls::{LinkOptions, WasiTls, WasiTlsCtx, WasiTlsCtxBuilder}; +//! use wasmtime_wasi_tls::{WasiTlsCtx, WasiTlsCtxBuilder, WasiTlsView, WasiTlsCtxView}; +//! use wasmtime_wasi_tls::p2::LinkOptions; //! //! struct Ctx { //! table: ResourceTable, @@ -27,6 +28,12 @@ //! } //! } //! +//! impl WasiTlsView for Ctx { +//! fn tls(&mut self) -> WasiTlsCtxView<'_> { +//! WasiTlsCtxView { ctx: &mut self.wasi_tls_ctx, table: &mut self.table } +//! } +//! } +//! //! #[tokio::main] //! async fn main() -> Result<()> { //! let ctx = Ctx { @@ -37,8 +44,10 @@ //! .allow_ip_name_lookup(true) //! .build(), //! wasi_tls_ctx: WasiTlsCtxBuilder::new() -//! // Optionally, configure a different TLS provider: -//! // .provider(Box::new(wasmtime_wasi_tls_nativetls::NativeTlsProvider::default())) +//! // Optionally, configure a specific TLS provider: +//! // .provider(Box::new(wasmtime_wasi_tls::RustlsProvider::default())) +//! // .provider(Box::new(wasmtime_wasi_tls::NativeTlsProvider::default())) +//! // .provider(Box::new(wasmtime_wasi_tls::OpenSslProvider::default())) //! .build(), //! }; //! @@ -46,15 +55,13 @@ //! //! // Set up wasi-cli //! let mut store = Store::new(&engine, ctx); -//! let mut linker = Linker::new(&engine); +//! let mut linker: Linker = Linker::new(&engine); //! wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; //! //! // Add wasi-tls types and turn on the feature in linker //! let mut opts = LinkOptions::default(); //! opts.tls(true); -//! wasmtime_wasi_tls::add_to_linker(&mut linker, &mut opts, |h: &mut Ctx| { -//! WasiTls::new(&h.wasi_tls_ctx, &mut h.table) -//! })?; +//! wasmtime_wasi_tls::p2::add_to_linker(&mut linker, &opts)?; //! //! // ... use `linker` to instantiate within `store` ... //! Ok(()) @@ -69,50 +76,28 @@ #![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))] use tokio::io::{AsyncRead, AsyncWrite}; -use wasmtime::component::{HasData, ResourceTable}; - -pub mod bindings; -mod host; -mod io; -mod rustls; +mod error; +mod providers; -pub use bindings::types::LinkOptions; -pub use host::{HostClientConnection, HostClientHandshake, HostFutureClientStreams}; -pub use rustls::RustlsProvider; +/// WASIp2 (`wasi:tls@0.2.0-draft`) host implementation. +#[cfg(feature = "p2")] +pub mod p2; +/// WASIp3 (`wasi:tls@0.3.0-draft`) host implementation. +#[cfg(feature = "p3")] +pub mod p3; -/// Capture the state necessary for use in the `wasi-tls` API implementation. -pub struct WasiTls<'a> { - ctx: &'a WasiTlsCtx, - table: &'a mut ResourceTable, -} - -impl<'a> WasiTls<'a> { - /// Create a new Wasi TLS context - pub fn new(ctx: &'a WasiTlsCtx, table: &'a mut ResourceTable) -> Self { - Self { ctx, table } - } -} - -/// Add the `wasi-tls` world's types to a [`wasmtime::component::Linker`]. -pub fn add_to_linker( - l: &mut wasmtime::component::Linker, - opts: &mut LinkOptions, - f: fn(&mut T) -> WasiTls<'_>, -) -> wasmtime::Result<()> { - bindings::types::add_to_linker::<_, HasWasiTls>(l, &opts, f)?; - Ok(()) -} +pub use error::Error; +pub use providers::*; -struct HasWasiTls; -impl HasData for HasWasiTls { - type Data<'a> = WasiTls<'a>; -} +#[cfg(any(feature = "p2", feature = "p3"))] +use wasmtime::component::{HasData, ResourceTable}; /// Builder-style structure used to create a [`WasiTlsCtx`]. +#[cfg(any(feature = "p2", feature = "p3"))] pub struct WasiTlsCtxBuilder { provider: Box, } - +#[cfg(any(feature = "p2", feature = "p3"))] impl WasiTlsCtxBuilder { /// Creates a builder for a new context with default parameters set. pub fn new() -> Self { @@ -121,7 +106,10 @@ impl WasiTlsCtxBuilder { /// Configure the TLS provider to use for this context. /// - /// By default, this is set to the [`RustlsProvider`]. + /// By default, this is set to the [`DefaultProvider`] which is picked at + /// compile time based on feature flags. If this crate is compiled with + /// multiple TLS providers, this method can be used to specify the provider + /// at runtime. pub fn provider(mut self, provider: Box) -> Self { self.provider = provider; self @@ -134,19 +122,46 @@ impl WasiTlsCtxBuilder { } } } +#[cfg(any(feature = "p2", feature = "p3"))] impl Default for WasiTlsCtxBuilder { fn default() -> Self { Self { - provider: Box::new(RustlsProvider::default()), + provider: Box::new(DefaultProvider::default()), } } } /// Wasi TLS context needed for internal `wasi-tls` state. +#[cfg(any(feature = "p2", feature = "p3"))] pub struct WasiTlsCtx { pub(crate) provider: Box, } +/// The type for which this crate implements the `wasi:tls` interfaces. +#[cfg(any(feature = "p2", feature = "p3"))] +pub(crate) struct WasiTls; +#[cfg(any(feature = "p2", feature = "p3"))] +impl HasData for WasiTls { + type Data<'a> = WasiTlsCtxView<'a>; +} + +/// View into [`WasiTlsCtx`] implementation and [`ResourceTable`]. +#[cfg(any(feature = "p2", feature = "p3"))] +pub struct WasiTlsCtxView<'a> { + /// Mutable reference to table used to manage resources. + pub table: &'a mut ResourceTable, + + /// Mutable reference to the WASI TLS context. + pub ctx: &'a mut WasiTlsCtx, +} + +/// A trait which provides internal WASI TLS state. +#[cfg(any(feature = "p2", feature = "p3"))] +pub trait WasiTlsView: Send { + /// Return a [`WasiTlsCtxView`] from mutable reference to self. + fn tls(&mut self) -> WasiTlsCtxView<'_>; +} + /// The data stream that carries the encrypted TLS data. /// Typically this is a TCP stream. pub trait TlsTransport: AsyncRead + AsyncWrite + Send + Unpin + 'static {} @@ -158,11 +173,8 @@ pub trait TlsStream: AsyncRead + AsyncWrite + Send + Unpin + 'static {} /// A TLS implementation. pub trait TlsProvider: Send + Sync + 'static { /// Set up a client TLS connection using the provided `server_name` and `transport`. - fn connect( - &self, - server_name: String, - transport: Box, - ) -> BoxFuture>>; + fn connect(&self, server_name: String, transport: Box) -> BoxFutureTlsStream; } -pub(crate) type BoxFuture = std::pin::Pin + Send>>; +pub(crate) type BoxFutureTlsStream = + std::pin::Pin, Error>> + Send>>; diff --git a/crates/wasi-tls/src/bindings.rs b/crates/wasi-tls/src/p2/bindings.rs similarity index 61% rename from crates/wasi-tls/src/bindings.rs rename to crates/wasi-tls/src/p2/bindings.rs index e6de19e96779..ce4c0c2cb1f7 100644 --- a/crates/wasi-tls/src/bindings.rs +++ b/crates/wasi-tls/src/p2/bindings.rs @@ -7,9 +7,9 @@ mod generated { world: "wasi:tls/imports", with: { "wasi:io": wasmtime_wasi::p2::bindings::io, - "wasi:tls/types.client-connection": crate::HostClientConnection, - "wasi:tls/types.client-handshake": crate::HostClientHandshake, - "wasi:tls/types.future-client-streams": crate::HostFutureClientStreams, + "wasi:tls/types.client-connection": crate::p2::HostClientConnection, + "wasi:tls/types.client-handshake": crate::p2::HostClientHandshake, + "wasi:tls/types.future-client-streams": crate::p2::HostFutureClientStreams, }, imports: { default: trappable }, require_store_data_send: true, diff --git a/crates/wasi-tls/src/host.rs b/crates/wasi-tls/src/p2/host.rs similarity index 91% rename from crates/wasi-tls/src/host.rs rename to crates/wasi-tls/src/p2/host.rs index 517813ccde51..955c4d8defc3 100644 --- a/crates/wasi-tls/src/host.rs +++ b/crates/wasi-tls/src/p2/host.rs @@ -4,15 +4,16 @@ use wasmtime_wasi::async_trait; use wasmtime_wasi::p2::Pollable; use wasmtime_wasi::p2::{DynInputStream, DynOutputStream, DynPollable, IoError}; -use crate::{ - TlsStream, TlsTransport, WasiTls, bindings, +use crate::p2::{ + bindings, io::{ AsyncReadStream, AsyncWriteStream, FutureOutput, WasiFuture, WasiStreamReader, WasiStreamWriter, }, }; +use crate::{TlsStream, TlsTransport, WasiTlsCtxView}; -impl<'a> bindings::types::Host for WasiTls<'a> {} +impl<'a> bindings::types::Host for WasiTlsCtxView<'a> {} /// Represents the ClientHandshake which will be used to configure the handshake pub struct HostClientHandshake { @@ -20,7 +21,7 @@ pub struct HostClientHandshake { transport: Box, } -impl<'a> bindings::types::HostClientHandshake for WasiTls<'a> { +impl<'a> bindings::types::HostClientHandshake for WasiTlsCtxView<'a> { fn new( &mut self, server_name: String, @@ -85,7 +86,7 @@ impl Pollable for HostFutureClientStreams { } } -impl<'a> bindings::types::HostFutureClientStreams for WasiTls<'a> { +impl<'a> bindings::types::HostFutureClientStreams for WasiTlsCtxView<'a> { fn subscribe( &mut self, this: Resource, @@ -141,10 +142,10 @@ impl<'a> bindings::types::HostFutureClientStreams for WasiTls<'a> { /// Represents the client connection and used to shut down the tls stream pub struct HostClientConnection( - crate::io::AsyncWriteStream>>, + crate::p2::io::AsyncWriteStream>>, ); -impl<'a> bindings::types::HostClientConnection for WasiTls<'a> { +impl<'a> bindings::types::HostClientConnection for WasiTlsCtxView<'a> { fn close_output(&mut self, this: Resource) -> wasmtime::Result<()> { self.table.get_mut(&this)?.0.close() } diff --git a/crates/wasi-tls/src/io.rs b/crates/wasi-tls/src/p2/io.rs similarity index 98% rename from crates/wasi-tls/src/io.rs rename to crates/wasi-tls/src/p2/io.rs index 798b65433826..7edbc315cdd6 100644 --- a/crates/wasi-tls/src/io.rs +++ b/crates/wasi-tls/src/p2/io.rs @@ -163,7 +163,10 @@ impl AsyncWrite for WasiStreamWriter { } Ok(count) => { let count = count.min(buf.len()); - return match output.write(Bytes::copy_from_slice(&buf[..count])) { + return match OutputStream::write( + &mut **output, + Bytes::copy_from_slice(&buf[..count]), + ) { Ok(()) => Poll::Ready(Ok(count)), Err(StreamError::Closed) => Poll::Ready(Ok(0)), Err(e) => Poll::Ready(Err(std::io::Error::other(e))), diff --git a/crates/wasi-tls/src/p2/mod.rs b/crates/wasi-tls/src/p2/mod.rs new file mode 100644 index 000000000000..a8b49a82ae99 --- /dev/null +++ b/crates/wasi-tls/src/p2/mod.rs @@ -0,0 +1,20 @@ +use crate::{WasiTls, WasiTlsView}; + +pub mod bindings; +mod host; +mod io; + +pub use bindings::types::LinkOptions; +pub use host::{HostClientConnection, HostClientHandshake, HostFutureClientStreams}; + +/// Add the `wasi-tls` world's types to a [`wasmtime::component::Linker`]. +pub fn add_to_linker( + l: &mut wasmtime::component::Linker, + opts: &LinkOptions, +) -> wasmtime::Result<()> +where + T: WasiTlsView + 'static, +{ + bindings::types::add_to_linker::<_, WasiTls>(l, &opts, T::tls)?; + Ok(()) +} diff --git a/crates/wasi-tls/src/p3/bindings.rs b/crates/wasi-tls/src/p3/bindings.rs new file mode 100644 index 000000000000..5f263ea8a2ad --- /dev/null +++ b/crates/wasi-tls/src/p3/bindings.rs @@ -0,0 +1,21 @@ +//! Raw bindings to the `wasi:tls` package. + +#[expect(missing_docs, reason = "generated code")] +mod generated { + wasmtime::component::bindgen!({ + path: "src/p3/wit", + world: "wasi:tls/imports", + imports: { + "wasi:tls/client.[method]connector.send": store | trappable | tracing, + "wasi:tls/client.[method]connector.receive": store | trappable | tracing, + default: trappable | tracing, + }, + with: { + "wasi:tls/client.connector": crate::p3::host::Connector, + "wasi:tls/types.error": crate::Error, + }, + require_store_data_send: true, + }); +} + +pub use self::generated::wasi::*; diff --git a/crates/wasi-tls/src/p3/host.rs b/crates/wasi-tls/src/p3/host.rs new file mode 100644 index 000000000000..1228785bf6f6 --- /dev/null +++ b/crates/wasi-tls/src/p3/host.rs @@ -0,0 +1,290 @@ +//! p3 host implementation for `wasi:tls`. + +use crate::p3::util::{AsyncReadProducer, AsyncWriteConsumer, Closed, Deferred, Shared, pipe}; +use crate::p3::{WasiTls, bindings}; +use crate::{BoxFutureTlsStream, Error, TlsStream, WasiTlsCtxView}; +use std::pin::Pin; +use std::task::{Context, Poll}; +use tokio::{io::AsyncWriteExt as _, sync::oneshot}; +use wasmtime::StoreContextMut; +use wasmtime::component::{ + Access, Accessor, AccessorTask, FutureProducer, FutureReader, HasData, Resource, StreamReader, +}; + +/// Host-side state stored for `wasi:tls/client` `connector` resources. +pub struct Connector { + connection: Shared>>, + send: Option, + recv: Option, +} + +impl<'a> bindings::tls::client::Host for WasiTlsCtxView<'a> {} +impl<'a> bindings::tls::types::Host for WasiTlsCtxView<'a> {} + +impl<'a> bindings::tls::types::HostError for WasiTlsCtxView<'a> { + fn to_debug_string(&mut self, this: Resource) -> wasmtime::Result { + Ok(self.table.get(&this)?.to_string()) + } + + fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.table.delete(rep)?; + Ok(()) + } +} + +impl<'a> bindings::tls::client::HostConnector for WasiTlsCtxView<'a> { + fn new(&mut self) -> wasmtime::Result> { + Ok(self.table.push(Connector { + connection: Shared::new(Deferred::pending()), + send: None, + recv: None, + })?) + } + + fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.table.delete(rep)?; + Ok(()) + } +} + +impl bindings::tls::client::HostConnectorWithStore for WasiTls { + fn send( + mut store: Access<'_, T, Self>, + this: Resource, + mut cleartext: StreamReader, + ) -> wasmtime::Result<(StreamReader, FutureReader>>)> { + let getter = store.getter(); + + { + let ctx = store.get(); + let connector = ctx.table.get(&this)?; + if connector.send.is_some() { + cleartext.close(&mut store)?; + + let err = Error::msg("send already configured"); + let ciphertext = Closed(err.clone()); + let result = ResultProducer::ready(getter, Err(err)); + + return Ok(( + StreamReader::new(&mut store, ciphertext)?, + FutureReader::new(&mut store, result)?, + )); + } + } + + let (ciphertext_reader, ciphertext_writer) = pipe::pipe(); + let (ciphertext_result_tx, ciphertext_result_rx) = oneshot::channel(); + let (cleartext_result_tx, cleartext_result_rx) = oneshot::channel(); + let (send_result_tx, send_result_rx) = oneshot::channel(); + + let connection = { + let ctx = store.get(); + let connector = ctx.table.get_mut(&this)?; + connector.send = Some(ciphertext_writer); + connector.connection.clone() + }; + + cleartext.pipe( + &mut store, + AsyncWriteConsumer::new(connection, cleartext_result_tx), + )?; + + let ciphertext = AsyncReadProducer::new(ciphertext_reader, ciphertext_result_tx); + store.spawn(FnTask(async move || { + let cleartext_result = match cleartext_result_rx.await? { + Ok(mut inner) => inner.shutdown().await, // Drive the close_notify sequence + Err(e) => Err(e), + }; + let ciphertext_result = ciphertext_result_rx.await?.map(drop); + let combined_result = cleartext_result + .and(ciphertext_result) + .map_err(|e| Error::from(e)); + _ = send_result_tx.send(combined_result); + Ok(()) + })); + let result = ResultProducer::new(getter, send_result_rx); + + Ok(( + StreamReader::new(&mut store, ciphertext)?, + FutureReader::new(&mut store, result)?, + )) + } + + fn receive( + mut store: Access<'_, T, Self>, + this: Resource, + mut ciphertext: StreamReader, + ) -> wasmtime::Result<(StreamReader, FutureReader>>)> { + let getter = store.getter(); + + { + let ctx = store.get(); + let connector = ctx.table.get(&this)?; + if connector.recv.is_some() { + ciphertext.close(&mut store)?; + + let err = Error::msg("receive already configured"); + let cleartext = Closed(err.clone()); + let result = ResultProducer::ready(getter, Err(err)); + + return Ok(( + StreamReader::new(&mut store, cleartext)?, + FutureReader::new(&mut store, result)?, + )); + } + } + + let (ciphertext_reader, ciphertext_writer) = pipe::pipe(); + let (ciphertext_result_tx, ciphertext_result_rx) = oneshot::channel(); + let (cleartext_result_tx, cleartext_result_rx) = oneshot::channel(); + let (recv_result_tx, recv_result_rx) = oneshot::channel(); + + let connection = { + let ctx = store.get(); + let connector = ctx.table.get_mut(&this)?; + connector.recv = Some(ciphertext_reader); + connector.connection.clone() + }; + + ciphertext.pipe( + &mut store, + AsyncWriteConsumer::new(ciphertext_writer, ciphertext_result_tx), + )?; + + let cleartext = AsyncReadProducer::new(connection, cleartext_result_tx); + store.spawn(FnTask(async move || { + let ciphertext_result = match ciphertext_result_rx.await? { + // Let the TLS implementation know the transport is closed. + // Most likely, `shutdown` will be entirely synchronous and + // complete immediately, but awaiting it anyway to adhere to + // the AsyncWrite contract: + Ok(mut inner) => inner.shutdown().await, + Err(e) => Err(e), + }; + let cleartext_result = cleartext_result_rx.await?.map(drop); + let combined_result = cleartext_result + .and(ciphertext_result) + .map_err(|e| Error::from(e)); + _ = recv_result_tx.send(combined_result); + Ok(()) + })); + let result = ResultProducer::new(getter, recv_result_rx); + + Ok(( + StreamReader::new(&mut store, cleartext)?, + FutureReader::new(&mut store, result)?, + )) + } + + async fn connect( + accessor: &Accessor, + this: Resource, + server_name: String, + ) -> wasmtime::Result>> { + fn connect_err(msg: &'static str) -> BoxFutureTlsStream { + Box::pin(async move { Err(Error::msg(msg)) }) + } + let (fut, connection) = accessor.with( + move |mut access| -> wasmtime::Result<(BoxFutureTlsStream, _)> { + let WasiTlsCtxView { table, ctx } = access.get(); + let connector = table.delete(this)?; + let connection = connector.connection; + + let Some(ciphertext_writer) = connector.send else { + return Ok(( + connect_err("send() must be called before connect()"), + connection, + )); + }; + let Some(ciphertext_reader) = connector.recv else { + return Ok(( + connect_err("receive() must be called before connect()"), + connection, + )); + }; + + let transport = Box::new(tokio::io::join(ciphertext_reader, ciphertext_writer)); + let fut = ctx.provider.connect(server_name, transport); + + Ok((fut, connection)) + }, + )?; + + match fut.await { + Ok(tls_stream) => { + connection.lock().resolve(tls_stream); + Ok(Ok(())) + } + Err(e) => { + connection.lock().resolve(Box::new(Closed(e.clone()))); + let resource = accessor.with(|mut access| access.get().table.push(e))?; + Ok(Err(resource)) + } + } + } +} + +pub(crate) struct FnTask(pub(crate) Fn); +impl AccessorTask for FnTask +where + Fn: FnOnce() -> Fut + Send + 'static, + Fut: Future> + Send + 'static, + D: HasData + ?Sized, +{ + fn run( + self, + _accessor: &wasmtime::component::Accessor, + ) -> impl Future> + Send { + self.0() + } +} + +pub(crate) struct ResultProducer { + result: oneshot::Receiver>, + getter: for<'a> fn(&'a mut D) -> WasiTlsCtxView<'a>, +} +impl ResultProducer { + pub(crate) fn new( + getter: for<'a> fn(&'a mut D) -> WasiTlsCtxView<'a>, + result: oneshot::Receiver>, + ) -> Self { + Self { result, getter } + } + + pub(crate) fn ready( + getter: for<'a> fn(&'a mut D) -> WasiTlsCtxView<'a>, + result: Result<(), Error>, + ) -> Self { + let (sender, receiver) = oneshot::channel(); + sender.send(result).expect("receiver dropped"); + Self { + result: receiver, + getter, + } + } +} +impl FutureProducer for ResultProducer +where + D: 'static, +{ + type Item = Result<(), Resource>; + + fn poll_produce( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + mut store: StoreContextMut, + finish: bool, + ) -> Poll>> { + match Pin::new(&mut self.result).poll(cx) { + Poll::Ready(Ok(Ok(()))) => Poll::Ready(Ok(Some(Ok(())))), + Poll::Ready(Ok(Err(err))) => { + let WasiTlsCtxView { table, .. } = (self.getter)(store.data_mut()); + let err = table.push(err)?; + Poll::Ready(Ok(Some(Err(err)))) + } + Poll::Ready(Err(_)) => Poll::Ready(Err(wasmtime::format_err!("sender dropped"))), + Poll::Pending if finish => Poll::Ready(Ok(None)), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/crates/wasi-tls/src/p3/mod.rs b/crates/wasi-tls/src/p3/mod.rs new file mode 100644 index 000000000000..e8d41f4b0f85 --- /dev/null +++ b/crates/wasi-tls/src/p3/mod.rs @@ -0,0 +1,27 @@ +//! Experimental, unstable and incomplete implementation of wasip3 version of `wasi:tls`. +//! +//! This module is under heavy development. +//! It is not compliant with semver and is not ready +//! for production use. +//! +//! Bug and security fixes limited to wasip3 will not be given patch releases. +//! +//! Documentation of this module may be incorrect or out-of-sync with the implementation. + +pub mod bindings; +pub mod host; +pub(crate) mod util; + +use crate::{WasiTls, WasiTlsView}; +use bindings::tls::{client, types}; +use wasmtime::component::Linker; + +/// Add all interfaces from this module into the `linker` provided. +pub fn add_to_linker(linker: &mut Linker) -> wasmtime::Result<()> +where + T: WasiTlsView + 'static, +{ + client::add_to_linker::<_, WasiTls>(linker, T::tls)?; + types::add_to_linker::<_, WasiTls>(linker, T::tls)?; + Ok(()) +} diff --git a/crates/wasi-tls/src/p3/util/closed.rs b/crates/wasi-tls/src/p3/util/closed.rs new file mode 100644 index 000000000000..3e4e5e2fc813 --- /dev/null +++ b/crates/wasi-tls/src/p3/util/closed.rs @@ -0,0 +1,65 @@ +use crate::Error; +use bytes::BytesMut; +use std::io::Cursor; +use std::pin::Pin; +use std::task::{Context, Poll}; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use wasmtime::StoreContextMut; +use wasmtime::component::{Destination, Source, StreamConsumer, StreamProducer, StreamResult}; + +/// A stream which is closed and returns an error on all operations. +pub(crate) struct Closed(pub(crate) Error); +impl crate::TlsStream for Closed {} +impl AsyncRead for Closed { + fn poll_read( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &mut ReadBuf<'_>, + ) -> Poll> { + Poll::Ready(Err(self.0.clone().into())) + } +} +impl AsyncWrite for Closed { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &[u8], + ) -> Poll> { + Poll::Ready(Err(self.0.clone().into())) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Err(self.0.clone().into())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Err(self.0.clone().into())) + } +} +impl StreamProducer for Closed { + type Item = u8; + type Buffer = Cursor; + + fn poll_produce<'a>( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _store: StoreContextMut<'a, D>, + _dst: Destination<'a, Self::Item, Self::Buffer>, + _finish: bool, + ) -> Poll> { + Poll::Ready(Ok(StreamResult::Dropped)) + } +} +impl StreamConsumer for Closed { + type Item = u8; + + fn poll_consume( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _store: StoreContextMut, + _src: Source<'_, Self::Item>, + _finish: bool, + ) -> Poll> { + Poll::Ready(Ok(StreamResult::Dropped)) + } +} diff --git a/crates/wasi-tls/src/p3/util/deferred.rs b/crates/wasi-tls/src/p3/util/deferred.rs new file mode 100644 index 000000000000..1b9a5db5170e --- /dev/null +++ b/crates/wasi-tls/src/p3/util/deferred.rs @@ -0,0 +1,261 @@ +use std::pin::Pin; +use std::task::{Context, Poll, Waker}; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; + +/// A stream whose underlying IO implementation will come later. +/// +/// The stream starts out idle. All IO operations during this time will return +/// `Poll::Pending` and store the waker. Once the underlying IO is installed via +/// `resolve()`, the wakers will be fired and all IO operations will be +/// delegated to the inner IO. +pub(crate) enum Deferred { + Pending { + read_waker: Waker, + write_waker: Waker, + }, + Ready(IO), +} +impl Deferred { + /// Creates an idle stream. + pub(crate) fn pending() -> Self { + let noop = Waker::noop(); + Deferred::Pending { + read_waker: noop.clone(), + write_waker: noop.clone(), + } + } + + /// Install the inner IO and wake any stored wakers. + pub(crate) fn resolve(&mut self, io: IO) { + match std::mem::replace(self, Deferred::Ready(io)) { + Deferred::Pending { + read_waker, + write_waker, + } => { + read_waker.wake(); + write_waker.wake(); + } + Deferred::Ready(_) => unreachable!("Deferred should only be made ready once"), + } + } + + fn poll_read_io( + &mut self, + cx: &mut Context<'_>, + f: impl FnOnce(Pin<&mut IO>, &mut Context<'_>) -> Poll, + ) -> Poll + where + IO: Unpin, + { + match self { + Deferred::Pending { read_waker, .. } => { + *read_waker = cx.waker().clone(); + Poll::Pending + } + Deferred::Ready(io) => f(Pin::new(io), cx), + } + } + + fn poll_write_io( + &mut self, + cx: &mut Context<'_>, + f: impl FnOnce(Pin<&mut IO>, &mut Context<'_>) -> Poll, + ) -> Poll + where + IO: Unpin, + { + match self { + Deferred::Pending { write_waker, .. } => { + *write_waker = cx.waker().clone(); + Poll::Pending + } + Deferred::Ready(io) => f(Pin::new(io), cx), + } + } +} + +impl AsyncRead for Deferred { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + self.poll_read_io(cx, |io, cx| io.poll_read(cx, buf)) + } +} + +impl AsyncWrite for Deferred { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.poll_write_io(cx, |io, cx| io.poll_write(cx, buf)) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_write_io(cx, |io, cx| io.poll_flush(cx)) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_write_io(cx, |io, cx| io.poll_shutdown(cx)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::p3::util::pipe; + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::task::Wake; + use tokio::io::{AsyncReadExt, AsyncWriteExt, DuplexStream}; + + struct BoolWaker(AtomicBool); + impl BoolWaker { + fn new() -> Arc { + Arc::new(Self(AtomicBool::new(false))) + } + fn waker(self: &Arc) -> Waker { + Waker::from(self.clone()) + } + fn is_set(&self) -> bool { + self.0.load(Ordering::SeqCst) + } + } + impl Wake for BoolWaker { + fn wake(self: Arc) { + self.0.store(true, Ordering::SeqCst); + } + } + + /// Core lifecycle: pending → Poll::Pending (read waker stored) → resolve + /// wakes it → IO is then delegated to the inner reader. + #[tokio::test] + async fn read_lifecycle() { + let read_flag = BoolWaker::new(); + let read_waker = read_flag.waker(); + let mut cx = Context::from_waker(&read_waker); + + let (pipe_reader, mut pipe_writer) = pipe::pipe(); + pipe_writer.write_all(b"hello").await.unwrap(); + drop(pipe_writer); + let mut deferred: Deferred = Deferred::pending(); + + let mut buf = [0u8; 8]; + assert!( + Pin::new(&mut deferred) + .poll_read(&mut cx, &mut ReadBuf::new(&mut buf)) + .is_pending() + ); + assert!( + !read_flag.is_set(), + "read waker must not fire before resolve" + ); + + deferred.resolve(pipe_reader); + assert!( + read_flag.is_set(), + "resolve must wake the stored read waker" + ); + + let mut out = Vec::new(); + deferred.read_to_end(&mut out).await.unwrap(); + assert_eq!(&out, b"hello"); + } + + /// Write-side lifecycle: pending → Poll::Pending (write waker stored) → + /// resolve wakes it → IO is then delegated to the inner writer. + #[tokio::test] + async fn write_lifecycle() { + let write_flag = BoolWaker::new(); + let write_waker = write_flag.waker(); + let mut cx = Context::from_waker(&write_waker); + + let (mut pipe_reader, pipe_writer) = pipe::pipe(); + let mut deferred: Deferred = Deferred::pending(); + + // Poll write while still pending — write waker must be stored. + assert!( + Pin::new(&mut deferred) + .poll_write(&mut cx, b"world") + .is_pending() + ); + assert!( + !write_flag.is_set(), + "write waker must not fire before resolve" + ); + + deferred.resolve(pipe_writer); + assert!( + write_flag.is_set(), + "resolve must wake the stored write waker" + ); + + deferred.write_all(b"world").await.unwrap(); + deferred.shutdown().await.unwrap(); + + let mut out = Vec::new(); + pipe_reader.read_to_end(&mut out).await.unwrap(); + assert_eq!(&out, b"world"); + } + + /// resolve() wakes both the read and write wakers on the *same* Deferred. + /// Uses DuplexStream which implements both AsyncRead and AsyncWrite, so a + /// single Deferred can have distinct wakers registered on + /// each side before the IO is available. + #[tokio::test] + async fn resolve_wakes_both() { + let read_flag = BoolWaker::new(); + let write_flag = BoolWaker::new(); + + let (duplex, _other) = tokio::io::duplex(64); + let mut deferred: Deferred = Deferred::pending(); + + let read_waker = read_flag.waker(); + let write_waker = write_flag.waker(); + let mut read_cx = Context::from_waker(&read_waker); + let mut write_cx = Context::from_waker(&write_waker); + + // Register different wakers on each side of the same Deferred. + let mut buf = [0u8; 4]; + assert!( + Pin::new(&mut deferred) + .poll_read(&mut read_cx, &mut ReadBuf::new(&mut buf)) + .is_pending() + ); + assert!( + Pin::new(&mut deferred) + .poll_write(&mut write_cx, b"bye") + .is_pending() + ); + assert!( + !read_flag.is_set(), + "read waker must not fire before resolve" + ); + assert!( + !write_flag.is_set(), + "write waker must not fire before resolve" + ); + + deferred.resolve(duplex); + + assert!(read_flag.is_set(), "resolve must wake the read waker"); + assert!(write_flag.is_set(), "resolve must wake the write waker"); + } + + /// resolve() before any poll is fine; the noop waker is woken harmlessly. + #[tokio::test] + async fn resolve_before_poll() { + let (pipe_reader, mut pipe_writer) = pipe::pipe(); + pipe_writer.write_all(b"eager").await.unwrap(); + drop(pipe_writer); + + let mut deferred: Deferred = Deferred::pending(); + deferred.resolve(pipe_reader); + + let mut out = Vec::new(); + deferred.read_to_end(&mut out).await.unwrap(); + assert_eq!(&out, b"eager"); + } +} diff --git a/crates/wasi-tls/src/p3/util/mod.rs b/crates/wasi-tls/src/p3/util/mod.rs new file mode 100644 index 000000000000..84a10c245506 --- /dev/null +++ b/crates/wasi-tls/src/p3/util/mod.rs @@ -0,0 +1,10 @@ +mod closed; +mod deferred; +pub(crate) mod pipe; +mod shared; +mod tokio_streams; + +pub(crate) use closed::Closed; +pub(crate) use deferred::Deferred; +pub(crate) use shared::Shared; +pub(crate) use tokio_streams::{AsyncReadProducer, AsyncWriteConsumer}; diff --git a/crates/wasi-tls/src/p3/util/pipe.rs b/crates/wasi-tls/src/p3/util/pipe.rs new file mode 100644 index 000000000000..fd67ab2c5fee --- /dev/null +++ b/crates/wasi-tls/src/p3/util/pipe.rs @@ -0,0 +1,87 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; +use tokio::io::{AsyncRead, AsyncWrite, DuplexStream, ReadBuf}; + +pub(crate) const CAPACITY: usize = 16 * 1024; + +/// A unidirectional in-memory pipe. +/// +/// Data written to the `Writer` can be read from the `Reader`. +/// Closing the `Writer` will cause the `Reader` to see EOF, and +/// closing the `Reader` will cause the `Writer` to see a broken pipe error. +/// +/// Naively one would reach for `tokio::io::simplex`, but that does not +/// communicate EOF or broken pipe when the other side is dropped. +pub(crate) fn pipe() -> (Reader, Writer) { + let (r, w) = tokio::io::duplex(CAPACITY); + (Reader(r), Writer(w)) +} +pub(crate) struct Reader(DuplexStream); +pub(crate) struct Writer(DuplexStream); + +impl AsyncRead for Reader { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + Pin::new(&mut self.0).poll_read(cx, buf) + } +} + +impl AsyncWrite for Writer { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.0).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.0).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.0).poll_shutdown(cx) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + + #[tokio::test] + async fn basic_functionality() { + let (mut r, mut w) = pipe(); + w.write_all(b"hello, world").await.unwrap(); + let mut buf = vec![0u8; 12]; + r.read_exact(&mut buf).await.unwrap(); + assert_eq!(&buf, b"hello, world"); + } + + #[tokio::test] + async fn reader_eof_on_writer_drop() { + let (mut r, w) = pipe(); + drop(w); + let n = r.read(&mut vec![0u8; 16]).await.unwrap(); + assert_eq!(n, 0, "expected EOF after writer drop"); + } + + #[tokio::test] + async fn reader_eof_on_writer_shutdown() { + let (mut r, mut w) = pipe(); + w.shutdown().await.unwrap(); + let n = r.read(&mut vec![0u8; 16]).await.unwrap(); + assert_eq!(n, 0, "expected EOF after writer shutdown"); + } + + #[tokio::test] + async fn writer_broken_pipe_on_reader_drop() { + let (r, mut w) = pipe(); + drop(r); + let err = w.write_all(b"hello").await.unwrap_err(); + assert_eq!(err.kind(), std::io::ErrorKind::BrokenPipe); + } +} diff --git a/crates/wasi-tls/src/p3/util/shared.rs b/crates/wasi-tls/src/p3/util/shared.rs new file mode 100644 index 000000000000..d1b9c6dc4610 --- /dev/null +++ b/crates/wasi-tls/src/p3/util/shared.rs @@ -0,0 +1,48 @@ +use std::pin::Pin; +use std::sync::{Arc, Mutex, MutexGuard}; +use std::task::{Context, Poll}; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; + +/// An `Arc>` wrapper. +/// +/// Implements `AsyncRead` and `AsyncWrite` if the inner `IO` does so too. +pub(crate) struct Shared(Arc>); +impl Shared { + pub(crate) fn new(io: IO) -> Self { + Self(Arc::new(Mutex::new(io))) + } + pub(crate) fn lock(&self) -> MutexGuard<'_, IO> { + self.0.lock().expect("other thread panicked") + } +} +impl Clone for Shared { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } +} +impl AsyncRead for Shared { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + Pin::new(&mut *self.lock()).poll_read(cx, buf) + } +} +impl AsyncWrite for Shared { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut *self.lock()).poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut *self.lock()).poll_flush(cx) + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut *self.lock()).poll_shutdown(cx) + } +} diff --git a/crates/wasi-tls/src/p3/util/tokio_streams.rs b/crates/wasi-tls/src/p3/util/tokio_streams.rs new file mode 100644 index 000000000000..14331792fc76 --- /dev/null +++ b/crates/wasi-tls/src/p3/util/tokio_streams.rs @@ -0,0 +1,161 @@ +use bytes::BytesMut; +use std::io::Cursor; +use std::pin::Pin; +use std::task::{Context, Poll}; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use tokio::sync::oneshot; +use wasmtime::StoreContextMut; +use wasmtime::component::{Destination, Source, StreamConsumer, StreamProducer, StreamResult}; + +use crate::p3::util::pipe; + +/// A [StreamProducer] for an [AsyncRead] stream. +/// +/// Once the inner `AsyncRead` returns EOF or an error, the producer will be +/// dropped and the final result is submitted to the `dropped` channel. +pub(crate) struct AsyncReadProducer(Option<(IO, oneshot::Sender>)>); +impl AsyncReadProducer { + pub(crate) fn new(io: IO, dropped: oneshot::Sender>) -> Self { + Self(Some((io, dropped))) + } + fn io(&mut self) -> Option<&mut IO> { + self.0.as_mut().map(|(io, _)| io) + } + fn notify_dropped(&mut self, result: std::io::Result<()>) { + if let Some((io, dropped)) = self.0.take() { + let _ = dropped.send(result.map(|()| io)); + } + } +} +impl Drop for AsyncReadProducer { + fn drop(&mut self) { + self.notify_dropped(Ok(())); + } +} +impl StreamProducer for AsyncReadProducer +where + IO: AsyncRead + Send + Unpin + 'static, +{ + type Item = u8; + type Buffer = Cursor; + + fn poll_produce<'a>( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + store: StoreContextMut<'a, D>, + dst: Destination<'a, Self::Item, Self::Buffer>, + finish: bool, + ) -> Poll> { + let Some(io) = self.io() else { + return Poll::Ready(Ok(StreamResult::Dropped)); + }; + + let mut dst = dst.as_direct(store, pipe::CAPACITY); + let remaining = dst.remaining(); + + // A zero-remaining destination means the guest is waiting for + // readiness rather than requesting data. AsyncRead has no + // separate readiness API, so we lie and say "ready" here; the + // next actual poll will read data. + // + // See WebAssembly/component-model#561 for background. + if remaining.is_empty() { + return Poll::Ready(Ok(StreamResult::Completed)); + } + + let mut buf = ReadBuf::new(remaining); + match Pin::new(io).poll_read(cx, &mut buf) { + Poll::Ready(Ok(())) if buf.filled().is_empty() => { + // EOF — signal clean completion. + self.notify_dropped(Ok(())); + Poll::Ready(Ok(StreamResult::Dropped)) + } + Poll::Ready(Ok(())) => { + let n = buf.filled().len(); + dst.mark_written(n); + Poll::Ready(Ok(StreamResult::Completed)) + } + Poll::Ready(Err(e)) => { + self.notify_dropped(Err(e)); + Poll::Ready(Ok(StreamResult::Dropped)) + } + Poll::Pending if finish => Poll::Ready(Ok(StreamResult::Cancelled)), + Poll::Pending => Poll::Pending, + } + } +} + +/// A [StreamConsumer] for an [AsyncWrite] stream. +/// +/// Once the inner `AsyncWrite` returns an error, the consumer will be dropped +/// and the final result is submitted to the `dropped` channel. +pub(crate) struct AsyncWriteConsumer(Option<(IO, oneshot::Sender>)>); +impl AsyncWriteConsumer { + pub(crate) fn new(io: IO, dropped: oneshot::Sender>) -> Self { + Self(Some((io, dropped))) + } + fn io(&mut self) -> Option<&mut IO> { + self.0.as_mut().map(|(io, _)| io) + } + fn notify_dropped(&mut self, result: std::io::Result<()>) { + if let Some((io, dropped)) = self.0.take() { + let _ = dropped.send(result.map(|()| io)); + } + } +} +impl Drop for AsyncWriteConsumer { + fn drop(&mut self) { + self.notify_dropped(Ok(())); + } +} +impl StreamConsumer for AsyncWriteConsumer +where + IO: AsyncWrite + Send + Unpin + 'static, +{ + type Item = u8; + + fn poll_consume( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + store: StoreContextMut, + src: Source<'_, Self::Item>, + finish: bool, + ) -> Poll> { + let Some(io) = self.io() else { + return Poll::Ready(Ok(StreamResult::Dropped)); + }; + + let mut src = src.as_direct(store); + let remaining = src.remaining(); + + if remaining.is_empty() { + // Zero-length consume = readiness check. AsyncWrite has no + // dedicated readiness API so we report ready; the next poll will + // perform the actual write. + // + // See WebAssembly/component-model#561 for background. + return Poll::Ready(Ok(if finish { + StreamResult::Cancelled + } else { + StreamResult::Completed + })); + } + + match Pin::new(io).poll_write(cx, remaining) { + Poll::Ready(Ok(0)) => { + self.notify_dropped(Err(std::io::ErrorKind::WriteZero.into())); + Poll::Ready(Ok(StreamResult::Dropped)) + } + Poll::Ready(Ok(n)) => { + src.mark_read(n); + Poll::Ready(Ok(StreamResult::Completed)) + } + Poll::Ready(Err(e)) => { + self.notify_dropped(Err(e)); + Poll::Ready(Ok(StreamResult::Dropped)) + } + Poll::Pending if finish => Poll::Ready(Ok(StreamResult::Cancelled)), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/crates/wasi-tls/src/p3/wit/deps/tls/client.wit b/crates/wasi-tls/src/p3/wit/deps/tls/client.wit new file mode 100644 index 000000000000..8f282dbe8e36 --- /dev/null +++ b/crates/wasi-tls/src/p3/wit/deps/tls/client.wit @@ -0,0 +1,22 @@ +interface client { + use types.{error}; + + resource connector { + constructor(); + + /// Set up the encryption stream transform. + /// This takes an unprotected `cleartext` application data stream and + /// returns an encrypted data stream, ready to be sent out over the network. + /// Closing the `cleartext` stream will cause a `close_notify` packet to be emitted on the returned output stream. + send: func(cleartext: stream) -> tuple, future>>; + + /// Set up the decryption stream transform. + /// This takes an encrypted data stream, as received via e.g. the network, + /// and returns a decrypted application data stream. + receive: func(ciphertext: stream) -> tuple, future>>; + + /// Perform the handshake. + /// The `send` & `receive` streams must be set up before calling this method. + connect: static async func(this: connector, server-name: string) -> result<_, error>; + } +} diff --git a/crates/wasi-tls/src/p3/wit/deps/tls/types.wit b/crates/wasi-tls/src/p3/wit/deps/tls/types.wit new file mode 100644 index 000000000000..fc6c4b103e16 --- /dev/null +++ b/crates/wasi-tls/src/p3/wit/deps/tls/types.wit @@ -0,0 +1,5 @@ +interface types { + resource error { + to-debug-string: func() -> string; + } +} diff --git a/crates/wasi-tls/src/p3/wit/deps/tls/world.wit b/crates/wasi-tls/src/p3/wit/deps/tls/world.wit new file mode 100644 index 000000000000..599d49688ad2 --- /dev/null +++ b/crates/wasi-tls/src/p3/wit/deps/tls/world.wit @@ -0,0 +1,6 @@ +package wasi:tls@0.3.0-draft; + +world imports { + import client; + import types; +} diff --git a/crates/wasi-tls/src/p3/wit/world.wit b/crates/wasi-tls/src/p3/wit/world.wit new file mode 100644 index 000000000000..51e0e7e8cc58 --- /dev/null +++ b/crates/wasi-tls/src/p3/wit/world.wit @@ -0,0 +1,2 @@ +// We actually don't use this; it's just to let bindgen! find the corresponding world in wit/deps. +package wasmtime:wasi-tls; diff --git a/crates/wasi-tls/src/providers/mod.rs b/crates/wasi-tls/src/providers/mod.rs new file mode 100644 index 000000000000..c36450ad7d9b --- /dev/null +++ b/crates/wasi-tls/src/providers/mod.rs @@ -0,0 +1,28 @@ +//! The available TLS providers. + +mod unsupported; +pub use unsupported::*; +#[cfg(feature = "rustls")] +mod rustls; +#[cfg(feature = "rustls")] +pub use rustls::RustlsProvider; +#[cfg(feature = "openssl")] +mod openssl; +#[cfg(feature = "openssl")] +pub use openssl::OpenSslProvider; +#[cfg(feature = "nativetls")] +mod nativetls; +#[cfg(feature = "nativetls")] +pub use nativetls::NativeTlsProvider; + +cfg_if::cfg_if! { + if #[cfg(feature = "rustls")] { + pub use RustlsProvider as DefaultProvider; + } else if #[cfg(feature = "openssl")] { + pub use OpenSslProvider as DefaultProvider; + } else if #[cfg(feature = "nativetls")] { + pub use NativeTlsProvider as DefaultProvider; + } else { + pub use UnsupportedProvider as DefaultProvider; + } +} diff --git a/crates/wasi-tls-nativetls/src/lib.rs b/crates/wasi-tls/src/providers/nativetls.rs similarity index 57% rename from crates/wasi-tls-nativetls/src/lib.rs rename to crates/wasi-tls/src/providers/nativetls.rs index 488614512dcb..8691a97fa675 100644 --- a/crates/wasi-tls-nativetls/src/lib.rs +++ b/crates/wasi-tls/src/providers/nativetls.rs @@ -1,10 +1,9 @@ //! The `native_tls` provider. -use std::{io, pin::pin}; - -use wasmtime_wasi_tls::{TlsProvider, TlsStream, TlsTransport}; - -type BoxFuture = std::pin::Pin + Send>>; +use crate::{BoxFutureTlsStream, Error, TlsProvider, TlsStream, TlsTransport}; +use std::io; +use std::pin::{Pin, pin}; +use std::task::Poll; /// The `native_tls` provider. pub struct NativeTlsProvider { @@ -12,27 +11,13 @@ pub struct NativeTlsProvider { } impl TlsProvider for NativeTlsProvider { - fn connect( - &self, - server_name: String, - transport: Box, - ) -> BoxFuture>> { - async fn connect_impl( - server_name: String, - transport: Box, - ) -> Result { + fn connect(&self, server_name: String, transport: Box) -> BoxFutureTlsStream { + Box::pin(async move { let connector = native_tls::TlsConnector::new()?; let stream = tokio_native_tls::TlsConnector::from(connector) .connect(&server_name, transport) .await?; - Ok(NativeTlsStream(stream)) - } - - Box::pin(async move { - let stream = connect_impl(server_name, transport) - .await - .map_err(|e| io::Error::other(e))?; - Ok(Box::new(stream) as Box) + Ok(Box::new(NativeTlsStream(stream)) as Box) }) } } @@ -52,7 +37,7 @@ impl tokio::io::AsyncRead for NativeTlsStream { mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &mut tokio::io::ReadBuf<'_>, - ) -> std::task::Poll> { + ) -> Poll> { pin!(&mut self.as_mut().0).poll_read(cx, buf) } } @@ -62,21 +47,36 @@ impl tokio::io::AsyncWrite for NativeTlsStream { mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &[u8], - ) -> std::task::Poll> { + ) -> Poll> { pin!(&mut self.as_mut().0).poll_write(cx, buf) } fn poll_flush( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { + ) -> Poll> { pin!(&mut self.as_mut().0).poll_flush(cx) } fn poll_shutdown( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - pin!(&mut self.as_mut().0).poll_shutdown(cx) + ) -> Poll> { + match pin!(&mut self.as_mut().0).poll_shutdown(cx) { + Poll::Ready(Ok(())) => {} + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Pending => return Poll::Pending, + } + + // `tokio-rustls` & `tokio-openssl` shut down the underlying transport, + // but `tokio-native-tls` does not, so we need to do that ourselves: + let inner = self.0.get_mut().get_mut().get_mut(); + Pin::new(inner).poll_shutdown(cx) + } +} + +impl From for Error { + fn from(e: native_tls::Error) -> Self { + Error::msg(e.to_string()) } } diff --git a/crates/wasi-tls-openssl/src/lib.rs b/crates/wasi-tls/src/providers/openssl.rs similarity index 75% rename from crates/wasi-tls-openssl/src/lib.rs rename to crates/wasi-tls/src/providers/openssl.rs index 554dfdc010a8..c17b1dc0812e 100644 --- a/crates/wasi-tls-openssl/src/lib.rs +++ b/crates/wasi-tls/src/providers/openssl.rs @@ -1,13 +1,9 @@ //! The `openssl` provider. +use crate::{BoxFutureTlsStream, Error, TlsProvider, TlsStream, TlsTransport}; use openssl::ssl::{SslConnector, SslMethod}; -use std::{ - io, - pin::{Pin, pin}, -}; -use wasmtime_wasi_tls::{TlsProvider, TlsStream, TlsTransport}; - -type BoxFuture = std::pin::Pin + Send>>; +use std::io; +use std::pin::{Pin, pin}; /// The `openssl` provider. pub struct OpenSslProvider { @@ -15,15 +11,8 @@ pub struct OpenSslProvider { } impl TlsProvider for OpenSslProvider { - fn connect( - &self, - server_name: String, - transport: Box, - ) -> BoxFuture>> { - async fn connect_impl( - server_name: String, - transport: Box, - ) -> Result { + fn connect(&self, server_name: String, transport: Box) -> BoxFutureTlsStream { + Box::pin(async move { // Per the `openssl` crate's recommendation, we're using the // `SslConnector` to set up a Ssl object with secure defaults: // @@ -37,14 +26,7 @@ impl TlsProvider for OpenSslProvider { let ssl = config.into_ssl(&server_name)?; let mut stream = tokio_openssl::SslStream::new(ssl, transport)?; Pin::new(&mut stream).connect().await?; - Ok(OpenSslStream(stream)) - } - - Box::pin(async move { - let stream = connect_impl(server_name, transport) - .await - .map_err(|e| io::Error::other(e))?; - Ok(Box::new(stream) as Box) + Ok(Box::new(OpenSslStream(stream)) as Box) }) } } @@ -92,3 +74,14 @@ impl tokio::io::AsyncWrite for OpenSslStream { pin!(&mut self.as_mut().0).poll_shutdown(cx) } } + +impl From for Error { + fn from(e: openssl::ssl::Error) -> Self { + Error::msg(e.to_string()) + } +} +impl From for Error { + fn from(e: openssl::error::ErrorStack) -> Self { + openssl::ssl::Error::from(e).into() + } +} diff --git a/crates/wasi-tls/src/rustls.rs b/crates/wasi-tls/src/providers/rustls.rs similarity index 63% rename from crates/wasi-tls/src/rustls.rs rename to crates/wasi-tls/src/providers/rustls.rs index 0d8fcd2cf22a..7003a84db68f 100644 --- a/crates/wasi-tls/src/rustls.rs +++ b/crates/wasi-tls/src/providers/rustls.rs @@ -1,11 +1,9 @@ //! The `rustls` provider. +use crate::{BoxFutureTlsStream, Error, TlsProvider, TlsStream, TlsTransport}; use rustls::pki_types::ServerName; -use std::io; use std::sync::{Arc, LazyLock}; -use crate::{BoxFuture, TlsProvider, TlsStream, TlsTransport}; - impl crate::TlsStream for tokio_rustls::client::TlsStream> {} /// The `rustls` provider. @@ -14,19 +12,16 @@ pub struct RustlsProvider { } impl TlsProvider for RustlsProvider { - fn connect( - &self, - server_name: String, - transport: Box, - ) -> BoxFuture>> { + fn connect(&self, server_name: String, transport: Box) -> BoxFutureTlsStream { let client_config = Arc::clone(&self.client_config); Box::pin(async move { - let domain = ServerName::try_from(server_name) - .map_err(|_| io::Error::other("invalid server name"))?; + let domain = + ServerName::try_from(server_name).map_err(|_| Error::msg("invalid server name"))?; let stream = tokio_rustls::TlsConnector::from(client_config) .connect(domain, transport) - .await?; + .await + .map_err(rustls_to_wasi_error)?; Ok(Box::new(stream) as Box) }) } @@ -49,3 +44,16 @@ impl Default for RustlsProvider { } } } + +impl From for Error { + fn from(e: rustls::Error) -> Self { + Error::msg(e.to_string()) + } +} + +fn rustls_to_wasi_error(e: std::io::Error) -> Error { + match e.downcast::() { + Ok(e) => e.into(), + Err(io_err) => Error::msg(io_err), + } +} diff --git a/crates/wasi-tls/src/providers/unsupported.rs b/crates/wasi-tls/src/providers/unsupported.rs new file mode 100644 index 000000000000..93ac6fa519ff --- /dev/null +++ b/crates/wasi-tls/src/providers/unsupported.rs @@ -0,0 +1,24 @@ +//! The `unsupported` provider. + +use crate::{BoxFutureTlsStream, Error, TlsProvider, TlsTransport}; + +/// A pseudo TLS provider that returns an error for all operations. This is the +/// default provider when no real TLS providers were enabled at compile time. +#[derive(Default)] +pub struct UnsupportedProvider { + _priv: (), +} + +impl TlsProvider for UnsupportedProvider { + fn connect( + &self, + _server_name: String, + _transport: Box, + ) -> BoxFutureTlsStream { + Box::pin(async move { + Err(Error::msg( + "no TLS provider enabled; recompile with a TLS provider feature", + )) + }) + } +} diff --git a/crates/wasi-tls/tests/main.rs b/crates/wasi-tls/tests/main.rs index 86ddee7f888f..a1f886eb33b8 100644 --- a/crates/wasi-tls/tests/main.rs +++ b/crates/wasi-tls/tests/main.rs @@ -1,68 +1,6 @@ -use wasmtime::{ - Result, Store, - component::{Component, Linker, ResourceTable}, - format_err, -}; -use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView, p2::bindings::Command}; -use wasmtime_wasi_tls::{LinkOptions, WasiTls, WasiTlsCtx, WasiTlsCtxBuilder}; +#![cfg(any(feature = "rustls", feature = "openssl", feature = "nativetls"))] -struct Ctx { - table: ResourceTable, - wasi_ctx: WasiCtx, - wasi_tls_ctx: WasiTlsCtx, -} - -impl WasiView for Ctx { - fn ctx(&mut self) -> WasiCtxView<'_> { - WasiCtxView { - ctx: &mut self.wasi_ctx, - table: &mut self.table, - } - } -} - -async fn run_test(path: &str) -> Result<()> { - let ctx = Ctx { - table: ResourceTable::new(), - wasi_ctx: WasiCtx::builder() - .inherit_stdout() - .inherit_stderr() - .inherit_network() - .allow_ip_name_lookup(true) - .build(), - wasi_tls_ctx: WasiTlsCtxBuilder::new().build(), - }; - - let engine = test_programs_artifacts::engine(|_config| {}); - let mut store = Store::new(&engine, ctx); - let component = Component::from_file(&engine, path)?; - - let mut linker = Linker::new(&engine); - wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; - let mut opts = LinkOptions::default(); - opts.tls(true); - wasmtime_wasi_tls::add_to_linker(&mut linker, &mut opts, |h: &mut Ctx| { - WasiTls::new(&h.wasi_tls_ctx, &mut h.table) - })?; - - let command = Command::instantiate_async(&mut store, &component, &linker).await?; - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| format_err!("command returned with failing exit status")) -} - -macro_rules! assert_test_exists { - ($name:ident) => { - #[expect(unused_imports, reason = "just here to assert it exists")] - use self::$name as _; - }; -} - -test_programs_artifacts::foreach_tls!(assert_test_exists); - -#[tokio::test(flavor = "multi_thread")] -async fn tls_sample_application() -> Result<()> { - run_test(test_programs_artifacts::TLS_SAMPLE_APPLICATION_COMPONENT).await -} +#[cfg(feature = "p2")] +mod p2; +#[cfg(feature = "p3")] +mod p3; diff --git a/crates/wasi-tls/tests/p2/mod.rs b/crates/wasi-tls/tests/p2/mod.rs new file mode 100644 index 000000000000..0e046b988cd7 --- /dev/null +++ b/crates/wasi-tls/tests/p2/mod.rs @@ -0,0 +1,113 @@ +use wasmtime::{ + Result, Store, + component::{Component, Linker, ResourceTable}, + format_err, +}; +use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView, p2::bindings::Command}; +use wasmtime_wasi_tls::{ + TlsProvider, WasiTlsCtx, WasiTlsCtxBuilder, WasiTlsCtxView, WasiTlsView, p2, +}; + +struct Ctx { + table: ResourceTable, + wasi_ctx: WasiCtx, + wasi_tls_ctx: WasiTlsCtx, +} + +impl WasiView for Ctx { + fn ctx(&mut self) -> WasiCtxView<'_> { + WasiCtxView { + ctx: &mut self.wasi_ctx, + table: &mut self.table, + } + } +} + +impl WasiTlsView for Ctx { + fn tls(&mut self) -> WasiTlsCtxView<'_> { + WasiTlsCtxView { + ctx: &mut self.wasi_tls_ctx, + table: &mut self.table, + } + } +} + +async fn run_test(provider: Box, path: &str) -> Result<()> { + let ctx = Ctx { + table: ResourceTable::new(), + wasi_ctx: WasiCtx::builder() + .inherit_stdout() + .inherit_stderr() + .inherit_network() + .allow_ip_name_lookup(true) + .build(), + wasi_tls_ctx: WasiTlsCtxBuilder::new().provider(provider).build(), + }; + + let engine = test_programs_artifacts::engine(|_config| {}); + let mut store = Store::new(&engine, ctx); + let component = Component::from_file(&engine, path)?; + + let mut linker = Linker::new(&engine); + wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; + let mut opts = p2::LinkOptions::default(); + opts.tls(true); + wasmtime_wasi_tls::p2::add_to_linker(&mut linker, &opts)?; + + let command = Command::instantiate_async(&mut store, &component, &linker).await?; + command + .wasi_cli_run() + .call_run(&mut store) + .await? + .map_err(|()| format_err!("command returned with failing exit status")) +} + +macro_rules! test_case { + ($provider:ident, $name:ident) => { + #[tokio::test(flavor = "multi_thread")] + async fn $name() -> wasmtime::Result<()> { + super::$name(Box::new(wasmtime_wasi_tls::$provider::default())).await + } + }; +} + +#[cfg(feature = "rustls")] +mod rustls { + macro_rules! rustls_test_case { + ($name:ident) => { + test_case!(RustlsProvider, $name); + }; + } + + test_programs_artifacts::foreach_p2_tls!(rustls_test_case); +} + +#[cfg(feature = "openssl")] +mod openssl { + macro_rules! openssl_test_case { + ($name:ident) => { + test_case!(OpenSslProvider, $name); + }; + } + + test_programs_artifacts::foreach_p2_tls!(openssl_test_case); +} + +#[cfg(feature = "nativetls")] +mod nativetls { + macro_rules! nativetls_test_case { + ($name:ident) => { + test_case!(NativeTlsProvider, $name); + }; + } + + test_programs_artifacts::foreach_p2_tls!(nativetls_test_case); +} + +async fn p2_tls_sample_application(provider: Box) -> Result<()> { + run_test( + provider, + test_programs_artifacts::P2_TLS_SAMPLE_APPLICATION_COMPONENT, + ) + .await +} diff --git a/crates/wasi-tls/tests/p3/mod.rs b/crates/wasi-tls/tests/p3/mod.rs new file mode 100644 index 000000000000..5ad61830f8e7 --- /dev/null +++ b/crates/wasi-tls/tests/p3/mod.rs @@ -0,0 +1,112 @@ +use wasmtime::{ + Result, Store, + component::{Component, Linker, ResourceTable}, + format_err, +}; +use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView, p3::bindings::Command}; +use wasmtime_wasi_tls::{TlsProvider, WasiTlsCtx, WasiTlsCtxBuilder, WasiTlsCtxView, WasiTlsView}; + +struct Ctx { + table: ResourceTable, + wasi_ctx: WasiCtx, + wasi_tls_ctx: WasiTlsCtx, +} + +impl WasiView for Ctx { + fn ctx(&mut self) -> WasiCtxView<'_> { + WasiCtxView { + ctx: &mut self.wasi_ctx, + table: &mut self.table, + } + } +} + +impl WasiTlsView for Ctx { + fn tls(&mut self) -> WasiTlsCtxView<'_> { + WasiTlsCtxView { + ctx: &mut self.wasi_tls_ctx, + table: &mut self.table, + } + } +} + +async fn run_test(provider: Box, path: &str) -> Result<()> { + let ctx = Ctx { + table: ResourceTable::new(), + wasi_ctx: WasiCtx::builder() + .inherit_stdout() + .inherit_stderr() + .inherit_network() + .allow_ip_name_lookup(true) + .build(), + wasi_tls_ctx: WasiTlsCtxBuilder::new().provider(provider).build(), + }; + + let engine = test_programs_artifacts::engine(|config| { + config.wasm_component_model_async(true); + }); + let mut store = Store::new(&engine, ctx); + let component = Component::from_file(&engine, path)?; + + let mut linker = Linker::new(&engine); + // TODO: Remove p2 once test components are not built for `wasm32-wasip1` + wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; + wasmtime_wasi::p3::add_to_linker(&mut linker)?; + wasmtime_wasi_tls::p3::add_to_linker(&mut linker)?; + + let command = Command::instantiate_async(&mut store, &component, &linker).await?; + store + .run_concurrent(async move |store| command.wasi_cli_run().call_run(store).await) + .await?? + .map_err(|()| format_err!("command returned with failing exit status")) +} + +macro_rules! test_case { + ($provider:ident, $name:ident) => { + #[tokio::test(flavor = "multi_thread")] + async fn $name() -> wasmtime::Result<()> { + super::$name(Box::new(wasmtime_wasi_tls::$provider::default())).await + } + }; +} + +#[cfg(feature = "rustls")] +mod rustls { + macro_rules! rustls_test_case { + ($name:ident) => { + test_case!(RustlsProvider, $name); + }; + } + + test_programs_artifacts::foreach_p3_tls!(rustls_test_case); +} + +#[cfg(feature = "openssl")] +mod openssl { + macro_rules! openssl_test_case { + ($name:ident) => { + test_case!(OpenSslProvider, $name); + }; + } + + test_programs_artifacts::foreach_p3_tls!(openssl_test_case); +} + +#[cfg(feature = "nativetls")] +mod nativetls { + macro_rules! nativetls_test_case { + ($name:ident) => { + test_case!(NativeTlsProvider, $name); + }; + } + + test_programs_artifacts::foreach_p3_tls!(nativetls_test_case); +} + +async fn p3_tls_sample_application(provider: Box) -> Result<()> { + run_test( + provider, + test_programs_artifacts::P3_TLS_SAMPLE_APPLICATION_COMPONENT, + ) + .await +} diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index ec65711df966..ee2d4a95db80 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -15,6 +15,9 @@ include = ["src/**/*", "README.md", "LICENSE", "witx/*", "wit/**/*", "tests/*"] [lints] workspace = true +[package.metadata.docs.rs] +all-features = true + [dependencies] wasmtime = { workspace = true, features = ["runtime", "std"] } wasmtime-wasi-io = { workspace = true, features = ["std"] } diff --git a/crates/wasi/src/cli/worker_thread_stdin.rs b/crates/wasi/src/cli/worker_thread_stdin.rs index 10400cd121fd..10465c04da37 100644 --- a/crates/wasi/src/cli/worker_thread_stdin.rs +++ b/crates/wasi/src/cli/worker_thread_stdin.rs @@ -121,7 +121,8 @@ fn create() -> GlobalStdin { *state.state.lock().unwrap(), StdinState::ReadRequested )); - *state.state.lock().unwrap() = new_state; + let mut lock = state.state.lock().unwrap(); + *lock = new_state; state.read_completed.notify_waiters(); if done { break; @@ -205,6 +206,14 @@ impl AsyncRead for WasiStdinAsyncRead { ) -> Poll> { let g = GlobalStdin::get(); + // Everything below is executed under the global stdin lock. It's not + // going to block below so that's semantically fine. Optimization-wise + // it's probably possible to move this within the loop around just a + // small part of reading/writing the state, but that was done + // historically and it resulted in lost wakeups with `Notify`, so this + // is conservatively hoisted up here. + let mut locked = g.state.lock().unwrap(); + // Perform everything below in a `loop` to handle the case that a read // was stolen by another thread, for example, or perhaps a spurious // notification to `Notified`. @@ -222,7 +231,6 @@ impl AsyncRead for WasiStdinAsyncRead { // Once we're in the "ready" state then take a look at the global // state of stdin. - let mut locked = g.state.lock().unwrap(); match mem::replace(&mut *locked, StdinState::ReadRequested) { // If data is available then drain what we can into `buf`. StdinState::Data(mut data) => { @@ -260,11 +268,6 @@ impl AsyncRead for WasiStdinAsyncRead { } self.set(WasiStdinAsyncRead::Waiting(g.read_completed.notified())); - - // Intentionally drop the lock after the `notified()` future - // creation just above as to work correctly this needs to happen - // within the lock. - drop(locked); } } } diff --git a/crates/wasi/src/filesystem.rs b/crates/wasi/src/filesystem.rs index 917defac77e0..a01770d3355f 100644 --- a/crates/wasi/src/filesystem.rs +++ b/crates/wasi/src/filesystem.rs @@ -966,6 +966,7 @@ impl Dir { if oflags.contains(OpenFlags::TRUNCATE) { opts.truncate(true).write(true); + open_mode |= OpenMode::WRITE; } if flags.contains(DescriptorFlags::READ) { opts.read(true); diff --git a/crates/wasi/src/p2/pipe.rs b/crates/wasi/src/p2/pipe.rs index 4f5b1f5cd03f..def3d52e2e6a 100644 --- a/crates/wasi/src/p2/pipe.rs +++ b/crates/wasi/src/p2/pipe.rs @@ -239,7 +239,15 @@ impl InputStream for AsyncReadStream { self.closed = true; Err(e) } - Err(TryRecvError::Empty) => Ok(Bytes::new()), + Err(TryRecvError::Empty) => { + if self.closed { + // Note: if the stream is already closed it should return an error, + // returning empty list would break the wasi contract (returning 0 and ready) + Err(StreamError::Closed) + } else { + Ok(Bytes::new()) + } + } Err(TryRecvError::Disconnected) => Err(StreamError::Trap(format_err!( "AsyncReadStream sender died - should be impossible" ))), @@ -382,6 +390,11 @@ mod test { assert!(bs.is_empty()); resolves_immediately(reader.ready()).await; assert!(matches!(reader.read(0), Err(StreamError::Closed))); + + // Try again to make sure it keeps returning `StreamError::Closed` + resolves_immediately(reader.ready()).await; + assert!(matches!(reader.read(0), Err(StreamError::Closed))); + assert!(matches!(reader.read(0), Err(StreamError::Closed))); } res => panic!("unexpected: {res:?}"), } @@ -445,6 +458,10 @@ mod test { } res => panic!("unexpected: {res:?}"), } + + // Make sure it stays closed + resolves_immediately(reader.ready()).await; + assert!(matches!(reader.read(0), Err(StreamError::Closed))); } #[test_log::test(tokio::test(flavor = "multi_thread"))] diff --git a/crates/wasi/src/p3/bindings.rs b/crates/wasi/src/p3/bindings.rs index dd100a4fd8a1..d6c11da7703e 100644 --- a/crates/wasi/src/p3/bindings.rs +++ b/crates/wasi/src/p3/bindings.rs @@ -23,7 +23,7 @@ //! // An example of extending the `wasi:cli/command` world with a //! // custom host interface. //! world my-world { -//! include wasi:cli/command@0.3.0-rc-2026-02-09; +//! include wasi:cli/command@0.3.0-rc-2026-03-15; //! //! import custom-host; //! } diff --git a/crates/wasi/src/p3/filesystem/mod.rs b/crates/wasi/src/p3/filesystem/mod.rs index 9977d677044c..0ef9c3166c7f 100644 --- a/crates/wasi/src/p3/filesystem/mod.rs +++ b/crates/wasi/src/p3/filesystem/mod.rs @@ -255,7 +255,7 @@ impl From for types::DescriptorStat { impl From for types::DescriptorType { fn from(ty: crate::filesystem::DescriptorType) -> Self { match ty { - crate::filesystem::DescriptorType::Unknown => Self::Unknown, + crate::filesystem::DescriptorType::Unknown => Self::Other(None), crate::filesystem::DescriptorType::BlockDevice => Self::BlockDevice, crate::filesystem::DescriptorType::CharacterDevice => Self::CharacterDevice, crate::filesystem::DescriptorType::Directory => Self::Directory, @@ -279,7 +279,7 @@ impl From for types::DescriptorType { } else if ft.is_file() { Self::RegularFile } else { - Self::Unknown + Self::Other(None) } } } diff --git a/crates/wasi/src/p3/random/host.rs b/crates/wasi/src/p3/random/host.rs index 03f106a078ff..0bb466b7ab55 100644 --- a/crates/wasi/src/p3/random/host.rs +++ b/crates/wasi/src/p3/random/host.rs @@ -1,14 +1,13 @@ -use cap_rand::Rng; -use cap_rand::distributions::Standard; - use crate::p3::bindings::random::{insecure, insecure_seed, random}; use crate::random::WasiRandomCtx; +use cap_rand::Rng; +use cap_rand::distributions::Standard; impl random::Host for WasiRandomCtx { fn get_random_bytes(&mut self, len: u64) -> wasmtime::Result> { Ok((&mut self.random) .sample_iter(Standard) - .take(len as usize) + .take(len.min(self.max_size) as usize) .collect()) } @@ -21,7 +20,7 @@ impl insecure::Host for WasiRandomCtx { fn get_insecure_random_bytes(&mut self, len: u64) -> wasmtime::Result> { Ok((&mut self.insecure_random) .sample_iter(Standard) - .take(len as usize) + .take(len.min(self.max_size) as usize) .collect()) } diff --git a/crates/wasi/src/p3/sockets/conv.rs b/crates/wasi/src/p3/sockets/conv.rs index 915bd3e96271..a6cc69528ed4 100644 --- a/crates/wasi/src/p3/sockets/conv.rs +++ b/crates/wasi/src/p3/sockets/conv.rs @@ -167,7 +167,7 @@ impl From<&std::io::Error> for types::ErrorCode { std::io::ErrorKind::Unsupported => Self::NotSupported, _ => { debug!("unknown I/O error: {value}"); - Self::Unknown + Self::Other(None) } } } @@ -216,7 +216,7 @@ impl From<&Errno> for types::ErrorCode { // FYI, EINPROGRESS should have already been handled by connect. _ => { debug!("unknown I/O error: {value}"); - Self::Unknown + Self::Other(None) } } } @@ -225,7 +225,7 @@ impl From<&Errno> for types::ErrorCode { impl From for types::ErrorCode { fn from(code: crate::sockets::util::ErrorCode) -> Self { match code { - crate::sockets::util::ErrorCode::Unknown => Self::Unknown, + crate::sockets::util::ErrorCode::Unknown => Self::Other(None), crate::sockets::util::ErrorCode::AccessDenied => Self::AccessDenied, crate::sockets::util::ErrorCode::NotSupported => Self::NotSupported, crate::sockets::util::ErrorCode::InvalidArgument => Self::InvalidArgument, diff --git a/crates/wasi/src/p3/sockets/host/types/tcp.rs b/crates/wasi/src/p3/sockets/host/types/tcp.rs index b6b1ddcbbbda..6a0871565b4b 100644 --- a/crates/wasi/src/p3/sockets/host/types/tcp.rs +++ b/crates/wasi/src/p3/sockets/host/types/tcp.rs @@ -226,10 +226,14 @@ impl StreamConsumer for SendStreamConsumer { Poll::Pending => return Poll::Pending, } } - Err(err) if err.kind() == std::io::ErrorKind::BrokenPipe => { - break 'result Ok(()); + Err(err) => { + break 'result Err(match err.kind() { + // ECONNABORTED is the closest thing to EPIPE on Windows. + std::io::ErrorKind::BrokenPipe + | std::io::ErrorKind::ConnectionAborted => ErrorCode::ConnectionBroken, + _ => err.into(), + }); } - Err(err) => break 'result Err(err.into()), } } }; diff --git a/crates/wasi/src/p3/wit/deps/cli.wit b/crates/wasi/src/p3/wit/deps/cli.wit index 4f8bbf8a24a5..8ba52c5cd594 100644 --- a/crates/wasi/src/p3/wit/deps/cli.wit +++ b/crates/wasi/src/p3/wit/deps/cli.wit @@ -1,6 +1,6 @@ -package wasi:cli@0.3.0-rc-2026-02-09; +package wasi:cli@0.3.0-rc-2026-03-15; -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface environment { /// Get the POSIX-style environment variables. /// @@ -10,23 +10,23 @@ interface environment { /// Morally, these are a value import, but until value imports are available /// in the component model, this import function should return the same /// values each time it is called. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-environment: func() -> list>; /// Get the POSIX-style arguments to the program. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-arguments: func() -> list; /// Return a path that programs should use as their initial current working /// directory, interpreting `.` as shorthand for this. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-initial-cwd: func() -> option; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface exit { /// Exit the current instance and any linked instances. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) exit: func(status: result); /// Exit the current instance and any linked instances, reporting the @@ -41,16 +41,16 @@ interface exit { exit-with-code: func(status-code: u8); } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface run { /// Run the program. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) run: async func() -> result; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum error-code { /// Input/output error io, @@ -61,7 +61,7 @@ interface types { } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stdin { use types.{error-code}; @@ -78,11 +78,11 @@ interface stdin { /// /// Multiple streams may be active at the same time. The behavior of concurrent /// reads is implementation-specific. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-via-stream: func() -> tuple, future>>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stdout { use types.{error-code}; @@ -94,11 +94,11 @@ interface stdout { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream) -> future>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface stderr { use types.{error-code}; @@ -110,7 +110,7 @@ interface stderr { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream) -> future>; } @@ -119,10 +119,10 @@ interface stderr { /// In the future, this may include functions for disabling echoing, /// disabling input buffering so that keyboard events are sent through /// immediately, querying supported features, and so on. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-input { /// The input side of a terminal. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource terminal-input; } @@ -131,126 +131,126 @@ interface terminal-input { /// In the future, this may include functions for querying the terminal /// size, being notified of terminal size changes, querying supported /// features, and so on. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-output { /// The output side of a terminal. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource terminal-output; } /// An interface providing an optional `terminal-input` for stdin as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stdin { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-input.{terminal-input}; /// If stdin is connected to a terminal, return a `terminal-input` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stdin: func() -> option; } /// An interface providing an optional `terminal-output` for stdout as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stdout { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-output.{terminal-output}; /// If stdout is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stdout: func() -> option; } /// An interface providing an optional `terminal-output` for stderr as a /// link-time authority. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface terminal-stderr { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use terminal-output.{terminal-output}; /// If stderr is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-terminal-stderr: func() -> option; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import environment; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import exit; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stderr; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-input; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-output; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-02-09; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:filesystem/types@0.3.0-rc-2026-02-09; - import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; - import wasi:sockets/types@0.3.0-rc-2026-02-09; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:filesystem/types@0.3.0-rc-2026-03-15; + import wasi:filesystem/preopens@0.3.0-rc-2026-03-15; + import wasi:sockets/types@0.3.0-rc-2026-03-15; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world command { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import environment; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import exit; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import stderr; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-input; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-output; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdin; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stdout; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-02-09; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-03-15; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15; + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-02-09; - import wasi:filesystem/types@0.3.0-rc-2026-02-09; - import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; - import wasi:sockets/types@0.3.0-rc-2026-02-09; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; - import wasi:random/random@0.3.0-rc-2026-02-09; - import wasi:random/insecure@0.3.0-rc-2026-02-09; - import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; + import wasi:clocks/timezone@0.3.0-rc-2026-03-15; + import wasi:filesystem/types@0.3.0-rc-2026-03-15; + import wasi:filesystem/preopens@0.3.0-rc-2026-03-15; + import wasi:sockets/types@0.3.0-rc-2026-03-15; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-03-15; + import wasi:random/random@0.3.0-rc-2026-03-15; + import wasi:random/insecure@0.3.0-rc-2026-03-15; + import wasi:random/insecure-seed@0.3.0-rc-2026-03-15; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) export run; } diff --git a/crates/wasi/src/p3/wit/deps/clocks.wit b/crates/wasi/src/p3/wit/deps/clocks.wit index 71986bab2bcc..19fc4bcd59e9 100644 --- a/crates/wasi/src/p3/wit/deps/clocks.wit +++ b/crates/wasi/src/p3/wit/deps/clocks.wit @@ -1,10 +1,10 @@ -package wasi:clocks@0.3.0-rc-2026-02-09; +package wasi:clocks@0.3.0-rc-2026-03-15; /// This interface common types used throughout wasi:clocks. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { /// A duration of time, in nanoseconds. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type duration = u64; } @@ -16,14 +16,14 @@ interface types { /// /// A monotonic clock is a clock which has an unspecified initial value, and /// successive reads of the clock will produce non-decreasing values. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface monotonic-clock { use types.{duration}; /// A mark on a monotonic clock is a number of nanoseconds since an /// unspecified initial value, and can only be compared to instances from /// the same monotonic-clock. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type mark = u64; /// Read the current value of the clock. @@ -35,20 +35,20 @@ interface monotonic-clock { /// the value of the clock in a `mark`. Consequently, implementations /// should ensure that the starting time is low enough to avoid the /// possibility of overflow in practice. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) now: func() -> mark; /// Query the resolution of the clock. Returns the duration of time /// corresponding to a clock tick. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-resolution: func() -> duration; /// Wait until the specified mark has occurred. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) wait-until: async func(when: mark); /// Wait for the specified duration to elapse. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) wait-for: async func(how-long: duration); } @@ -62,7 +62,7 @@ interface monotonic-clock { /// monotonic, making it unsuitable for measuring elapsed time. /// /// It is intended for reporting the current date and time for humans. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface system-clock { use types.{duration}; @@ -82,7 +82,7 @@ interface system-clock { /// /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record instant { seconds: s64, nanoseconds: u32, @@ -94,12 +94,12 @@ interface system-clock { /// will not necessarily produce a sequence of non-decreasing values. /// /// The nanoseconds field of the output is always less than 1000000000. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) now: func() -> instant; /// Query the resolution of the clock. Returns the smallest duration of time /// that the implementation permits distinguishing. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-resolution: func() -> duration; } @@ -148,13 +148,13 @@ interface timezone { to-debug-string: func() -> string; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import monotonic-clock; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import system-clock; @unstable(feature = clocks-timezone) import timezone; diff --git a/crates/wasi/src/p3/wit/deps/filesystem.wit b/crates/wasi/src/p3/wit/deps/filesystem.wit index 8fb83041a103..697681f30c5e 100644 --- a/crates/wasi/src/p3/wit/deps/filesystem.wit +++ b/crates/wasi/src/p3/wit/deps/filesystem.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.3.0-rc-2026-02-09; +package wasi:filesystem@0.3.0-rc-2026-03-15; /// WASI filesystem is a filesystem API primarily intended to let users run WASI /// programs that access their files on their existing filesystems, without @@ -35,23 +35,20 @@ package wasi:filesystem@0.3.0-rc-2026-02-09; /// store or a database instead. /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) - use wasi:clocks/system-clock@0.3.0-rc-2026-02-09.{instant}; + @since(version = 0.3.0-rc-2026-03-15) + use wasi:clocks/system-clock@0.3.0-rc-2026-03-15.{instant}; /// File size or length of a region within a file. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type filesize = u64; /// The type of a filesystem object referenced by a descriptor. /// /// Note: This was called `filetype` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) - enum descriptor-type { - /// The type of the descriptor or file is unknown or is different from - /// any of the other types specified. - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant descriptor-type { /// The descriptor refers to a block device inode. block-device, /// The descriptor refers to a character device inode. @@ -66,12 +63,15 @@ interface types { regular-file, /// The descriptor refers to a socket. socket, + /// The type of the descriptor or file is different from any of the + /// other types specified. + other(option), } /// Descriptor flags. /// /// Note: This was called `fdflags` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags descriptor-flags { /// Read mode: Data can be read. read, @@ -113,7 +113,7 @@ interface types { } /// Flags determining the method of how paths are resolved. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags path-flags { /// As long as the resolved path corresponds to a symbolic link, it is /// expanded. @@ -121,7 +121,7 @@ interface types { } /// Open flags used by `open-at`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) flags open-flags { /// Create file if it does not exist, similar to `O_CREAT` in POSIX. create, @@ -134,13 +134,13 @@ interface types { } /// Number of hard links to an inode. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type link-count = u64; /// File attributes. /// /// Note: This was called `filestat` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record descriptor-stat { /// File type. %type: descriptor-type, @@ -167,7 +167,7 @@ interface types { } /// When setting a timestamp, this gives the value to set it to. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant new-timestamp { /// Leave the timestamp set to its previous value. no-change, @@ -179,7 +179,7 @@ interface types { } /// A directory entry. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record directory-entry { /// The type of the file referred to by this directory entry. %type: descriptor-type, @@ -191,8 +191,8 @@ interface types { /// Not all of these error codes are returned by the functions provided by this /// API; some are used in higher-level library layers, and others are provided /// merely for alignment with POSIX. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Permission denied, similar to `EACCES` in POSIX. access, /// Connection already in progress, similar to `EALREADY` in POSIX. @@ -265,10 +265,14 @@ interface types { text-file-busy, /// Cross-device link, similar to `EXDEV` in POSIX. cross-device, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } /// File or memory access pattern advisory information. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum advice { /// The application has no advice to give on its behavior with respect /// to the specified data. @@ -292,7 +296,7 @@ interface types { /// A 128-bit hash value, split into parts because wasm doesn't have a /// 128-bit integer type. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record metadata-hash-value { /// 64 bits of a 128-bit hash value. lower: u64, @@ -303,7 +307,7 @@ interface types { /// A descriptor is a reference to a filesystem object, which may be a file, /// directory, named pipe, special file, or other object on which filesystem /// calls may be made. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource descriptor { /// Return a stream for reading from a file. /// @@ -321,7 +325,7 @@ interface types { /// resolves to `err` with an `error-code`. /// /// Note: This is similar to `pread` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-via-stream: func(offset: filesize) -> tuple, future>>; /// Return a stream for writing to a file, if available. /// @@ -335,7 +339,7 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `pwrite` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) write-via-stream: func(data: stream, offset: filesize) -> future>; /// Return a stream for appending to a file, if available. /// @@ -345,12 +349,12 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `write` with `O_APPEND` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) append-via-stream: func(data: stream) -> future>; /// Provide file advisory information on a descriptor. /// /// This is similar to `posix_fadvise` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) advise: async func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; /// Synchronize the data of a file to disk. /// @@ -358,7 +362,7 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fdatasync` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) sync-data: async func() -> result<_, error-code>; /// Get flags associated with a descriptor. /// @@ -366,7 +370,7 @@ interface types { /// /// Note: This returns the value that was the `fs_flags` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-flags: async func() -> result; /// Get the dynamic type of a descriptor. /// @@ -378,20 +382,20 @@ interface types { /// /// Note: This returns the value that was the `fs_filetype` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-type: async func() -> result; /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-size: async func(size: filesize) -> result<_, error-code>; /// Adjust the timestamps of an open file or directory. /// /// Note: This is similar to `futimens` in POSIX. /// /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-times: async func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Read directory entries from a directory. /// @@ -405,7 +409,7 @@ interface types { /// /// This function returns a future, which will resolve to an error code if /// reading full contents of the directory fails. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) read-directory: func() -> tuple, future>>; /// Synchronize the data and metadata of a file to disk. /// @@ -413,12 +417,12 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fsync` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) sync: async func() -> result<_, error-code>; /// Create a directory. /// /// Note: This is similar to `mkdirat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create-directory-at: async func(path: string) -> result<_, error-code>; /// Return the attributes of an open file or directory. /// @@ -429,7 +433,7 @@ interface types { /// modified, use `metadata-hash`. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) stat: async func() -> result; /// Return the attributes of a file or directory. /// @@ -438,7 +442,7 @@ interface types { /// discussion of alternatives. /// /// Note: This was called `path_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) stat-at: async func(path-flags: path-flags, path: string) -> result; /// Adjust the timestamps of a file or directory. /// @@ -446,7 +450,7 @@ interface types { /// /// Note: This was called `path_filestat_set_times` in earlier versions of /// WASI. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-times-at: async func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Create a hard link. /// @@ -455,7 +459,7 @@ interface types { /// `error-code::not-permitted` if the old path is not a file. /// /// Note: This is similar to `linkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) link-at: async func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Open a file or directory. /// @@ -469,7 +473,7 @@ interface types { /// `error-code::read-only`. /// /// Note: This is similar to `openat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) open-at: async func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; /// Read the contents of a symbolic link. /// @@ -477,19 +481,19 @@ interface types { /// filesystem, this function fails with `error-code::not-permitted`. /// /// Note: This is similar to `readlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) readlink-at: async func(path: string) -> result; /// Remove a directory. /// /// Return `error-code::not-empty` if the directory is not empty. /// /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) remove-directory-at: async func(path: string) -> result<_, error-code>; /// Rename a filesystem object. /// /// Note: This is similar to `renameat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) rename-at: async func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Create a symbolic link (also known as a "symlink"). /// @@ -497,13 +501,18 @@ interface types { /// `error-code::not-permitted`. /// /// Note: This is similar to `symlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) symlink-at: async func(old-path: string, new-path: string) -> result<_, error-code>; /// Unlink a filesystem object that is not a directory. /// - /// Return `error-code::is-directory` if the path refers to a directory. - /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - @since(version = 0.3.0-rc-2026-02-09) + /// This is similar to `unlinkat(fd, path, 0)` in POSIX. + /// + /// Error returns are as specified by POSIX. + /// + /// If the filesystem object is a directory, `error-code::access` or + /// `error-code::is-directory` may be returned instead of the + /// POSIX-specified `error-code::not-permitted`. + @since(version = 0.3.0-rc-2026-03-15) unlink-file-at: async func(path: string) -> result<_, error-code>; /// Test whether two descriptors refer to the same filesystem object. /// @@ -511,7 +520,7 @@ interface types { /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. /// wasi-filesystem does not expose device and inode numbers, so this function /// may be used instead. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) is-same-object: async func(other: borrow) -> bool; /// Return a hash of the metadata associated with a filesystem object referred /// to by a descriptor. @@ -532,35 +541,35 @@ interface types { /// computed hash. /// /// However, none of these is required. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) metadata-hash: async func() -> result; /// Return a hash of the metadata associated with a filesystem object referred /// to by a directory descriptor and a relative path. /// /// This performs the same hash computation as `metadata-hash`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) metadata-hash-at: async func(path-flags: path-flags, path: string) -> result; } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface preopens { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use types.{descriptor}; /// Return the set of preopened directories, and their paths. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-directories: func() -> list>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/types@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/types@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/system-clock@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import preopens; } diff --git a/crates/wasi/src/p3/wit/deps/random.wit b/crates/wasi/src/p3/wit/deps/random.wit index 521df6e50fc5..026f44a10949 100644 --- a/crates/wasi/src/p3/wit/deps/random.wit +++ b/crates/wasi/src/p3/wit/deps/random.wit @@ -1,10 +1,10 @@ -package wasi:random@0.3.0-rc-2026-02-09; +package wasi:random@0.3.0-rc-2026-03-15; /// The insecure-seed interface for seeding hash-map DoS resistance. /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface insecure-seed { /// Return a 128-bit value that may contain a pseudo-random value. /// @@ -23,7 +23,7 @@ interface insecure-seed { /// This will likely be changed to a value import, to prevent it from being /// called multiple times and potentially used for purposes other than DoS /// protection. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-insecure-seed: func() -> tuple; } @@ -31,9 +31,9 @@ interface insecure-seed { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface insecure { - /// Return `len` insecure pseudo-random bytes. + /// Return up to `max-len` insecure pseudo-random bytes. /// /// This function is not cryptographically secure. Do not use it for /// anything related to security. @@ -41,14 +41,21 @@ interface insecure { /// There are no requirements on the values of the returned bytes, however /// implementations are encouraged to return evenly distributed values with /// a long period. - @since(version = 0.3.0-rc-2026-02-09) - get-insecure-random-bytes: func(len: u64) -> list; + /// + /// Implementations MAY return fewer bytes than requested (a short read). + /// Callers that require exactly `max-len` bytes MUST call this function in + /// a loop until the desired number of bytes has been accumulated. + /// Implementations MUST return at least 1 byte when `max-len` is greater + /// than zero. When `max-len` is zero, implementations MUST return an empty + /// list without trapping. + @since(version = 0.3.0-rc-2026-03-15) + get-insecure-random-bytes: func(max-len: u64) -> list; /// Return an insecure pseudo-random `u64` value. /// /// This function returns the same type of pseudo-random data as /// `get-insecure-random-bytes`, represented as a `u64`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-insecure-random-u64: func() -> u64; } @@ -56,9 +63,10 @@ interface insecure { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface random { - /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// Return up to `max-len` cryptographically-secure random or pseudo-random + /// bytes. /// /// This function must produce data at least as cryptographically secure and /// fast as an adequately seeded cryptographically-secure pseudo-random @@ -67,26 +75,33 @@ interface random { /// request and on requests for numbers of bytes. The returned data must /// always be unpredictable. /// + /// Implementations MAY return fewer bytes than requested (a short read). + /// Callers that require exactly `max-len` bytes MUST call this function in + /// a loop until the desired number of bytes has been accumulated. + /// Implementations MUST return at least 1 byte when `max-len` is greater + /// than zero. When `max-len` is zero, implementations MUST return an empty + /// list without trapping. + /// /// This function must always return fresh data. Deterministic environments /// must omit this function, rather than implementing it with deterministic /// data. - @since(version = 0.3.0-rc-2026-02-09) - get-random-bytes: func(len: u64) -> list; + @since(version = 0.3.0-rc-2026-03-15) + get-random-bytes: func(max-len: u64) -> list; /// Return a cryptographically-secure random or pseudo-random `u64` value. /// /// This function returns the same type of data as `get-random-bytes`, /// represented as a `u64`. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-random-u64: func() -> u64; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import random; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import insecure; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import insecure-seed; } diff --git a/crates/wasi/src/p3/wit/deps/sockets.wit b/crates/wasi/src/p3/wit/deps/sockets.wit index aa9d4d7f074e..cde2e4d6ebe0 100644 --- a/crates/wasi/src/p3/wit/deps/sockets.wit +++ b/crates/wasi/src/p3/wit/deps/sockets.wit @@ -1,63 +1,86 @@ -package wasi:sockets@0.3.0-rc-2026-02-09; +package wasi:sockets@0.3.0-rc-2026-03-15; -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface types { - @since(version = 0.3.0-rc-2026-02-09) - use wasi:clocks/types@0.3.0-rc-2026-02-09.{duration}; + @since(version = 0.3.0-rc-2026-03-15) + use wasi:clocks/types@0.3.0-rc-2026-03-15.{duration}; /// Error codes. /// /// In theory, every API can return any error code. /// In practice, API's typically only return the errors documented per API /// combined with a couple of errors that are always possible: - /// - `unknown` + /// - `other` /// - `access-denied` /// - `not-supported` /// - `out-of-memory` /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { - /// Unknown error - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Access denied. /// /// POSIX equivalent: EACCES, EPERM access-denied, /// The operation is not supported. /// - /// POSIX equivalent: EOPNOTSUPP + /// POSIX equivalent: EOPNOTSUPP, ENOPROTOOPT, EPFNOSUPPORT, EPROTONOSUPPORT, ESOCKTNOSUPPORT not-supported, /// One of the arguments is invalid. /// - /// POSIX equivalent: EINVAL + /// POSIX equivalent: EINVAL, EDESTADDRREQ, EAFNOSUPPORT invalid-argument, /// Not enough memory to complete the operation. /// - /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + /// POSIX equivalent: ENOMEM, ENOBUFS out-of-memory, /// The operation timed out before it could finish completely. + /// + /// POSIX equivalent: ETIMEDOUT timeout, /// The operation is not valid in the socket's current state. invalid-state, - /// A bind operation failed because the provided address is not an address that the `network` can bind to. + /// The local address is not available. + /// + /// POSIX equivalent: EADDRNOTAVAIL address-not-bindable, - /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + /// A bind operation failed because the provided address is already in + /// use or because there are no ephemeral ports available. + /// + /// POSIX equivalent: EADDRINUSE address-in-use, - /// The remote address is not reachable + /// The remote address is not reachable. + /// + /// POSIX equivalent: EHOSTUNREACH, EHOSTDOWN, ENETDOWN, ENETUNREACH, ENONET remote-unreachable, - /// The TCP connection was forcefully rejected + /// The connection was forcefully rejected. + /// + /// POSIX equivalent: ECONNREFUSED connection-refused, - /// The TCP connection was reset. + /// A write failed because the connection was broken. + /// + /// POSIX equivalent: EPIPE + connection-broken, + /// The connection was reset. + /// + /// POSIX equivalent: ECONNRESET connection-reset, - /// A TCP connection was aborted. + /// The connection was aborted. + /// + /// POSIX equivalent: ECONNABORTED connection-aborted, /// The size of a datagram sent to a UDP socket exceeded the maximum /// supported size. + /// + /// POSIX equivalent: EMSGSIZE datagram-too-large, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) enum ip-address-family { /// Similar to `AF_INET` in POSIX. ipv4, @@ -65,19 +88,19 @@ interface types { ipv6, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type ipv4-address = tuple; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) type ipv6-address = tuple; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant ip-address { ipv4(ipv4-address), ipv6(ipv6-address), } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record ipv4-socket-address { /// sin_port port: u16, @@ -85,7 +108,7 @@ interface types { address: ipv4-address, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) record ipv6-socket-address { /// sin6_port port: u16, @@ -97,7 +120,7 @@ interface types { scope-id: u32, } - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) variant ip-socket-address { ipv4(ipv4-socket-address), ipv6(ipv6-socket-address), @@ -112,39 +135,57 @@ interface types { /// - `connecting` /// - `connected` /// - `closed` - /// See + /// See /// for more information. /// /// Note: Except where explicitly mentioned, whenever this documentation uses /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. /// (i.e. `bound`, `listening`, `connecting` or `connected`) /// + /// WASI uses shared ownership semantics: the `tcp-socket` handle and all + /// derived `stream` and `future` values reference a single underlying OS + /// socket: + /// - Send/receive streams remain functional after the original `tcp-socket` + /// handle is dropped. + /// - The stream returned by `listen` behaves similarly. + /// - Client sockets returned by `tcp-socket::listen` are independent and do + /// not keep the listening socket alive. + /// + /// The OS socket is closed only after the last handle is dropped. This + /// model has observable effects; for example, it affects when the local + /// port binding is released. + /// /// In addition to the general error codes documented on the /// `types::error-code` type, TCP socket methods may always return /// `error(invalid-state)` when in the `closed` state. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource tcp-socket { /// Create a new TCP socket. /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. - /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` + /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and + /// can't be configured otherwise. /// /// Unlike POSIX, WASI sockets have no notion of a socket-level /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's /// async support. /// + /// # Typical errors + /// - `not-supported`: The `address-family` is not supported. (EAFNOSUPPORT) + /// /// # References /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is + /// left to the implementation to decide which network interface(s) to + /// bind to. If the TCP/UDP port is zero, the socket will be bound to a + /// random free port. /// /// Bind can be attempted multiple times on the same socket, even with /// different arguments on each iteration. But never concurrently and @@ -161,21 +202,27 @@ interface types { /// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL) /// /// # Implementors note - /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT - /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR - /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior - /// and SO_REUSEADDR performs something different entirely. + /// The bind operation shouldn't be affected by the TIME_WAIT state of a + /// recently closed socket on the same local address. In practice this + /// means that the SO_REUSEADDR socket option should be set implicitly + /// on all platforms, except on Windows where this is the default + /// behavior and SO_REUSEADDR performs something different. /// /// # References /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Connect to a remote endpoint. /// - /// On success, the socket is transitioned into the `connected` state and this function returns a connection resource. + /// On success, the socket is transitioned into the `connected` state + /// and the `remote-address` of the socket is updated. + /// The `local-address` may be updated as well, based on the best network + /// path to `remote-address`. If the socket was not already explicitly + /// bound, this function will implicitly bind the socket to a random + /// free port. /// /// After a failed connection attempt, the socket will be in the `closed` /// state and the only valid action left is to `drop` the socket. A single @@ -202,7 +249,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) connect: async func(remote-address: ip-socket-address) -> result<_, error-code>; /// Start listening and return a stream of new inbound connections. /// @@ -274,7 +321,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) listen: func() -> result, error-code>; /// Transmit data to peer. /// @@ -288,6 +335,8 @@ interface types { /// /// # Typical errors /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `invalid-state`: `send` has already been called on this socket. + /// - `connection-broken`: The connection is not writable anymore. (EPIPE, ECONNABORTED on Windows) /// - `connection-reset`: The connection was reset. (ECONNRESET) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// @@ -296,31 +345,27 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) send: func(data: stream) -> future>; /// Read data from peer. /// - /// This function returns a `stream` which provides the data received from the - /// socket, and a `future` providing additional error information in case the - /// socket is closed abnormally. + /// Returns a `stream` of data sent by the peer. The implementation + /// drops the stream once no more data is available. At that point, the + /// returned `future` resolves to: + /// - `ok` after a graceful shutdown from the peer (i.e. a FIN packet), or + /// - `err` if the socket was closed abnormally. /// - /// If the socket is closed normally, `stream.read` on the `stream` will return - /// `read-status::closed` with no `error-context` and the future resolves to - /// the value `ok`. If the socket is closed abnormally, `stream.read` on the - /// `stream` returns `read-status::closed` with an `error-context` and the future - /// resolves to `err` with an `error-code`. + /// `receive` may be called only once per socket. Subsequent calls return + /// a closed stream and a future resolved to `err(invalid-state)`. /// - /// `receive` is meant to be called only once per socket. If it is called more - /// than once, the subsequent calls return a new `stream` that fails as if it - /// were closed abnormally. - /// - /// If the caller is not expecting to receive any data from the peer, - /// they may drop the stream. Any data still in the receive queue + /// If the caller is not expecting to receive any more data from the peer, + /// they should drop the stream. Any data still in the receive queue /// will be discarded. This is equivalent to calling `shutdown(SHUT_RD)` /// in POSIX. /// /// # Typical errors /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// - `invalid-state`: `receive` has already been called on this socket. /// - `connection-reset`: The connection was reset. (ECONNRESET) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// @@ -329,7 +374,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) receive: func() -> tuple, future>>; /// Get the bound local address. /// @@ -337,7 +382,8 @@ interface types { /// > If the socket has not been bound to a local name, the value /// > stored in the object pointed to by `address` is unspecified. /// - /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// WASI is stricter and requires `get-local-address` to return + /// `invalid-state` when the socket hasn't been bound yet. /// /// # Typical errors /// - `invalid-state`: The socket is not bound to any local address. @@ -347,7 +393,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-local-address: func() -> result; /// Get the remote address. /// @@ -359,30 +405,32 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-remote-address: func() -> result; /// Whether the socket is in the `listening` state. /// /// Equivalent to the SO_ACCEPTCONN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-is-listening: func() -> bool; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-address-family: func() -> ip-address-family; - /// Hints the desired listen queue size. Implementations are free to ignore this. + /// Hints the desired listen queue size. Implementations are free to + /// ignore this. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// Any other value will never cause an error, but it might be silently + /// clamped and/or rounded. /// /// # Typical errors /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. /// - `invalid-argument`: (set) The provided value was 0. /// - `invalid-state`: (set) The socket is in the `connecting` or `connected` state. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-listen-backlog-size: func(value: u64) -> result<_, error-code>; /// Enables or disables keepalive. /// @@ -390,54 +438,60 @@ interface types { /// - `keep-alive-idle-time` /// - `keep-alive-interval` /// - `keep-alive-count` - /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// These properties can be configured while `keep-alive-enabled` is + /// false, but only come into effect when `keep-alive-enabled` is true. /// /// Equivalent to the SO_KEEPALIVE socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-enabled: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; - /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// Amount of time the connection has to be idle before TCP starts + /// sending keepalive packets. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-idle-time: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; /// The time between keepalive packets. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPINTVL socket option. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-interval: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-interval: func(value: duration) -> result<_, error-code>; - /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// The maximum amount of keepalive packets TCP should send before + /// aborting the connection. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the TCP_KEEPCNT socket option. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-keep-alive-count: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-keep-alive-count: func(value: u32) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -445,37 +499,49 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-hop-limit: func(value: u8) -> result<_, error-code>; - /// The kernel buffer space reserved for sends/receives on this socket. + /// Kernel buffer space reserved for sending/receiving on this socket. + /// Implementations usually treat this as a cap the buffer can grow to, + /// rather than allocating the full amount immediately. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. + /// + /// This is only a performance hint. The implementation may ignore it or + /// tweak it based on real traffic patterns. + /// Linux and macOS appear to behave differently depending on whether a + /// buffer size was explicitly set. When set, they tend to honor it; when + /// not set, they dynamically adjust the buffer size as the connection + /// progresses. This is especially noticeable when comparing the values + /// from before and after connection establishment. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } /// A UDP socket handle. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resource udp-socket { /// Create a new UDP socket. /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. - /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` + /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and + /// can't be configured otherwise. /// /// Unlike POSIX, WASI sockets have no notion of a socket-level /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's @@ -486,13 +552,14 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the port is zero, the socket will be bound to a random free port. + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is + /// left to the implementation to decide which network interface(s) to + /// bind to. If the port is zero, the socket will be bound to a random + /// free port. /// /// # Typical errors /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) @@ -506,7 +573,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Associate this socket with a specific peer address. /// @@ -544,12 +611,12 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) connect: func(remote-address: ip-socket-address) -> result<_, error-code>; /// Dissociate this socket from its peer address. /// /// After calling this method, `send` & `receive` are free to communicate - /// with any address again. + /// with any remote address again. /// /// The POSIX equivalent of this is calling `connect` with an `AF_UNSPEC` address. /// @@ -561,7 +628,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) disconnect: func() -> result<_, error-code>; /// Send a message on the socket to a particular peer. /// @@ -573,6 +640,9 @@ interface types { /// _may_ be provided but then it must be identical to the address /// passed to `connect`. /// + /// If the socket has not been explicitly bound, it will be + /// implicitly bound to a random free port. + /// /// Implementations may trap if the `data` length exceeds 64 KiB. /// /// # Typical errors @@ -584,6 +654,14 @@ interface types { /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) /// - `connection-refused`: The connection was refused. (ECONNREFUSED) /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// + /// # Implementors note + /// WASI requires `send` to perform an implicit bind if the socket + /// has not been bound. Not all platforms (notably Windows) exhibit + /// this behavior natively. On such platforms, the WASI implementation + /// should emulate it by performing the bind if the guest has not + /// already done so. /// /// # References /// - @@ -594,7 +672,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) send: async func(data: list, remote-address: option) -> result<_, error-code>; /// Receive a message on the socket. /// @@ -619,7 +697,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) receive: async func() -> result, ip-socket-address>, error-code>; /// Get the current bound address. /// @@ -627,7 +705,8 @@ interface types { /// > If the socket has not been bound to a local name, the value /// > stored in the object pointed to by `address` is unspecified. /// - /// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// WASI is stricter and requires `get-local-address` to return + /// `invalid-state` when the socket hasn't been bound yet. /// /// # Typical errors /// - `invalid-state`: The socket is not bound to any local address. @@ -637,7 +716,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-local-address: func() -> result; /// Get the address the socket is currently "connected" to. /// @@ -649,14 +728,14 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-remote-address: func() -> result; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-address-family: func() -> ip-address-family; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -664,41 +743,42 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-unicast-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; - /// The kernel buffer space reserved for sends/receives on this socket. + /// Kernel buffer space reserved for sending/receiving on this socket. + /// Implementations usually treat this as a cap the buffer can grow to, + /// rather than allocating the full amount immediately. /// /// If the provided value is 0, an `invalid-argument` error is returned. - /// Any other value will never cause an error, but it might be silently clamped and/or rounded. - /// I.e. after setting a value, reading the same setting back may return a different value. + /// All other values are accepted without error, but may be + /// clamped or rounded. As a result, the value read back from + /// this setting may differ from the value that was set. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) interface ip-name-lookup { - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) use types.{ip-address}; /// Lookup error codes. - @since(version = 0.3.0-rc-2026-02-09) - enum error-code { - /// Unknown error - unknown, + @since(version = 0.3.0-rc-2026-03-15) + variant error-code { /// Access denied. /// /// POSIX equivalent: EACCES, EPERM @@ -719,13 +799,17 @@ interface ip-name-lookup { /// /// POSIX equivalent: EAI_FAIL permanent-resolver-failure, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } /// Resolve an internet host name to a list of IP addresses. /// - /// Unicode domain names are automatically converted to ASCII using IDNA encoding. - /// If the input is an IP address string, the address is parsed and returned - /// as-is without making any external requests. + /// Unicode domain names are automatically converted to ASCII using IDNA + /// encoding. If the input is an IP address string, the address is parsed + /// and returned as-is without making any external requests. /// /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. /// @@ -735,24 +819,21 @@ interface ip-name-lookup { /// with at least one address. Additionally, this function never returns /// IPv4-mapped IPv6 addresses. /// - /// The returned future will resolve to an error code in case of failure. - /// It will resolve to success once the returned stream is exhausted. - /// /// # References: /// - /// - /// - /// - - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) resolve-addresses: async func(name: string) -> result, error-code>; } -@since(version = 0.3.0-rc-2026-02-09) +@since(version = 0.3.0-rc-2026-03-15) world imports { - @since(version = 0.3.0-rc-2026-02-09) - import wasi:clocks/types@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) + import wasi:clocks/types@0.3.0-rc-2026-03-15; + @since(version = 0.3.0-rc-2026-03-15) import types; - @since(version = 0.3.0-rc-2026-02-09) + @since(version = 0.3.0-rc-2026-03-15) import ip-name-lookup; } diff --git a/crates/wasi/tests/all/p1.rs b/crates/wasi/tests/all/p1.rs index e27912860ea6..a8118f07f289 100644 --- a/crates/wasi/tests/all/p1.rs +++ b/crates/wasi/tests/all/p1.rs @@ -3,10 +3,10 @@ use std::path::Path; use test_programs_artifacts::*; use wasmtime::Result; use wasmtime::{Linker, Module}; -use wasmtime_wasi::WasiView; use wasmtime_wasi::p1::{WasiP1Ctx, add_to_linker_async}; +use wasmtime_wasi::{WasiCtxBuilder, WasiView}; -async fn run(path: &str, inherit_stdio: bool) -> Result<()> { +async fn run(path: &str, with_builder: impl FnOnce(&mut WasiCtxBuilder)) -> Result<()> { let path = Path::new(path); let name = path.file_stem().unwrap().to_str().unwrap(); let engine = test_programs_artifacts::engine(|_config| {}); @@ -15,9 +15,7 @@ async fn run(path: &str, inherit_stdio: bool) -> Result<()> { let module = Module::from_file(&engine, path)?; let (mut store, _td) = Ctx::new(&engine, name, |builder| { - if inherit_stdio { - builder.inherit_stdio(); - } + with_builder(builder); builder.build_p1() })?; store.data_mut().wasi.ctx().table.set_max_capacity(1000); @@ -33,217 +31,277 @@ foreach_p1!(assert_test_exists); // wasi-tests. #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_big_random_buf() { - run(P1_BIG_RANDOM_BUF, false).await.unwrap() + run(P1_BIG_RANDOM_BUF, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_clock_time_get() { - run(P1_CLOCK_TIME_GET, false).await.unwrap() + run(P1_CLOCK_TIME_GET, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_close_preopen() { - run(P1_CLOSE_PREOPEN, false).await.unwrap() + run(P1_CLOSE_PREOPEN, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_dangling_fd() { - run(P1_DANGLING_FD, false).await.unwrap() + run(P1_DANGLING_FD, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_dangling_symlink() { - run(P1_DANGLING_SYMLINK, false).await.unwrap() + run(P1_DANGLING_SYMLINK, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_directory_seek() { - run(P1_DIRECTORY_SEEK, false).await.unwrap() + run(P1_DIRECTORY_SEEK, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_dir_fd_op_failures() { - run(P1_DIR_FD_OP_FAILURES, false).await.unwrap() + run(P1_DIR_FD_OP_FAILURES, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_advise() { - run(P1_FD_ADVISE, false).await.unwrap() + run(P1_FD_ADVISE, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_filestat_get() { - run(P1_FD_FILESTAT_GET, false).await.unwrap() + run(P1_FD_FILESTAT_GET, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_filestat_set() { - run(P1_FD_FILESTAT_SET, false).await.unwrap() + run(P1_FD_FILESTAT_SET, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_flags_set() { - run(P1_FD_FLAGS_SET, false).await.unwrap() + run(P1_FD_FLAGS_SET, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_readdir() { - run(P1_FD_READDIR, false).await.unwrap() + run(P1_FD_READDIR, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_allocate() { - run(P1_FILE_ALLOCATE, false).await.unwrap() + run(P1_FILE_ALLOCATE, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_pread_pwrite() { - run(P1_FILE_PREAD_PWRITE, false).await.unwrap() + run(P1_FILE_PREAD_PWRITE, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_read_write() { - run(P1_FILE_READ_WRITE, false).await.unwrap() + run(P1_FILE_READ_WRITE, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_seek_tell() { - run(P1_FILE_SEEK_TELL, false).await.unwrap() + run(P1_FILE_SEEK_TELL, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_truncation() { - run(P1_FILE_TRUNCATION, false).await.unwrap() + run(P1_FILE_TRUNCATION, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_unbuffered_write() { - run(P1_FILE_UNBUFFERED_WRITE, false).await.unwrap() + run(P1_FILE_UNBUFFERED_WRITE, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_interesting_paths() { - run(P1_INTERESTING_PATHS, true).await.unwrap() + run(P1_INTERESTING_PATHS, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_regular_file_isatty() { - run(P1_REGULAR_FILE_ISATTY, false).await.unwrap() + run(P1_REGULAR_FILE_ISATTY, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_nofollow_errors() { - run(P1_NOFOLLOW_ERRORS, false).await.unwrap() + run(P1_NOFOLLOW_ERRORS, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_overwrite_preopen() { - run(P1_OVERWRITE_PREOPEN, false).await.unwrap() + run(P1_OVERWRITE_PREOPEN, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_exists() { - run(P1_PATH_EXISTS, false).await.unwrap() + run(P1_PATH_EXISTS, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_filestat() { - run(P1_PATH_FILESTAT, false).await.unwrap() + run(P1_PATH_FILESTAT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_link() { - run(P1_PATH_LINK, false).await.unwrap() + run(P1_PATH_LINK, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_create_existing() { - run(P1_PATH_OPEN_CREATE_EXISTING, false).await.unwrap() + run(P1_PATH_OPEN_CREATE_EXISTING, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_read_write() { - run(P1_PATH_OPEN_READ_WRITE, false).await.unwrap() + run(P1_PATH_OPEN_READ_WRITE, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_dirfd_not_dir() { - run(P1_PATH_OPEN_DIRFD_NOT_DIR, false).await.unwrap() + run(P1_PATH_OPEN_DIRFD_NOT_DIR, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_missing() { - run(P1_PATH_OPEN_MISSING, false).await.unwrap() + run(P1_PATH_OPEN_MISSING, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_nonblock() { - run(P1_PATH_OPEN_NONBLOCK, false).await.unwrap() + run(P1_PATH_OPEN_NONBLOCK, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_rename_dir_trailing_slashes() { - run(P1_PATH_RENAME_DIR_TRAILING_SLASHES, false) + run(P1_PATH_RENAME_DIR_TRAILING_SLASHES, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_rename() { - run(P1_PATH_RENAME, false).await.unwrap() + run(P1_PATH_RENAME, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_symlink_trailing_slashes() { - run(P1_PATH_SYMLINK_TRAILING_SLASHES, false).await.unwrap() + run(P1_PATH_SYMLINK_TRAILING_SLASHES, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_poll_oneoff_files() { - run(P1_POLL_ONEOFF_FILES, false).await.unwrap() + run(P1_POLL_ONEOFF_FILES, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_poll_oneoff_stdio() { - run(P1_POLL_ONEOFF_STDIO, true).await.unwrap() + run(P1_POLL_ONEOFF_STDIO, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_readlink() { - run(P1_READLINK, false).await.unwrap() + run(P1_READLINK, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_remove_directory() { - run(P1_REMOVE_DIRECTORY, false).await.unwrap() + run(P1_REMOVE_DIRECTORY, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_remove_nonempty_directory() { - run(P1_REMOVE_NONEMPTY_DIRECTORY, false).await.unwrap() + run(P1_REMOVE_NONEMPTY_DIRECTORY, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_renumber() { - run(P1_RENUMBER, false).await.unwrap() + run(P1_RENUMBER, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_sched_yield() { - run(P1_SCHED_YIELD, false).await.unwrap() + run(P1_SCHED_YIELD, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_stdio() { - run(P1_STDIO, false).await.unwrap() + run(P1_STDIO, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_stdio_isatty() { // If the test process is setup such that stdio is a terminal: if test_programs_artifacts::stdio_is_terminal() { // Inherit stdio, test asserts each is not tty: - run(P1_STDIO_ISATTY, true).await.unwrap() + run(P1_STDIO_ISATTY, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_stdio_not_isatty() { // Don't inherit stdio, test asserts each is not tty: - run(P1_STDIO_NOT_ISATTY, false).await.unwrap() + run(P1_STDIO_NOT_ISATTY, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_symlink_create() { - run(P1_SYMLINK_CREATE, false).await.unwrap() + run(P1_SYMLINK_CREATE, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_symlink_filestat() { - run(P1_SYMLINK_FILESTAT, false).await.unwrap() + run(P1_SYMLINK_FILESTAT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_symlink_loop() { - run(P1_SYMLINK_LOOP, false).await.unwrap() + run(P1_SYMLINK_LOOP, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_unlink_file_trailing_slashes() { - run(P1_UNLINK_FILE_TRAILING_SLASHES, false).await.unwrap() + run(P1_UNLINK_FILE_TRAILING_SLASHES, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_preopen() { - run(P1_PATH_OPEN_PREOPEN, false).await.unwrap() + run(P1_PATH_OPEN_PREOPEN, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_unicode_output() { - run(P1_UNICODE_OUTPUT, true).await.unwrap() + run(P1_UNICODE_OUTPUT, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_write() { - run(P1_FILE_WRITE, true).await.unwrap() + run(P1_FILE_WRITE, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_lots() { - run(P1_PATH_OPEN_LOTS, true).await.unwrap() + run(P1_PATH_OPEN_LOTS, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_sleep_quickly_but_lots() { - run(P1_SLEEP_QUICKLY_BUT_LOTS, true).await.unwrap() + run(P1_SLEEP_QUICKLY_BUT_LOTS, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn p1_file_truncation_readonly() { + use std::path::PathBuf; + use wasmtime_wasi::{DirPerms, FilePerms}; + + let prefix = format!("wasi_components_truncation_readonly_ro_"); + let tempdir = tempfile::Builder::new() + .prefix(&prefix) + .tempdir() + .expect("create readonly tempdir"); + const FILENAME: &str = "test.txt"; + const EXPECTED_CONTENTS: &[u8] = b"truncation test file\n"; + let mut file: PathBuf = PathBuf::from(tempdir.path()); + file.push(FILENAME); + std::fs::write(&file, EXPECTED_CONTENTS).expect("write truncation test file"); + + run(P1_FILE_TRUNCATION_READONLY, |b| { + b.preopened_dir( + tempdir.path(), + "readonly", + DirPerms::READ | DirPerms::MUTATE, + FilePerms::READ, + ) + .unwrap(); + }) + .await + .expect("run p1_file_truncation_readonly guest"); + + let contents = std::fs::read(&file).expect("read truncation test file"); + assert_eq!(EXPECTED_CONTENTS, contents); } diff --git a/crates/wasi/tests/all/p2/async_.rs b/crates/wasi/tests/all/p2/async_.rs index 41fdd2842d93..cb8ca10a3307 100644 --- a/crates/wasi/tests/all/p2/async_.rs +++ b/crates/wasi/tests/all/p2/async_.rs @@ -3,10 +3,11 @@ use std::path::Path; use test_programs_artifacts::*; use wasmtime::Result; use wasmtime::component::{Component, Linker}; +use wasmtime_wasi::WasiCtxBuilder; use wasmtime_wasi::p2::add_to_linker_async; use wasmtime_wasi::p2::bindings::Command; -async fn run(path: &str, inherit_stdio: bool) -> Result<()> { +async fn run(path: &str, with_builder: impl FnOnce(&mut WasiCtxBuilder)) -> Result<()> { let path = Path::new(path); let name = path.file_stem().unwrap().to_str().unwrap(); let engine = test_programs_artifacts::engine(|_config| {}); @@ -14,9 +15,7 @@ async fn run(path: &str, inherit_stdio: bool) -> Result<()> { add_to_linker_async(&mut linker)?; let (mut store, _td) = Ctx::new(&engine, name, |builder| { - if inherit_stdio { - builder.inherit_stdio(); - } + with_builder(builder); MyWasiCtx::new(builder.build()) })?; let component = Component::from_file(&engine, path)?; @@ -35,308 +34,326 @@ foreach_p2!(assert_test_exists); // wasi-tests. #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_big_random_buf() { - run(P1_BIG_RANDOM_BUF_COMPONENT, false).await.unwrap() + run(P1_BIG_RANDOM_BUF_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_clock_time_get() { - run(P1_CLOCK_TIME_GET_COMPONENT, false).await.unwrap() + run(P1_CLOCK_TIME_GET_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_close_preopen() { - run(P1_CLOSE_PREOPEN_COMPONENT, false).await.unwrap() + run(P1_CLOSE_PREOPEN_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_dangling_fd() { - run(P1_DANGLING_FD_COMPONENT, false).await.unwrap() + run(P1_DANGLING_FD_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_dangling_symlink() { - run(P1_DANGLING_SYMLINK_COMPONENT, false).await.unwrap() + run(P1_DANGLING_SYMLINK_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_directory_seek() { - run(P1_DIRECTORY_SEEK_COMPONENT, false).await.unwrap() + run(P1_DIRECTORY_SEEK_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_dir_fd_op_failures() { - run(P1_DIR_FD_OP_FAILURES_COMPONENT, false).await.unwrap() + run(P1_DIR_FD_OP_FAILURES_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_advise() { - run(P1_FD_ADVISE_COMPONENT, false).await.unwrap() + run(P1_FD_ADVISE_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_filestat_get() { - run(P1_FD_FILESTAT_GET_COMPONENT, false).await.unwrap() + run(P1_FD_FILESTAT_GET_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_filestat_set() { - run(P1_FD_FILESTAT_SET_COMPONENT, false).await.unwrap() + run(P1_FD_FILESTAT_SET_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_flags_set() { - run(P1_FD_FLAGS_SET_COMPONENT, false).await.unwrap() + run(P1_FD_FLAGS_SET_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_fd_readdir() { - run(P1_FD_READDIR_COMPONENT, false).await.unwrap() + run(P1_FD_READDIR_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_allocate() { - run(P1_FILE_ALLOCATE_COMPONENT, false).await.unwrap() + run(P1_FILE_ALLOCATE_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_pread_pwrite() { - run(P1_FILE_PREAD_PWRITE_COMPONENT, false).await.unwrap() + run(P1_FILE_PREAD_PWRITE_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_read_write() { - run(P1_FILE_READ_WRITE_COMPONENT, false).await.unwrap() + run(P1_FILE_READ_WRITE_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_seek_tell() { - run(P1_FILE_SEEK_TELL_COMPONENT, false).await.unwrap() + run(P1_FILE_SEEK_TELL_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_truncation() { - run(P1_FILE_TRUNCATION_COMPONENT, false).await.unwrap() + run(P1_FILE_TRUNCATION_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_unbuffered_write() { - run(P1_FILE_UNBUFFERED_WRITE_COMPONENT, false) + run(P1_FILE_UNBUFFERED_WRITE_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_interesting_paths() { - run(P1_INTERESTING_PATHS_COMPONENT, true).await.unwrap() + run(P1_INTERESTING_PATHS_COMPONENT, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_regular_file_isatty() { - run(P1_REGULAR_FILE_ISATTY_COMPONENT, false).await.unwrap() + run(P1_REGULAR_FILE_ISATTY_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_nofollow_errors() { - run(P1_NOFOLLOW_ERRORS_COMPONENT, false).await.unwrap() + run(P1_NOFOLLOW_ERRORS_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_overwrite_preopen() { - run(P1_OVERWRITE_PREOPEN_COMPONENT, false).await.unwrap() + run(P1_OVERWRITE_PREOPEN_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_exists() { - run(P1_PATH_EXISTS_COMPONENT, false).await.unwrap() + run(P1_PATH_EXISTS_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_filestat() { - run(P1_PATH_FILESTAT_COMPONENT, false).await.unwrap() + run(P1_PATH_FILESTAT_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_link() { - run(P1_PATH_LINK_COMPONENT, false).await.unwrap() + run(P1_PATH_LINK_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_create_existing() { - run(P1_PATH_OPEN_CREATE_EXISTING_COMPONENT, false) + run(P1_PATH_OPEN_CREATE_EXISTING_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_read_write() { - run(P1_PATH_OPEN_READ_WRITE_COMPONENT, false).await.unwrap() + run(P1_PATH_OPEN_READ_WRITE_COMPONENT, |_| {}) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_dirfd_not_dir() { - run(P1_PATH_OPEN_DIRFD_NOT_DIR_COMPONENT, false) + run(P1_PATH_OPEN_DIRFD_NOT_DIR_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_missing() { - run(P1_PATH_OPEN_MISSING_COMPONENT, false).await.unwrap() + run(P1_PATH_OPEN_MISSING_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_nonblock() { - run(P1_PATH_OPEN_NONBLOCK_COMPONENT, false).await.unwrap() + run(P1_PATH_OPEN_NONBLOCK_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_rename_dir_trailing_slashes() { - run(P1_PATH_RENAME_DIR_TRAILING_SLASHES_COMPONENT, false) + run(P1_PATH_RENAME_DIR_TRAILING_SLASHES_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_rename() { - run(P1_PATH_RENAME_COMPONENT, false).await.unwrap() + run(P1_PATH_RENAME_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_symlink_trailing_slashes() { - run(P1_PATH_SYMLINK_TRAILING_SLASHES_COMPONENT, false) + run(P1_PATH_SYMLINK_TRAILING_SLASHES_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_poll_oneoff_files() { - run(P1_POLL_ONEOFF_FILES_COMPONENT, false).await.unwrap() + run(P1_POLL_ONEOFF_FILES_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_poll_oneoff_stdio() { - run(P1_POLL_ONEOFF_STDIO_COMPONENT, true).await.unwrap() + run(P1_POLL_ONEOFF_STDIO_COMPONENT, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_readlink() { - run(P1_READLINK_COMPONENT, false).await.unwrap() + run(P1_READLINK_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_remove_directory() { - run(P1_REMOVE_DIRECTORY_COMPONENT, false).await.unwrap() + run(P1_REMOVE_DIRECTORY_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_remove_nonempty_directory() { - run(P1_REMOVE_NONEMPTY_DIRECTORY_COMPONENT, false) + run(P1_REMOVE_NONEMPTY_DIRECTORY_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_renumber() { - run(P1_RENUMBER_COMPONENT, false).await.unwrap() + run(P1_RENUMBER_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_sched_yield() { - run(P1_SCHED_YIELD_COMPONENT, false).await.unwrap() + run(P1_SCHED_YIELD_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_stdio() { - run(P1_STDIO_COMPONENT, false).await.unwrap() + run(P1_STDIO_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_stdio_isatty() { // If the test process is setup such that stdio is a terminal: if test_programs_artifacts::stdio_is_terminal() { // Inherit stdio, test asserts each is not tty: - run(P1_STDIO_ISATTY_COMPONENT, true).await.unwrap() + run(P1_STDIO_ISATTY_COMPONENT, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_stdio_not_isatty() { // Don't inherit stdio, test asserts each is not tty: - run(P1_STDIO_NOT_ISATTY_COMPONENT, false).await.unwrap() + run(P1_STDIO_NOT_ISATTY_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_symlink_create() { - run(P1_SYMLINK_CREATE_COMPONENT, false).await.unwrap() + run(P1_SYMLINK_CREATE_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_symlink_filestat() { - run(P1_SYMLINK_FILESTAT_COMPONENT, false).await.unwrap() + run(P1_SYMLINK_FILESTAT_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_symlink_loop() { - run(P1_SYMLINK_LOOP_COMPONENT, false).await.unwrap() + run(P1_SYMLINK_LOOP_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_unlink_file_trailing_slashes() { - run(P1_UNLINK_FILE_TRAILING_SLASHES_COMPONENT, false) + run(P1_UNLINK_FILE_TRAILING_SLASHES_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_preopen() { - run(P1_PATH_OPEN_PREOPEN_COMPONENT, false).await.unwrap() + run(P1_PATH_OPEN_PREOPEN_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_unicode_output() { - run(P1_UNICODE_OUTPUT_COMPONENT, true).await.unwrap() + run(P1_UNICODE_OUTPUT_COMPONENT, |b| { + b.inherit_stdio(); + }) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_file_write() { - run(P1_FILE_WRITE_COMPONENT, false).await.unwrap() + run(P1_FILE_WRITE_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_path_open_lots() { - run(P1_PATH_OPEN_LOTS_COMPONENT, false).await.unwrap() + run(P1_PATH_OPEN_LOTS_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p1_sleep_quickly_but_lots() { - run(P1_SLEEP_QUICKLY_BUT_LOTS_COMPONENT, false) + run(P1_SLEEP_QUICKLY_BUT_LOTS_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_sleep() { - run(P2_SLEEP_COMPONENT, false).await.unwrap() + run(P2_SLEEP_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_random() { - run(P2_RANDOM_COMPONENT, false).await.unwrap() + run(P2_RANDOM_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_ip_name_lookup() { - run(P2_IP_NAME_LOOKUP_COMPONENT, false).await.unwrap() + run(P2_IP_NAME_LOOKUP_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_tcp_sockopts() { - run(P2_TCP_SOCKOPTS_COMPONENT, false).await.unwrap() + run(P2_TCP_SOCKOPTS_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_tcp_sample_application() { - run(P2_TCP_SAMPLE_APPLICATION_COMPONENT, false) + run(P2_TCP_SAMPLE_APPLICATION_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_tcp_states() { - run(P2_TCP_STATES_COMPONENT, false).await.unwrap() + run(P2_TCP_STATES_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_tcp_streams() { - run(P2_TCP_STREAMS_COMPONENT, false).await.unwrap() + run(P2_TCP_STREAMS_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_tcp_bind() { - run(P2_TCP_BIND_COMPONENT, false).await.unwrap() + run(P2_TCP_BIND_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_tcp_connect() { - run(P2_TCP_CONNECT_COMPONENT, false).await.unwrap() + run(P2_TCP_CONNECT_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_tcp_listen() { - run(P2_TCP_LISTEN_COMPONENT, false).await.unwrap() + run(P2_TCP_LISTEN_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_udp_sockopts() { - run(P2_UDP_SOCKOPTS_COMPONENT, false).await.unwrap() + run(P2_UDP_SOCKOPTS_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_udp_sample_application() { - run(P2_UDP_SAMPLE_APPLICATION_COMPONENT, false) + run(P2_UDP_SAMPLE_APPLICATION_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_udp_states() { - run(P2_UDP_STATES_COMPONENT, false).await.unwrap() + run(P2_UDP_STATES_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_udp_bind() { - run(P2_UDP_BIND_COMPONENT, false).await.unwrap() + run(P2_UDP_BIND_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_udp_connect() { - run(P2_UDP_CONNECT_COMPONENT, false).await.unwrap() + run(P2_UDP_CONNECT_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_stream_pollable_correct() { - run(P2_STREAM_POLLABLE_CORRECT_COMPONENT, false) + run(P2_STREAM_POLLABLE_CORRECT_COMPONENT, |_| {}) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_stream_pollable_traps() { - let e = run(P2_STREAM_POLLABLE_TRAPS_COMPONENT, false) + let e = run(P2_STREAM_POLLABLE_TRAPS_COMPONENT, |_| {}) .await .unwrap_err(); assert_eq!( @@ -346,11 +363,11 @@ async fn p2_stream_pollable_traps() { } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_pollable_correct() { - run(P2_POLLABLE_CORRECT_COMPONENT, false).await.unwrap() + run(P2_POLLABLE_CORRECT_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_pollable_traps() { - let e = run(P2_POLLABLE_TRAPS_COMPONENT, false).await.unwrap_err(); + let e = run(P2_POLLABLE_TRAPS_COMPONENT, |_| {}).await.unwrap_err(); assert_eq!( format!("{}", e.source().expect("trap source")), "empty poll list" @@ -358,15 +375,15 @@ async fn p2_pollable_traps() { } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_adapter_badfd() { - run(P2_ADAPTER_BADFD_COMPONENT, false).await.unwrap() + run(P2_ADAPTER_BADFD_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_file_read_write() { - run(P2_FILE_READ_WRITE_COMPONENT, false).await.unwrap() + run(P2_FILE_READ_WRITE_COMPONENT, |_| {}).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p2_udp_send_too_much() { - let e = run(P2_UDP_SEND_TOO_MUCH_COMPONENT, false) + let e = run(P2_UDP_SEND_TOO_MUCH_COMPONENT, |_| {}) .await .unwrap_err(); assert_eq!( @@ -374,3 +391,43 @@ async fn p2_udp_send_too_much() { "unpermitted: argument exceeds permitted size" ) } + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn p1_file_truncation_readonly() { + file_truncation_readonly(P1_FILE_TRUNCATION_READONLY_COMPONENT).await +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn p2_file_truncation_readonly() { + file_truncation_readonly(P2_FILE_TRUNCATION_READONLY_COMPONENT).await +} + +async fn file_truncation_readonly(component_path: &str) { + use std::path::PathBuf; + use wasmtime_wasi::{DirPerms, FilePerms}; + + let prefix = "wasi_components_truncation_readonly_ro_"; + let tempdir = tempfile::Builder::new() + .prefix(prefix) + .tempdir() + .expect("create readonly tempdir"); + const FILENAME: &str = "test.txt"; + const EXPECTED_CONTENTS: &[u8] = b"truncation test file\n"; + let mut file: PathBuf = PathBuf::from(tempdir.path()); + file.push(FILENAME); + std::fs::write(&file, EXPECTED_CONTENTS).expect("write truncation test file"); + + run(component_path, |b| { + b.preopened_dir( + tempdir.path(), + "readonly", + DirPerms::READ | DirPerms::MUTATE, + FilePerms::READ, + ) + .unwrap(); + }) + .await + .expect("run p1_file_truncation_readonly guest"); + + let contents = std::fs::read(&file).expect("read truncation test file"); + assert_eq!(EXPECTED_CONTENTS, contents); +} diff --git a/crates/wasi/tests/all/p2/sync.rs b/crates/wasi/tests/all/p2/sync.rs index b7cbdc8e1217..712b84a15bbf 100644 --- a/crates/wasi/tests/all/p2/sync.rs +++ b/crates/wasi/tests/all/p2/sync.rs @@ -3,10 +3,11 @@ use std::path::Path; use test_programs_artifacts::*; use wasmtime::Result; use wasmtime::component::{Component, Linker}; +use wasmtime_wasi::WasiCtxBuilder; use wasmtime_wasi::p2::add_to_linker_sync; use wasmtime_wasi::p2::bindings::sync::Command; -fn run(path: &str, inherit_stdio: bool) -> Result<()> { +fn run(path: &str, with_builder: impl Fn(&mut WasiCtxBuilder)) -> Result<()> { let path = Path::new(path); let name = path.file_stem().unwrap().to_str().unwrap(); let engine = test_programs_artifacts::engine(|_| {}); @@ -17,9 +18,7 @@ fn run(path: &str, inherit_stdio: bool) -> Result<()> { for blocking in [false, true] { let (mut store, _td) = Ctx::new(&engine, name, |builder| { - if inherit_stdio { - builder.inherit_stdio(); - } + with_builder(builder); builder.allow_blocking_current_thread(blocking); MyWasiCtx::new(builder.build()) })?; @@ -39,286 +38,295 @@ foreach_p2!(assert_test_exists); // wasi-tests. #[test_log::test] fn p1_big_random_buf() { - run(P1_BIG_RANDOM_BUF_COMPONENT, false).unwrap() + run(P1_BIG_RANDOM_BUF_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_clock_time_get() { - run(P1_CLOCK_TIME_GET_COMPONENT, false).unwrap() + run(P1_CLOCK_TIME_GET_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_close_preopen() { - run(P1_CLOSE_PREOPEN_COMPONENT, false).unwrap() + run(P1_CLOSE_PREOPEN_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_dangling_fd() { - run(P1_DANGLING_FD_COMPONENT, false).unwrap() + run(P1_DANGLING_FD_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_dangling_symlink() { - run(P1_DANGLING_SYMLINK_COMPONENT, false).unwrap() + run(P1_DANGLING_SYMLINK_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_directory_seek() { - run(P1_DIRECTORY_SEEK_COMPONENT, false).unwrap() + run(P1_DIRECTORY_SEEK_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_dir_fd_op_failures() { - run(P1_DIR_FD_OP_FAILURES_COMPONENT, false).unwrap() + run(P1_DIR_FD_OP_FAILURES_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_fd_advise() { - run(P1_FD_ADVISE_COMPONENT, false).unwrap() + run(P1_FD_ADVISE_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_fd_filestat_get() { - run(P1_FD_FILESTAT_GET_COMPONENT, false).unwrap() + run(P1_FD_FILESTAT_GET_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_fd_filestat_set() { - run(P1_FD_FILESTAT_SET_COMPONENT, false).unwrap() + run(P1_FD_FILESTAT_SET_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_fd_flags_set() { - run(P1_FD_FLAGS_SET_COMPONENT, false).unwrap() + run(P1_FD_FLAGS_SET_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_fd_readdir() { - run(P1_FD_READDIR_COMPONENT, false).unwrap() + run(P1_FD_READDIR_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_file_allocate() { - run(P1_FILE_ALLOCATE_COMPONENT, false).unwrap() + run(P1_FILE_ALLOCATE_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_file_pread_pwrite() { - run(P1_FILE_PREAD_PWRITE_COMPONENT, false).unwrap() + run(P1_FILE_PREAD_PWRITE_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_file_read_write() { - run(P1_FILE_READ_WRITE_COMPONENT, false).unwrap() + run(P1_FILE_READ_WRITE_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_file_seek_tell() { - run(P1_FILE_SEEK_TELL_COMPONENT, false).unwrap() + run(P1_FILE_SEEK_TELL_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_file_truncation() { - run(P1_FILE_TRUNCATION_COMPONENT, false).unwrap() + run(P1_FILE_TRUNCATION_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_file_unbuffered_write() { - run(P1_FILE_UNBUFFERED_WRITE_COMPONENT, false).unwrap() + run(P1_FILE_UNBUFFERED_WRITE_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_interesting_paths() { - run(P1_INTERESTING_PATHS_COMPONENT, false).unwrap() + run(P1_INTERESTING_PATHS_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_regular_file_isatty() { - run(P1_REGULAR_FILE_ISATTY_COMPONENT, false).unwrap() + run(P1_REGULAR_FILE_ISATTY_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_nofollow_errors() { - run(P1_NOFOLLOW_ERRORS_COMPONENT, false).unwrap() + run(P1_NOFOLLOW_ERRORS_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_overwrite_preopen() { - run(P1_OVERWRITE_PREOPEN_COMPONENT, false).unwrap() + run(P1_OVERWRITE_PREOPEN_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_exists() { - run(P1_PATH_EXISTS_COMPONENT, false).unwrap() + run(P1_PATH_EXISTS_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_filestat() { - run(P1_PATH_FILESTAT_COMPONENT, false).unwrap() + run(P1_PATH_FILESTAT_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_link() { - run(P1_PATH_LINK_COMPONENT, false).unwrap() + run(P1_PATH_LINK_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_open_create_existing() { - run(P1_PATH_OPEN_CREATE_EXISTING_COMPONENT, false).unwrap() + run(P1_PATH_OPEN_CREATE_EXISTING_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_open_read_write() { - run(P1_PATH_OPEN_READ_WRITE_COMPONENT, false).unwrap() + run(P1_PATH_OPEN_READ_WRITE_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_open_dirfd_not_dir() { - run(P1_PATH_OPEN_DIRFD_NOT_DIR_COMPONENT, false).unwrap() + run(P1_PATH_OPEN_DIRFD_NOT_DIR_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_open_missing() { - run(P1_PATH_OPEN_MISSING_COMPONENT, false).unwrap() + run(P1_PATH_OPEN_MISSING_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_open_nonblock() { - run(P1_PATH_OPEN_NONBLOCK_COMPONENT, false).unwrap() + run(P1_PATH_OPEN_NONBLOCK_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_rename_dir_trailing_slashes() { - run(P1_PATH_RENAME_DIR_TRAILING_SLASHES_COMPONENT, false).unwrap() + run(P1_PATH_RENAME_DIR_TRAILING_SLASHES_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_rename() { - run(P1_PATH_RENAME_COMPONENT, false).unwrap() + run(P1_PATH_RENAME_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_symlink_trailing_slashes() { - run(P1_PATH_SYMLINK_TRAILING_SLASHES_COMPONENT, false).unwrap() + run(P1_PATH_SYMLINK_TRAILING_SLASHES_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_poll_oneoff_files() { - run(P1_POLL_ONEOFF_FILES_COMPONENT, false).unwrap() + run(P1_POLL_ONEOFF_FILES_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_poll_oneoff_stdio() { - run(P1_POLL_ONEOFF_STDIO_COMPONENT, true).unwrap() + run(P1_POLL_ONEOFF_STDIO_COMPONENT, |b| { + b.inherit_stdio(); + }) + .unwrap() } #[test_log::test] fn p1_readlink() { - run(P1_READLINK_COMPONENT, false).unwrap() + run(P1_READLINK_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_remove_directory() { - run(P1_REMOVE_DIRECTORY_COMPONENT, false).unwrap() + run(P1_REMOVE_DIRECTORY_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_remove_nonempty_directory() { - run(P1_REMOVE_NONEMPTY_DIRECTORY_COMPONENT, false).unwrap() + run(P1_REMOVE_NONEMPTY_DIRECTORY_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_renumber() { - run(P1_RENUMBER_COMPONENT, false).unwrap() + run(P1_RENUMBER_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_sched_yield() { - run(P1_SCHED_YIELD_COMPONENT, false).unwrap() + run(P1_SCHED_YIELD_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_stdio() { - run(P1_STDIO_COMPONENT, false).unwrap() + run(P1_STDIO_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_stdio_isatty() { // If the test process is setup such that stdio is a terminal: if test_programs_artifacts::stdio_is_terminal() { // Inherit stdio, test asserts each is not tty: - run(P1_STDIO_ISATTY_COMPONENT, true).unwrap() + run(P1_STDIO_ISATTY_COMPONENT, |b| { + b.inherit_stdio(); + }) + .unwrap() } } #[test_log::test] fn p1_stdio_not_isatty() { // Don't inherit stdio, test asserts each is not tty: - run(P1_STDIO_NOT_ISATTY_COMPONENT, false).unwrap() + run(P1_STDIO_NOT_ISATTY_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_symlink_create() { - run(P1_SYMLINK_CREATE_COMPONENT, false).unwrap() + run(P1_SYMLINK_CREATE_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_symlink_filestat() { - run(P1_SYMLINK_FILESTAT_COMPONENT, false).unwrap() + run(P1_SYMLINK_FILESTAT_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_symlink_loop() { - run(P1_SYMLINK_LOOP_COMPONENT, false).unwrap() + run(P1_SYMLINK_LOOP_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_unlink_file_trailing_slashes() { - run(P1_UNLINK_FILE_TRAILING_SLASHES_COMPONENT, false).unwrap() + run(P1_UNLINK_FILE_TRAILING_SLASHES_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_open_preopen() { - run(P1_PATH_OPEN_PREOPEN_COMPONENT, false).unwrap() + run(P1_PATH_OPEN_PREOPEN_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_unicode_output() { - run(P1_UNICODE_OUTPUT_COMPONENT, true).unwrap() + run(P1_UNICODE_OUTPUT_COMPONENT, |b| { + b.inherit_stdio(); + }) + .unwrap() } #[test_log::test] fn p1_file_write() { - run(P1_FILE_WRITE_COMPONENT, false).unwrap() + run(P1_FILE_WRITE_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_path_open_lots() { - run(P1_PATH_OPEN_LOTS_COMPONENT, false).unwrap() + run(P1_PATH_OPEN_LOTS_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p1_sleep_quickly_but_lots() { - run(P1_SLEEP_QUICKLY_BUT_LOTS_COMPONENT, false).unwrap() + run(P1_SLEEP_QUICKLY_BUT_LOTS_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_sleep() { - run(P2_SLEEP_COMPONENT, false).unwrap() + run(P2_SLEEP_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_random() { - run(P2_RANDOM_COMPONENT, false).unwrap() + run(P2_RANDOM_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_ip_name_lookup() { - run(P2_IP_NAME_LOOKUP_COMPONENT, false).unwrap() + run(P2_IP_NAME_LOOKUP_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_tcp_sockopts() { - run(P2_TCP_SOCKOPTS_COMPONENT, false).unwrap() + run(P2_TCP_SOCKOPTS_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_tcp_sample_application() { - run(P2_TCP_SAMPLE_APPLICATION_COMPONENT, false).unwrap() + run(P2_TCP_SAMPLE_APPLICATION_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_tcp_states() { - run(P2_TCP_STATES_COMPONENT, false).unwrap() + run(P2_TCP_STATES_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_tcp_streams() { - run(P2_TCP_STREAMS_COMPONENT, false).unwrap() + run(P2_TCP_STREAMS_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_tcp_bind() { - run(P2_TCP_BIND_COMPONENT, false).unwrap() + run(P2_TCP_BIND_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_tcp_connect() { - run(P2_TCP_CONNECT_COMPONENT, false).unwrap() + run(P2_TCP_CONNECT_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_tcp_listen() { - run(P2_TCP_LISTEN_COMPONENT, false).unwrap() + run(P2_TCP_LISTEN_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_udp_sockopts() { - run(P2_UDP_SOCKOPTS_COMPONENT, false).unwrap() + run(P2_UDP_SOCKOPTS_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_udp_sample_application() { - run(P2_UDP_SAMPLE_APPLICATION_COMPONENT, false).unwrap() + run(P2_UDP_SAMPLE_APPLICATION_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_udp_states() { - run(P2_UDP_STATES_COMPONENT, false).unwrap() + run(P2_UDP_STATES_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_udp_bind() { - run(P2_UDP_BIND_COMPONENT, false).unwrap() + run(P2_UDP_BIND_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_udp_connect() { - run(P2_UDP_CONNECT_COMPONENT, false).unwrap() + run(P2_UDP_CONNECT_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_stream_pollable_correct() { - run(P2_STREAM_POLLABLE_CORRECT_COMPONENT, false).unwrap() + run(P2_STREAM_POLLABLE_CORRECT_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_stream_pollable_traps() { - let e = run(P2_STREAM_POLLABLE_TRAPS_COMPONENT, false).unwrap_err(); + let e = run(P2_STREAM_POLLABLE_TRAPS_COMPONENT, |_| {}).unwrap_err(); assert_eq!( format!("{}", e.source().expect("trap source")), "resource has children" @@ -326,11 +334,11 @@ fn p2_stream_pollable_traps() { } #[test_log::test] fn p2_pollable_correct() { - run(P2_POLLABLE_CORRECT_COMPONENT, false).unwrap() + run(P2_POLLABLE_CORRECT_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_pollable_traps() { - let e = run(P2_POLLABLE_TRAPS_COMPONENT, false).unwrap_err(); + let e = run(P2_POLLABLE_TRAPS_COMPONENT, |_| {}).unwrap_err(); assert_eq!( format!("{}", e.source().expect("trap source")), "empty poll list" @@ -338,17 +346,56 @@ fn p2_pollable_traps() { } #[test_log::test] fn p2_adapter_badfd() { - run(P2_ADAPTER_BADFD_COMPONENT, false).unwrap() + run(P2_ADAPTER_BADFD_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_file_read_write() { - run(P2_FILE_READ_WRITE_COMPONENT, false).unwrap() + run(P2_FILE_READ_WRITE_COMPONENT, |_| {}).unwrap() } #[test_log::test] fn p2_udp_send_too_much() { - let e = run(P2_UDP_SEND_TOO_MUCH_COMPONENT, false).unwrap_err(); + let e = run(P2_UDP_SEND_TOO_MUCH_COMPONENT, |_| {}).unwrap_err(); assert_eq!( format!("{}", e.source().expect("trap source")), "unpermitted: argument exceeds permitted size" ) } + +#[test_log::test] +fn p1_file_truncation_readonly() { + file_truncation_readonly(P1_FILE_TRUNCATION_READONLY_COMPONENT) +} +#[test_log::test] +fn p2_file_truncation_readonly() { + file_truncation_readonly(P2_FILE_TRUNCATION_READONLY_COMPONENT) +} + +fn file_truncation_readonly(component_path: &str) { + use std::path::PathBuf; + use wasmtime_wasi::{DirPerms, FilePerms}; + + let prefix = "wasi_components_truncation_readonly_ro_"; + let tempdir = tempfile::Builder::new() + .prefix(prefix) + .tempdir() + .expect("create readonly tempdir"); + const FILENAME: &str = "test.txt"; + const EXPECTED_CONTENTS: &[u8] = b"truncation test file\n"; + let mut file: PathBuf = PathBuf::from(tempdir.path()); + file.push(FILENAME); + std::fs::write(&file, EXPECTED_CONTENTS).expect("write truncation test file"); + + run(component_path, |b| { + b.preopened_dir( + tempdir.path(), + "readonly", + DirPerms::READ | DirPerms::MUTATE, + FilePerms::READ, + ) + .unwrap(); + }) + .expect("run p1_file_truncation_readonly guest"); + + let contents = std::fs::read(&file).expect("read truncation test file"); + assert_eq!(EXPECTED_CONTENTS, contents); +} diff --git a/crates/wasi/tests/all/p3/mod.rs b/crates/wasi/tests/all/p3/mod.rs index 3d6436c9d21a..10998cb1c6db 100644 --- a/crates/wasi/tests/all/p3/mod.rs +++ b/crates/wasi/tests/all/p3/mod.rs @@ -45,6 +45,11 @@ async fn run_allow_blocking_current_thread( foreach_p3!(assert_test_exists); +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn p3_big_random_buf() -> wasmtime::Result<()> { + run(P3_BIG_RANDOM_BUF_COMPONENT).await +} + #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn p3_cli() -> wasmtime::Result<()> { run(P3_CLI_COMPONENT).await diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index a77f986ec74a..3115a0c1dec4 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -29,7 +29,7 @@ wasmtime-component-macro = { workspace = true, optional = true } wasmtime-component-util = { workspace = true, optional = true } wasmtime-versioned-export-macros = { workspace = true } wasmtime-wmemcheck = { workspace = true, optional = true } -wasmtime-core = { workspace = true } +wasmtime-core = { workspace = true, features = ['serde'] } pulley-interpreter = { workspace = true } target-lexicon = { workspace = true } wasmparser = { workspace = true } @@ -249,7 +249,7 @@ trace-log = ["wasmtime-cranelift?/trace-log"] # Enables support for demangling WebAssembly function names at runtime in # errors such as backtraces. -demangle = ["wasmtime-environ/demangle", "std"] +demangle = ["wasmtime-environ/demangle"] # Enable support for generating core dumps on traps. coredump = ["dep:wasm-encoder", "runtime", "std"] @@ -379,7 +379,7 @@ reexport-wasmparser = [] # Enables instances of the traits defined in the wasm-wave crate, which # provides a human-readable text format for component values. -wave = ["dep:wasm-wave", 'component-model'] +wave = ["dep:wasm-wave", "component-model", "std"] # For platforms that Wasmtime does not have support for Wasmtime will disable # the use of virtual memory by default, for example allocating linear memories @@ -402,7 +402,13 @@ custom-sync-primitives = [] # Off-by-default support to profile the Pulley interpreter. This has a # performance hit, even when not profiling, so it's disabled by default at # compile time. -profile-pulley = ['anyhow', 'pulley', 'profiling', 'pulley-interpreter/profile'] +profile-pulley = [ + "anyhow", + "profiling", + "pulley", + "pulley-interpreter/interp", + "pulley-interpreter/profile", +] # Enables support for the Component Model Async ABI, along with `future`, # `stream`, and `error-context` types. diff --git a/crates/wasmtime/build.rs b/crates/wasmtime/build.rs index c80e4bf50546..c5c3b63850a3 100644 --- a/crates/wasmtime/build.rs +++ b/crates/wasmtime/build.rs @@ -26,7 +26,9 @@ fn main() { && (supported_os || cfg!(feature = "custom-native-signals")) && has_host_compiler_backend; let has_virtual_memory = supported_os || cfg!(feature = "custom-virtual-memory"); - let has_custom_sync = !cfg!(feature = "std") && cfg!(feature = "custom-sync-primitives"); + let has_custom_sync = !cfg!(feature = "std") + && cfg!(feature = "custom-sync-primitives") + && cfg!(feature = "runtime"); custom_cfg("has_native_signals", has_native_signals); custom_cfg("has_virtual_memory", has_virtual_memory); diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 5dd0da61020b..8338f9410bf3 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1269,6 +1269,16 @@ impl Config { self } + /// Configures whether the component model map type is enabled or not. + /// + /// This is part of the component model specification and enables the + /// `map` type in WIT and the component binary format. + #[cfg(feature = "component-model")] + pub fn wasm_component_model_map(&mut self, enable: bool) -> &mut Self { + self.wasm_features(WasmFeatures::CM_MAP, enable); + self + } + /// This corresponds to the 🔧 emoji in the component model specification. /// /// Please note that Wasmtime's support for this feature is _very_ @@ -1455,32 +1465,6 @@ impl Config { self } - /// Controls whether proof-carrying code (PCC) is used to validate - /// lowering of Wasm sandbox checks. - /// - /// Proof-carrying code carries "facts" about program values from - /// the IR all the way to machine code, and checks those facts - /// against known machine-instruction semantics. This guards - /// against bugs in instruction lowering that might create holes - /// in the Wasm sandbox. - /// - /// PCC is designed to be fast: it does not require complex - /// solvers or logic engines to verify, but only a linear pass - /// over a trail of "breadcrumbs" or facts at each intermediate - /// value. Thus, it is appropriate to enable in production. - /// - /// # Panics - /// - /// Panics if this configuration's compiler was [disabled][Config::enable_compiler]. - #[cfg(any(feature = "cranelift", feature = "winch"))] - pub fn cranelift_pcc(&mut self, enable: bool) -> &mut Self { - let val = if enable { "true" } else { "false" }; - self.compiler_config_mut() - .settings - .insert("enable_pcc".to_string(), val.to_string()); - self - } - /// Allows setting a Cranelift boolean flag or preset. This allows /// fine-tuning of Cranelift settings. /// @@ -2248,6 +2232,7 @@ impl Config { | WasmFeatures::CM_THREADING | WasmFeatures::CM_ERROR_CONTEXT | WasmFeatures::CM_GC + | WasmFeatures::CM_MAP | WasmFeatures::CM_FIXED_LENGTH_LISTS; #[allow(unused_mut, reason = "easier to avoid #[cfg]")] @@ -3004,7 +2989,7 @@ impl Config { /// The WebAssembly threads proposal, configured by [`Config::wasm_threads`] /// is on-by-default but there are enough deficiencies in Wasmtime's /// implementation and API integration that creation of a shared memory is - /// disabled by default. This cofiguration knob can be used to enable this. + /// disabled by default. This configuration knob can be used to enable this. /// /// When enabling this method be aware that wasm threads are, at this time, /// a [tier 2 @@ -3639,7 +3624,8 @@ impl PoolingAllocationConfig { } /// The maximum size, in bytes, allocated for a component instance's - /// `VMComponentContext` metadata. + /// `VMComponentContext` metadata as well as the aggregate size of this + /// component's core instances `VMContext` metadata. /// /// The [`wasmtime::component::Instance`][crate::component::Instance] type /// has a static size but its internal `VMComponentContext` is dynamically @@ -3656,10 +3642,17 @@ impl PoolingAllocationConfig { /// module will fail at runtime with an error indicating how many bytes were /// needed. /// + /// In addition to the memory in the runtime for the component itself, + /// components contain one or more core module instances. Each of these + /// require some memory in the runtime as described in + /// [`PoolingAllocationConfig::max_core_instance_size`]. The limit here + /// applies against the sum of all of these individual allocations. + /// /// The default value for this is 1MiB. /// - /// This provides an upper-bound on the total size of component - /// metadata-related allocations, along with + /// This provides an upper-bound on the total size of all component's + /// metadata-related allocations (for both the component and its embedded + /// core module instances), along with /// [`PoolingAllocationConfig::total_component_instances`]. The upper bound is /// /// ```text diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index f0f729c62e30..cb6834169513 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -489,7 +489,6 @@ information about this check\ | "enable_nan_canonicalization" | "enable_float" | "enable_verifier" - | "enable_pcc" | "regalloc_checker" | "regalloc_verbose_logs" | "regalloc_algorithm" diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index eab3c8e61733..7d6a09cd9d34 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -34,7 +34,7 @@ use object::{ read::elf::{ElfFile64, FileHeader, SectionHeader}, }; use serde_derive::{Deserialize, Serialize}; -use wasmtime_environ::{FlagValue, ObjectKind, OperatorCostStrategy, Tunables, collections, obj}; +use wasmtime_environ::{FlagValue, ObjectKind, OperatorCostStrategy, Tunables, obj}; const VERSION: u8 = 0; @@ -181,9 +181,9 @@ pub fn detect_precompiled_file(path: impl AsRef) -> Result { target: TryString, #[serde(borrow)] - shared_flags: collections::Vec<(&'a str, FlagValue<'a>)>, + shared_flags: TryVec<(&'a str, FlagValue<'a>)>, #[serde(borrow)] - isa_flags: collections::Vec<(&'a str, FlagValue<'a>)>, + isa_flags: TryVec<(&'a str, FlagValue<'a>)>, tunables: Tunables, features: u64, } diff --git a/crates/wasmtime/src/profiling_agent/pulley.rs b/crates/wasmtime/src/profiling_agent/pulley.rs index ef374aeea411..2a8da3e9b25a 100644 --- a/crates/wasmtime/src/profiling_agent/pulley.rs +++ b/crates/wasmtime/src/profiling_agent/pulley.rs @@ -35,6 +35,7 @@ use crate::ToWasmtimeResult as _; use crate::prelude::*; use crate::profiling_agent::ProfilingAgent; +#[cfg(feature = "runtime")] use crate::vm::Interpreter; use pulley_interpreter::profile::{ExecutingPc, Recorder, Samples}; use std::mem; @@ -141,6 +142,7 @@ impl ProfilingAgent for PulleyAgent { /// Registers a new interpreter coming online. Interpreters, with /// `pulley-profile` enabled, store a shadow program counter updated on each /// instruction which we can read from a different thread. + #[cfg(feature = "runtime")] fn register_interpreter(&self, interpreter: &Interpreter) { let pc = interpreter.pulley().executing_pc(); self.state diff --git a/crates/wasmtime/src/runtime.rs b/crates/wasmtime/src/runtime.rs index 3b61fffbf4f9..db49f397623e 100644 --- a/crates/wasmtime/src/runtime.rs +++ b/crates/wasmtime/src/runtime.rs @@ -26,6 +26,11 @@ // situation should be pretty rare though. #![warn(clippy::cast_possible_truncation)] +use crate::prelude::*; +use core::marker; +use core::pin::Pin; +use core::task::{Context, Poll}; + #[cfg(feature = "component-model-async")] mod bug; @@ -127,6 +132,43 @@ pub use coredump::*; #[cfg(feature = "wave")] mod wave; +/// Helper method to create a future trait object from the future `F` provided. +/// +/// This requires that the output of `F` is a result where the error can be +/// created from `OutOfMemory`. This will handle OOM when allocation of `F` on +/// the heap fails. +fn box_future<'a, F, T, E>(future: F) -> Pin> + Send + 'a>> +where + F: Future> + Send + 'a, + T: 'a, + E: From + 'a, +{ + if let Ok(future) = try_new::>(future) { + return Pin::from(future); + } + + // Use a custom guaranteed-zero-size struct to implement a future that + // returns an OOM error which satisfies the type signature of this function. + struct OomFuture(marker::PhantomData (T, F, E)>); + + impl Future for OomFuture + where + E: From, + { + type Output = Result; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Ready(Err(OutOfMemory::new(size_of::()).into())) + } + } + + // Zero-size allocations don't actually allocate memory with a `Box`, so + // it's ok to use the standard `Box::pin` here and not worry about OOM. + let future = OomFuture::(marker::PhantomData); + assert_eq!(size_of_val(&future), 0); + Box::pin(future) +} + fn _assertions_runtime() { use crate::_assert_send_and_sync; diff --git a/crates/wasmtime/src/runtime/code.rs b/crates/wasmtime/src/runtime/code.rs index 9b98a1959772..484fa9373eec 100644 --- a/crates/wasmtime/src/runtime/code.rs +++ b/crates/wasmtime/src/runtime/code.rs @@ -8,6 +8,7 @@ use crate::{code_memory::CodeMemory, type_registry::TypeCollection}; use alloc::boxed::Box; use alloc::sync::Arc; use core::ops::{Add, Range, Sub}; +use wasmtime_core::error::OutOfMemory; use wasmtime_environ::DefinedFuncIndex; use wasmtime_environ::ModuleTypes; use wasmtime_environ::StaticModuleIndex; @@ -126,16 +127,20 @@ pub struct EngineCode { } impl EngineCode { - pub fn new(mmap: Arc, signatures: TypeCollection, types: Types) -> EngineCode { + pub fn new( + mmap: Arc, + signatures: TypeCollection, + types: Types, + ) -> Result { // The corresponding unregister for this is below in `Drop for // EngineCode`. - crate::module::register_code(&mmap, mmap.raw_addr_range()); + crate::module::register_code(&mmap, mmap.raw_addr_range())?; - EngineCode { + Ok(EngineCode { original_code: mmap, signatures, types, - } + }) } #[cfg(feature = "component-model")] @@ -334,7 +339,10 @@ impl StoreCode { // this clones the whole image. let mut private_copy = engine_code.original_code.deep_clone(engine)?; private_copy.publish()?; - crate::module::register_code(&engine_code.original_code, private_copy.raw_addr_range()); + crate::module::register_code( + &engine_code.original_code, + private_copy.raw_addr_range(), + )?; StoreCodeStorage::Private(Box::new(private_copy)) } else { StoreCodeStorage::Shared(engine_code.original_code.clone()) diff --git a/crates/wasmtime/src/runtime/code_memory.rs b/crates/wasmtime/src/runtime/code_memory.rs index 3f02ced8fb4d..5c972a3547af 100644 --- a/crates/wasmtime/src/runtime/code_memory.rs +++ b/crates/wasmtime/src/runtime/code_memory.rs @@ -6,7 +6,7 @@ use crate::runtime::vm::MmapVec; use alloc::sync::Arc; use core::ops::Range; use object::read::elf::SectionTable; -use object::{LittleEndian, SectionIndex, U32Bytes}; +use object::{LittleEndian, SectionIndex, U32}; use object::{ elf::{FileHeader64, SectionHeader64}, endian::Endianness, @@ -378,7 +378,7 @@ impl CodeMemory { } let ends = self.wasm_bytecode_ends(); let count = ends.len() / core::mem::size_of::(); - let (ends, _) = object::slice_from_bytes::>(ends, count) + let (ends, _) = object::slice_from_bytes::>(ends, count) .expect("Invalid alignment of `ends` section"); let index = usize::try_from(index.as_u32()).unwrap(); Some(usize::try_from(ends[index].get(LittleEndian)).unwrap()) @@ -564,7 +564,7 @@ impl CodeMemory { pub fn text_mut(&mut self) -> &mut [u8] { assert!(!self.published); // SAFETY: we assert !published, which means we either have - // not yet applied readonly + execute permissinos, or we have + // not yet applied readonly + execute permissions, or we have // undone that and flipped back to read-write via unpublish. unsafe { &mut self.mmap.as_mut_slice()[self.text.clone()] } } diff --git a/crates/wasmtime/src/runtime/component/component.rs b/crates/wasmtime/src/runtime/component/component.rs index 462455de209d..fb0d5997c7d9 100644 --- a/crates/wasmtime/src/runtime/component/component.rs +++ b/crates/wasmtime/src/runtime/component/component.rs @@ -437,7 +437,7 @@ impl Component { // Assemble the `EngineCode` artifact which is shared by all core wasm // modules as well as the final component. let types = Arc::new(types); - let code = Arc::new(EngineCode::new(code_memory, signatures, types.into())); + let code = Arc::new(EngineCode::new(code_memory, signatures, types.into())?); // Convert all information about static core wasm modules into actual // `Module` instances by converting each `CompiledModuleInfo`, the @@ -482,7 +482,7 @@ impl Component { &self.inner.static_modules[idx] } - #[cfg(feature = "profiling")] + #[cfg(any(feature = "profiling", feature = "debug"))] pub(crate) fn static_modules(&self) -> impl Iterator { self.inner.static_modules.values() } diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs index 1740522c736d..35024a9907eb 100644 --- a/crates/wasmtime/src/runtime/component/concurrent.rs +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -142,7 +142,6 @@ impl Status { #[derive(Clone, Copy, Debug)] enum Event { None, - Cancelled, Subtask { status: Status, }, @@ -162,6 +161,7 @@ enum Event { code: ReturnCode, pending: Option<(TypeFutureTableIndex, u32)>, }, + Cancelled, } impl Event { @@ -785,14 +785,14 @@ pub(crate) fn poll_and_block( // It did not complete immediately; add it to // `ConcurrentState::futures` so it will be polled via the event loop; - // then use `GuestTask::sync_call_set` to wait for the task to + // then use `GuestThread::sync_call_set` to wait for the task to // complete, suspending the current fiber until it does so. Poll::Pending => { let state = store.concurrent_state_mut(); state.push_future(future); let caller = state.get_mut(task)?.caller; - let set = state.get_mut(caller.task)?.sync_call_set; + let set = state.get_mut(caller.thread)?.sync_call_set; Waitable::Host(task).join(state, Some(set))?; store.suspend(SuspendReason::Waiting { @@ -810,7 +810,7 @@ pub(crate) fn poll_and_block( // Retrieve and return the result. let host_state = &mut store.concurrent_state_mut().get_mut(task)?.state; - match mem::replace(host_state, HostTaskState::CalleeDone) { + match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) { HostTaskState::CalleeFinished(result) => Ok(match result.downcast() { Ok(result) => *result, Err(_) => bail_bug!("host task finished with wrong type of result"), @@ -1464,7 +1464,6 @@ impl StoreOpaque { debug_assert_eq!(instance, guest_caller); } let task = GuestTask::new( - state, Box::new(move |_, _| bail_bug!("cannot lower params in sync call")), LiftResult { lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")), @@ -1487,7 +1486,7 @@ impl StoreOpaque { )?; let guest_task = state.push(task)?; - let new_thread = GuestThread::new_implicit(guest_task); + let new_thread = GuestThread::new_implicit(state, guest_task)?; let guest_thread = state.push(new_thread)?; Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table( guest_thread, @@ -1879,7 +1878,7 @@ impl StoreOpaque { let state = self.concurrent_state_mut(); let caller = state.current_guest_thread()?; let old_set = waitable.common(state)?.set; - let set = state.get_mut(caller.task)?.sync_call_set; + let set = state.get_mut(caller.thread)?.sync_call_set; waitable.join(state, Some(set))?; self.suspend(SuspendReason::Waiting { set, @@ -2075,14 +2074,24 @@ impl Instance { guest_thread: QualifiedThreadId, runtime_instance: RuntimeComponentInstanceIndex, ) -> Result<()> { - let guest_id = match store - .concurrent_state_mut() - .get_mut(guest_thread.thread)? - .instance_rep - { + let state = store.concurrent_state_mut(); + let thread_data = state.get_mut(guest_thread.thread)?; + let guest_id = match thread_data.instance_rep { Some(id) => id, None => bail_bug!("thread must have instance_rep set by now"), }; + let sync_call_set = thread_data.sync_call_set; + + // Clean up any pending subtasks in the sync_call_set + for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) { + if let Some(Event::Subtask { + status: Status::Returned | Status::ReturnCancelled, + }) = waitable.common(state)?.event + { + waitable.delete_from(state)?; + } + } + store .instance_state(RuntimeInstance { instance: self.id().instance(), @@ -2092,6 +2101,7 @@ impl Instance { .guest_thread_remove(guest_id)?; store.concurrent_state_mut().delete(guest_thread.thread)?; + store.concurrent_state_mut().delete(sync_call_set)?; let task = store.concurrent_state_mut().get_mut(guest_thread.task)?; task.threads.remove(&guest_thread.thread); Ok(()) @@ -2437,7 +2447,6 @@ impl Instance { ); let new_task = GuestTask::new( - state, Box::new(move |store, dst| { let mut store = token.as_context_mut(store); assert!(dst.len() <= MAX_FLAT_PARAMS); @@ -2498,6 +2507,14 @@ impl Instance { if let ResultInfo::Heap { results } = &result_info { my_src.push(ValRaw::u32(*results)); } + + // Execute the `return_` hook, generated by Wasmtime's FACT + // compiler, in the context of the old thread. The old + // thread, this thread's caller, may have `realloc` + // callbacks invoked for example and those need the correct + // context set for the current thread. + let prev = store.0.set_thread(old_thread)?; + // SAFETY: `return_` is a valid `*mut VMFuncRef` from // `wasmtime-cranelift`-generated fused adapter code. Based // on how it was constructed (see @@ -2511,6 +2528,11 @@ impl Instance { my_src.as_mut_slice().into(), )?; } + + // Restore the previous current thread after the + // lifting/lowering has returned. + store.0.set_thread(prev)?; + let state = store.0.concurrent_state_mut(); let thread = state.current_guest_thread()?; if sync_caller { @@ -2542,7 +2564,7 @@ impl Instance { )?; let guest_task = state.push(new_task)?; - let new_thread = GuestThread::new_implicit(guest_task); + let new_thread = GuestThread::new_implicit(state, guest_task)?; let guest_thread = state.push(new_thread)?; state.get_mut(guest_task)?.threads.insert(guest_thread); @@ -2660,11 +2682,11 @@ impl Instance { let state = store.0.concurrent_state_mut(); - // Use the caller's `GuestTask::sync_call_set` to register interest in + // Use the caller's `GuestThread::sync_call_set` to register interest in // the subtask... let guest_waitable = Waitable::Guest(guest_thread.task); let old_set = guest_waitable.common(state)?.set; - let set = state.get_mut(caller.task)?.sync_call_set; + let set = state.get_mut(caller.thread)?.sync_call_set; guest_waitable.join(state, Some(set))?; // ... and suspend this fiber temporarily while we wait for it to start. @@ -2841,7 +2863,14 @@ impl Instance { lower(store.as_context_mut(), result)?; let state = store.0.concurrent_state_mut(); - state.get_mut(task)?.state = HostTaskState::CalleeDone; + match &mut state.get_mut(task)?.state { + // The task is already flagged as finished because it was + // cancelled. No need to transition further. + HostTaskState::CalleeDone { .. } => {} + + // Otherwise transition this task to the done state. + other => *other = HostTaskState::CalleeDone { cancelled: false }, + } Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?; store.0.set_thread(old)?; @@ -3092,7 +3121,7 @@ impl Instance { let task = concurrent_state.get_mut(id)?; match &task.state { HostTaskState::CalleeRunning(_) => bail!(Trap::SubtaskDropNotResolved), - HostTaskState::CalleeDone => {} + HostTaskState::CalleeDone { .. } => {} HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => { bail_bug!("invalid state for callee in `subtask.drop`") } @@ -3281,7 +3310,7 @@ impl Instance { let current_thread = state.current_guest_thread()?; let parent_task = current_thread.task; - let new_thread = GuestThread::new_explicit(parent_task, start_func); + let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?; let thread_id = state.push(new_thread)?; state.get_mut(parent_task)?.threads.insert(thread_id); @@ -3571,15 +3600,28 @@ impl Instance { let needs_block; if let Waitable::Host(host_task) = waitable { let state = &mut concurrent_state.get_mut(host_task)?.state; - match mem::replace(state, HostTaskState::CalleeDone) { + match mem::replace(state, HostTaskState::CalleeDone { cancelled: true }) { // If the callee is still running, signal an abort is requested. - // Then fall through to determine what to do next. - HostTaskState::CalleeRunning(handle) => handle.abort(), + // + // After cancelling this falls through to block waiting for the + // host task to actually finish assuming that `async_` is false. + // This blocking behavior resolves the race of `handle.abort()` + // with the task actually getting cancelled or finishing. + HostTaskState::CalleeRunning(handle) => { + handle.abort(); + needs_block = true; + } // Cancellation was already requested, so fail as the task can't // be cancelled twice. - HostTaskState::CalleeDone => { - bail!(Trap::SubtaskCancelAfterTerminal); + HostTaskState::CalleeDone { cancelled } => { + if cancelled { + bail!(Trap::SubtaskCancelAfterTerminal); + } else { + // The callee is already done so there's no need to + // block further for an event. + needs_block = false; + } } // These states should not be possible for a subtask that's @@ -3588,12 +3630,6 @@ impl Instance { bail_bug!("invalid states for host callee") } } - - // Cancelling host tasks always needs to block on them to await the - // result of the completion set up in `first_poll`. This'll resolve - // the race of `handle.abort()` above to see if it actually - // cancelled something or if the future ended up finishing. - needs_block = true; } else { let caller = concurrent_state.current_guest_thread()?; let guest_task = TableId::::new(rep); @@ -4232,7 +4268,7 @@ enum HostTaskState { /// Terminal state for host tasks meaning that the task was cancelled or the /// result was taken. - CalleeDone, + CalleeDone { cancelled: bool }, } impl HostTask { @@ -4289,7 +4325,7 @@ struct LiftResult { /// This exists to minimize table lookups and the necessity to pass stores around mutably /// for the common case of identifying the task to which a thread belongs. #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -struct QualifiedThreadId { +pub(crate) struct QualifiedThreadId { task: TableId, thread: TableId, } @@ -4339,6 +4375,8 @@ pub struct GuestThread { /// The index of this thread in the component instance's handle table. /// This must always be `Some` after initialization. instance_rep: Option, + /// Scratch waitable set used to watch subtasks during synchronous calls. + sync_call_set: TableId, } impl GuestThread { @@ -4355,29 +4393,34 @@ impl GuestThread { Ok(TableId::new(rep)) } - fn new_implicit(parent_task: TableId) -> Self { - Self { + fn new_implicit(state: &mut ConcurrentState, parent_task: TableId) -> Result { + let sync_call_set = state.push(WaitableSet::default())?; + Ok(Self { context: [0; 2], parent_task, wake_on_cancel: None, state: GuestThreadState::NotStartedImplicit, instance_rep: None, - } + sync_call_set, + }) } fn new_explicit( + state: &mut ConcurrentState, parent_task: TableId, start_func: Box< dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync, >, - ) -> Self { - Self { + ) -> Result { + let sync_call_set = state.push(WaitableSet::default())?; + Ok(Self { context: [0; 2], parent_task, wake_on_cancel: None, state: GuestThreadState::NotStartedExplicit(start_func), instance_rep: None, - } + sync_call_set, + }) } } @@ -4442,8 +4485,6 @@ pub(crate) struct GuestTask { /// Whether or not we've sent a `Status::Starting` event to any current or /// future waiters for this waitable. starting_sent: bool, - /// Scratch waitable set used to watch subtasks during synchronous calls. - sync_call_set: TableId, /// The runtime instance to which the exported function for this guest task /// belongs. /// @@ -4501,7 +4542,6 @@ impl GuestTask { } fn new( - state: &mut ConcurrentState, lower_params: RawLower, lift_result: LiftResult, caller: Caller, @@ -4509,7 +4549,6 @@ impl GuestTask { instance: RuntimeInstance, async_function: bool, ) -> Result { - let sync_call_set = state.push(WaitableSet::default())?; let host_future_state = match &caller { Caller::Guest { .. } => HostFutureState::NotApplicable, Caller::Host { @@ -4534,7 +4573,6 @@ impl GuestTask { sync_result: SyncResult::NotProduced, cancel_sent: false, starting_sent: false, - sync_call_set, instance, event: None, exited: false, @@ -4544,24 +4582,9 @@ impl GuestTask { }) } - /// Dispose of this guest task, reparenting any pending subtasks to the - /// caller. - fn dispose(self, state: &mut ConcurrentState) -> Result<()> { - // If there are not-yet-delivered completion events for subtasks in - // `self.sync_call_set`, recursively dispose of those subtasks as well. - for waitable in mem::take(&mut state.get_mut(self.sync_call_set)?.ready) { - if let Some(Event::Subtask { - status: Status::Returned | Status::ReturnCancelled, - }) = waitable.common(state)?.event - { - waitable.delete_from(state)?; - } - } - + /// Dispose of this guest task. + fn dispose(self, _state: &mut ConcurrentState) -> Result<()> { assert!(self.threads.is_empty()); - - state.delete(self.sync_call_set)?; - Ok(()) } } @@ -4797,7 +4820,7 @@ impl ConcurrentInstanceState { } #[derive(Debug, Copy, Clone)] -enum CurrentThread { +pub(crate) enum CurrentThread { Guest(QualifiedThreadId), Host(TableId), None, @@ -5127,27 +5150,27 @@ impl ConcurrentState { /// /// The `task` is bit-packed as returned by `current_call_context_scope_id` /// below. - pub fn call_context(&mut self, task: u32) -> &mut CallContext { + pub fn call_context(&mut self, task: u32) -> Result<&mut CallContext> { let (task, is_host) = (task >> 1, task & 1 == 1); if is_host { let task: TableId = TableId::new(task); - &mut self.get_mut(task).unwrap().call_context + Ok(&mut self.get_mut(task)?.call_context) } else { let task: TableId = TableId::new(task); - &mut self.get_mut(task).unwrap().call_context + Ok(&mut self.get_mut(task)?.call_context) } } /// Used by `ResourceTables` to record the scope of a borrow to get undone /// in the future. - pub fn current_call_context_scope_id(&self) -> u32 { + pub fn current_call_context_scope_id(&self) -> Result { let (bits, is_host) = match self.current_thread { CurrentThread::Guest(id) => (id.task.rep(), false), CurrentThread::Host(id) => (id.rep(), true), - CurrentThread::None => unreachable!(), + CurrentThread::None => bail_bug!("current thread is not set"), }; assert_eq!((bits << 1) >> 1, bits); - (bits << 1) | u32::from(is_host) + Ok((bits << 1) | u32::from(is_host)) } fn current_guest_thread(&self) -> Result { @@ -5170,6 +5193,10 @@ impl ConcurrentState { None => bail_bug!("futures field of concurrent state is currently taken"), } } + + pub(crate) fn table(&mut self) -> &mut ResourceTable { + self.table.get_mut() + } } /// Provide a type hint to compiler about the shape of a parameter lower @@ -5346,7 +5373,6 @@ pub(crate) fn prepare_call( }; let caller = state.current_thread; let task = GuestTask::new( - state, Box::new(for_any_lower(move |store, params| { lower_params(handle, token.as_context_mut(store), params) })), @@ -5378,7 +5404,8 @@ pub(crate) fn prepare_call( )?; let task = state.push(task)?; - let thread = state.push(GuestThread::new_implicit(task))?; + let new_thread = GuestThread::new_implicit(state, task)?; + let thread = state.push(new_thread)?; state.get_mut(task)?.threads.insert(thread); if !store.0.may_enter(instance)? { diff --git a/crates/wasmtime/src/runtime/component/concurrent/future_stream_any.rs b/crates/wasmtime/src/runtime/component/concurrent/future_stream_any.rs index a6f6a24258d3..0bdab44086a2 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/future_stream_any.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/future_stream_any.rs @@ -304,7 +304,7 @@ impl StreamAny { /// /// Panics if the `store` does not own this stream. pub fn close(&mut self, mut store: impl AsContextMut) -> Result<()> { - futures_and_streams::future_close(store.as_context_mut().0, &mut self.id) + futures_and_streams::stream_close(store.as_context_mut().0, &mut self.id) } } diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs index 81e84275f904..e7bd850d4370 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -1,6 +1,6 @@ use super::table::{TableDebug, TableId}; use super::{Event, GlobalErrorContextRefCount, Waitable, WaitableCommon}; -use crate::component::concurrent::{ConcurrentState, WorkItem, tls}; +use crate::component::concurrent::{ConcurrentState, QualifiedThreadId, WorkItem, tls}; use crate::component::func::{self, LiftContext, LowerContext}; use crate::component::matching::InstanceType; use crate::component::types; @@ -14,7 +14,7 @@ use crate::vm::component::{ComponentInstance, HandleTable, TransmitLocalState}; use crate::vm::{AlwaysMut, VMStore}; use crate::{AsContext, AsContextMut, StoreContextMut, ValRaw}; use crate::{ - Error, Result, bail, bail_bug, ensure, + Error, Result, Trap, bail, bail_bug, ensure, error::{Context as _, format_err}, }; use buffers::{Extender, SliceBuffer, UntypedWriteBuffer}; @@ -116,14 +116,20 @@ impl TransmitIndex { TransmitIndex::Future(_) => TransmitKind::Future, } } -} -/// Retrieve the payload type of the specified stream or future, or `None` if it -/// has no payload type. -fn payload(ty: TransmitIndex, types: &ComponentTypes) -> Option { - match ty { - TransmitIndex::Future(ty) => types[types[ty].ty].payload, - TransmitIndex::Stream(ty) => types[types[ty].ty].payload, + /// Retrieve the payload type of the specified stream or future, or `None` + /// if it has no payload type. + fn payload<'a>(&self, types: &'a ComponentTypes) -> Option<&'a InterfaceType> { + match self { + TransmitIndex::Stream(i) => { + let ty = types[*i].ty; + types[ty].payload.as_ref() + } + TransmitIndex::Future(i) => { + let ty = types[*i].ty; + types[ty].payload.as_ref() + } + } } } @@ -143,6 +149,7 @@ fn get_mut_by_index_from( fn lower, U: 'static>( mut store: StoreContextMut, instance: Instance, + caller_thread: QualifiedThreadId, options: OptionsIndex, ty: TransmitIndex, address: usize, @@ -151,11 +158,21 @@ fn lower, U: 'static>( ) -> Result<()> { let count = buffer.remaining().len().min(count); - let lower = &mut if T::MAY_REQUIRE_REALLOC { - LowerContext::new + // If lowering may call realloc in the guest, then the guest may need + // to access its thread context, so we need to set the current thread before lowering + // and restore the old one afterward. + let (lower, old_thread) = if T::MAY_REQUIRE_REALLOC { + let old_thread = store.0.set_thread(caller_thread)?; + ( + &mut LowerContext::new(store.as_context_mut(), options, instance), + Some(old_thread), + ) } else { - LowerContext::new_without_realloc - }(store.as_context_mut(), options, instance); + ( + &mut LowerContext::new_without_realloc(store.as_context_mut(), options, instance), + None, + ) + }; if address % usize::try_from(T::ALIGN32)? != 0 { bail!("read pointer not aligned"); @@ -166,8 +183,12 @@ fn lower, U: 'static>( .and_then(|b| b.get_mut(..T::SIZE32 * count)) .ok_or_else(|| crate::format_err!("read pointer out of bounds of memory"))?; - if let Some(ty) = payload(ty, lower.types) { - T::linear_store_list_to_memory(lower, ty, address, &buffer.remaining()[..count])?; + if let Some(ty) = ty.payload(lower.types) { + T::linear_store_list_to_memory(lower, *ty, address, &buffer.remaining()[..count])?; + } + + if let Some(old_thread) = old_thread { + store.0.set_thread(old_thread)?; } buffer.skip(count); @@ -717,24 +738,13 @@ impl StreamProducer for bytes::Bytes { type Buffer = Cursor; fn poll_produce<'a>( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, _: &mut Context<'_>, - mut store: StoreContextMut<'a, D>, + _store: StoreContextMut<'a, D>, mut dst: Destination<'a, Self::Item, Self::Buffer>, _: bool, ) -> Poll> { - let cap = dst.remaining(&mut store); - let Some(cap) = cap.and_then(core::num::NonZeroUsize::new) else { - // on 0-length or host reads, buffer the bytes - dst.set_buffer(Cursor::new(mem::take(self.get_mut()))); - return Poll::Ready(Ok(StreamResult::Dropped)); - }; - let cap = cap.into(); - // data does not fit in destination, fill it and buffer the rest - dst.set_buffer(Cursor::new(self.split_off(cap))); - let mut dst = dst.as_direct(store, cap); - dst.remaining().copy_from_slice(&self); - dst.mark_written(cap); + dst.set_buffer(Cursor::new(mem::take(self.get_mut()))); Poll::Ready(Ok(StreamResult::Dropped)) } } @@ -745,24 +755,13 @@ impl StreamProducer for bytes::BytesMut { type Buffer = Cursor; fn poll_produce<'a>( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, _: &mut Context<'_>, - mut store: StoreContextMut<'a, D>, + _store: StoreContextMut<'a, D>, mut dst: Destination<'a, Self::Item, Self::Buffer>, _: bool, ) -> Poll> { - let cap = dst.remaining(&mut store); - let Some(cap) = cap.and_then(core::num::NonZeroUsize::new) else { - // on 0-length or host reads, buffer the bytes - dst.set_buffer(Cursor::new(mem::take(self.get_mut()))); - return Poll::Ready(Ok(StreamResult::Dropped)); - }; - let cap = cap.into(); - // data does not fit in destination, fill it and buffer the rest - dst.set_buffer(Cursor::new(self.split_off(cap))); - let mut dst = dst.as_direct(store, cap); - dst.remaining().copy_from_slice(&self); - dst.mark_written(cap); + dst.set_buffer(Cursor::new(mem::take(self.get_mut()))); Poll::Ready(Ok(StreamResult::Dropped)) } } @@ -808,15 +807,15 @@ impl<'a, T> Source<'a, T> { .. } = &transmit.write else { - bail_bug!("expected WriteState::HostReady"); + bail_bug!("expected WriteState::GuestReady"); }; let cx = &mut LiftContext::new(store.0.store_opaque_mut(), options, instance); - let ty = payload(ty, cx.types); + let ty = ty.payload(cx.types); let old_remaining = buffer.remaining_capacity(); lift::( cx, - ty, + ty.copied(), buffer, address + (T::SIZE32 * guest_offset), count - guest_offset, @@ -2103,7 +2102,7 @@ struct TransmitState { write: WriteState, /// See `ReadState` read: ReadState, - /// Whether futher values may be transmitted via this stream or future. + /// Whether further values may be transmitted via this stream or future. done: bool, /// The original creator of this stream, used for type-checking with /// `{Future,Stream}Any`. @@ -2196,7 +2195,8 @@ enum ReadState { /// The read end is owned by a guest task and a read is pending. GuestReady { ty: TransmitIndex, - caller: RuntimeComponentInstanceIndex, + caller_instance: RuntimeComponentInstanceIndex, + caller_thread: QualifiedThreadId, flat_abi: Option, instance: Instance, options: OptionsIndex, @@ -2768,7 +2768,7 @@ impl StoreContextMut<'_, T> { &WriteState::GuestReady { count, .. } => count, WriteState::HostReady { .. } => match host_buffer_remaining_before { Some(n) => n, - None => bail_bug!("host_buffer_remaining_before shoudl be set"), + None => bail_bug!("host_buffer_remaining_before should be set"), }, _ => bail_bug!("invalid write state"), }, @@ -2930,7 +2930,8 @@ async fn write { let guest_offset = match guest_offset { Some(i) => i, @@ -2952,6 +2953,7 @@ async fn write( store.as_context_mut(), instance, + caller_thread, options, ty, address + (T::SIZE32 * guest_offset), @@ -3008,7 +3010,8 @@ async fn write( self, - mut store: StoreContextMut, + store: StoreContextMut, flat_abi: Option, - write_caller: RuntimeComponentInstanceIndex, + write_caller_instance: RuntimeComponentInstanceIndex, write_ty: TransmitIndex, write_options: OptionsIndex, write_address: usize, - read_caller: RuntimeComponentInstanceIndex, + read_caller_instance: RuntimeComponentInstanceIndex, + read_caller_thread: QualifiedThreadId, read_ty: TransmitIndex, read_options: OptionsIndex, read_address: usize, count: usize, rep: u32, ) -> Result<()> { - let types = self.id().get(store.0).component().types(); + let (component, mut store) = self.component_and_store_mut(store.0); + let types = component.types(); + + // Validate `write_ty` w.r.t. `write_address` to ensure it's properly + // aligned and in-bounds. + let write_payload_ty = write_ty.payload(types); + let write_abi = match write_payload_ty { + Some(ty) => types.canonical_abi(ty), + None => &CanonicalAbiInfo::ZERO, + }; + let write_length_in_bytes = match flat_abi { + Some(abi) => usize::try_from(abi.size)? * count, + None => usize::try_from(write_abi.size32)? * count, + }; + if write_length_in_bytes > 0 { + if write_address % usize::try_from(write_abi.align32)? != 0 { + bail!("write pointer not aligned"); + } + self.options_memory(store, write_options) + .get(write_address..) + .and_then(|b| b.get(..write_length_in_bytes)) + .ok_or_else(|| crate::format_err!("write pointer out of bounds"))?; + } + + let read_payload_ty = read_ty.payload(types); + let read_abi = match read_payload_ty { + Some(ty) => types.canonical_abi(ty), + None => &CanonicalAbiInfo::ZERO, + }; + let read_length_in_bytes = match flat_abi { + Some(abi) => usize::try_from(abi.size)? * count, + None => usize::try_from(read_abi.size32)? * count, + }; + if read_length_in_bytes > 0 { + if read_address % usize::try_from(read_abi.align32)? != 0 { + bail!("read pointer not aligned"); + } + self.options_memory(store, read_options) + .get(read_address..) + .and_then(|b| b.get(..read_length_in_bytes)) + .ok_or_else(|| crate::format_err!("read pointer out of bounds"))?; + } + + if write_caller_instance == read_caller_instance + && !allow_intra_component_read_write(write_payload_ty) + { + bail!( + "cannot read from and write to intra-component future/stream with non-numeric payload" + ) + } + match (write_ty, read_ty) { - (TransmitIndex::Future(write_ty), TransmitIndex::Future(read_ty)) => { + (TransmitIndex::Future(_), TransmitIndex::Future(_)) => { if count != 1 { bail_bug!("futures can only send 1 item"); } - let payload = types[types[write_ty].ty].payload; - - if write_caller == read_caller && !allow_intra_component_read_write(payload) { - bail!( - "cannot read from and write to intra-component future with non-numeric payload" - ) - } - - let val = payload + let val = write_payload_ty .map(|ty| { - let lift = - &mut LiftContext::new(store.0.store_opaque_mut(), write_options, self); - - let abi = lift.types.canonical_abi(&ty); - // FIXME: needs to read an i64 for memory64 - if write_address % usize::try_from(abi.align32)? != 0 { - bail!("write pointer not aligned"); - } - - let bytes = lift - .memory() - .get(write_address..) - .and_then(|b| b.get(..usize::try_from(abi.size32).ok()?)) - .ok_or_else(|| { - crate::format_err!("write pointer out of bounds of memory") - })?; - - Val::load(lift, ty, bytes) + let lift = &mut LiftContext::new(store, write_options, self); + let bytes = &lift.memory()[write_address..][..write_length_in_bytes]; + Val::load(lift, *ty, bytes) }) .transpose()?; if let Some(val) = val { + // Serializing the value may require calling the guest's realloc function, so we + // set the guest's thread context in case realloc requires it, and restore the original + // thread context after the copy is complete. + let old_thread = store.set_thread(read_caller_thread)?; let lower = &mut LowerContext::new(store.as_context_mut(), read_options, self); - let types = lower.types; - let ty = match types[types[read_ty].ty].payload { - Some(ty) => ty, - None => bail_bug!("expected payload type to be present"), - }; let ptr = func::validate_inbounds_dynamic( - types.canonical_abi(&ty), + read_abi, lower.as_slice_mut(), &ValRaw::u32(read_address.try_into()?), )?; - val.store(lower, ty, ptr)?; + let ty = match read_payload_ty { + Some(ty) => ty, + None => bail_bug!("expected read payload type to be present"), + }; + val.store(lower, *ty, ptr)?; + store.set_thread(old_thread)?; } } - (TransmitIndex::Stream(write_ty), TransmitIndex::Stream(read_ty)) => { - if write_caller == read_caller - && !allow_intra_component_read_write(types[types[write_ty].ty].payload) - { - bail!( - "cannot read from and write to intra-component stream with non-numeric payload" - ) + (TransmitIndex::Stream(_), TransmitIndex::Stream(_)) => { + if write_length_in_bytes == 0 { + return Ok(()); } - - if let Some(flat_abi) = flat_abi { + let write_payload_ty = match write_payload_ty { + Some(ty) => ty, + None => bail_bug!("expected write payload type to be present"), + }; + let read_payload_ty = match read_payload_ty { + Some(ty) => ty, + None => bail_bug!("expected read payload type to be present"), + }; + if flat_abi.is_some() { // Fast path memcpy for "flat" (i.e. no pointers or handles) payloads: - let length_in_bytes = usize::try_from(flat_abi.size)? * count; - if length_in_bytes > 0 { - if write_address % usize::try_from(flat_abi.align)? != 0 { - bail!("write pointer not aligned"); - } - if read_address % usize::try_from(flat_abi.align)? != 0 { - bail!("read pointer not aligned"); - } + let store_opaque = store.store_opaque_mut(); - let store_opaque = store.0.store_opaque_mut(); + assert_eq!(read_length_in_bytes, write_length_in_bytes); - { - let src = self - .options_memory(store_opaque, write_options) - .get(write_address..) - .and_then(|b| b.get(..length_in_bytes)) - .ok_or_else(|| { - crate::format_err!("write pointer out of bounds of memory") - })? - .as_ptr(); - let dst = self - .options_memory_mut(store_opaque, read_options) - .get_mut(read_address..) - .and_then(|b| b.get_mut(..length_in_bytes)) - .ok_or_else(|| { - crate::format_err!("read pointer out of bounds of memory") - })? - .as_mut_ptr(); - // SAFETY: Both `src` and `dst` have been validated - // above. - unsafe { - if write_caller == read_caller { - // If the same instance owns both ends of - // the stream, the source and destination - // buffers might overlap. - src.copy_to(dst, length_in_bytes) - } else { - // Since the read and write ends of the - // stream are owned by distinct instances, - // the buffers cannot possibly belong to the - // same memory and thus cannot overlap. - src.copy_to_nonoverlapping(dst, length_in_bytes) - } - } + if write_caller_instance == read_caller_instance { + let memory = self.options_memory_mut(store_opaque, read_options); + memory.copy_within( + write_address..write_address + write_length_in_bytes, + read_address, + ); + } else { + let src = self.options_memory(store_opaque, write_options)[write_address..] + [..write_length_in_bytes] + .as_ptr(); + let dst = self.options_memory_mut(store_opaque, read_options) + [read_address..][..read_length_in_bytes] + .as_mut_ptr(); + + // SAFETY: Both `src` and `dst` have been validated + // above to be valid pointers as they're derived from + // slices that have the desired length with the desired + // read/write permission. The `unsafe` bit here is that + // the memories are disjoint (present in different + // instances) and there's no easy way to borrow both + // simultaneously from the store. Different instances + // are guaranteed to be disjoint, however, so the + // `unsafe` here should be ok. + unsafe { + src.copy_to_nonoverlapping(dst, write_length_in_bytes); } } } else { - let store_opaque = store.0.store_opaque_mut(); + let store_opaque = store.store_opaque_mut(); let lift = &mut LiftContext::new(store_opaque, write_options, self); - let ty = match lift.types[lift.types[write_ty].ty].payload { - Some(ty) => ty, - None => bail_bug!("expected payload type to be present"), - }; - let abi = lift.types.canonical_abi(&ty); - let size = usize::try_from(abi.size32)?; - if write_address % usize::try_from(abi.align32)? != 0 { - bail!("write pointer not aligned"); - } - let bytes = lift - .memory() - .get(write_address..) - .and_then(|b| b.get(..size * count)) - .ok_or_else(|| { - crate::format_err!("write pointer out of bounds of memory") - })?; + let bytes = &lift.memory()[write_address..][..write_length_in_bytes]; + lift.consume_fuel_array(count, size_of::())?; let values = (0..count) - .map(|index| Val::load(lift, ty, &bytes[(index * size)..][..size])) + .map(|index| { + let size = usize::try_from(write_abi.size32)?; + Val::load(lift, *write_payload_ty, &bytes[(index * size)..][..size]) + }) .collect::>>()?; let id = TableId::::new(rep); log::trace!("copy values {values:?} for {id:?}"); + // Serializing the value may require calling the guest's realloc function, so we + // set the guest's thread context in case realloc requires it, and restore the original + // thread context after the copy is complete. + let old_thread = store.set_thread(read_caller_thread)?; let lower = &mut LowerContext::new(store.as_context_mut(), read_options, self); - let ty = match lower.types[lower.types[read_ty].ty].payload { - Some(ty) => ty, - None => bail_bug!("expected payload type to be present"), - }; - let abi = lower.types.canonical_abi(&ty); - if read_address % usize::try_from(abi.align32)? != 0 { - bail!("read pointer not aligned"); - } - let size = usize::try_from(abi.size32)?; - lower - .as_slice_mut() - .get_mut(read_address..) - .and_then(|b| b.get_mut(..size * count)) - .ok_or_else(|| { - crate::format_err!("read pointer out of bounds of memory") - })?; let mut ptr = read_address; for value in values { - value.store(lower, ty, ptr)?; - ptr += size + value.store(lower, *read_payload_ty, ptr)?; + ptr += usize::try_from(read_abi.size32)?; } + store.set_thread(old_thread)?; } } _ => bail_bug!("mismatched transmit types in copy"), @@ -3412,10 +3413,7 @@ impl Instance { self.check_bounds(store.0, options, ty, address, count)?; let (rep, state) = self.id().get_mut(store.0).get_mut_by_index(ty, handle)?; let TransmitLocalState::Write { done } = *state else { - bail!( - "invalid handle {handle}; expected `Write`; got {:?}", - *state - ); + bail!(Trap::ConcurrentFutureStreamOp); }; if done { @@ -3469,7 +3467,8 @@ impl Instance { count: read_count, handle: read_handle, instance: read_instance, - caller: read_caller, + caller_instance: read_caller_instance, + caller_thread: read_caller_thread, } => { if flat_abi != read_flat_abi { bail_bug!("expected flat ABI calculations to be the same"); @@ -3515,7 +3514,8 @@ impl Instance { ty, options, address, - read_caller, + read_caller_instance, + read_caller_thread, read_ty, read_options, read_address, @@ -3525,8 +3525,8 @@ impl Instance { let instance = self.id().get_mut(store.0); let types = instance.component().types(); - let item_size = match payload(ty, types) { - Some(ty) => usize::try_from(types.canonical_abi(&ty).size32)?, + let item_size = match ty.payload(types) { + Some(ty) => usize::try_from(types.canonical_abi(ty).size32)?, None => 0, }; let concurrent_state = store.0.concurrent_state_mut(); @@ -3557,7 +3557,8 @@ impl Instance { count: read_count - count, handle: read_handle, instance: read_instance, - caller: read_caller, + caller_instance: read_caller_instance, + caller_thread: read_caller_thread, }; } @@ -3634,7 +3635,7 @@ impl Instance { pub(super) fn guest_read( self, mut store: StoreContextMut, - caller: RuntimeComponentInstanceIndex, + caller_instance: RuntimeComponentInstanceIndex, ty: TransmitIndex, options: OptionsIndex, flat_abi: Option, @@ -3654,7 +3655,7 @@ impl Instance { self.check_bounds(store.0, options, ty, address, count)?; let (rep, state) = self.id().get_mut(store.0).get_mut_by_index(ty, handle)?; let TransmitLocalState::Read { done } = *state else { - bail_bug!("invalid handle {handle}; expected `Read`; got {:?}", *state); + bail!(Trap::ConcurrentFutureStreamOp); }; if done { @@ -3664,6 +3665,7 @@ impl Instance { *state = TransmitLocalState::Busy; let transmit_handle = TableId::::new(rep); let concurrent_state = store.0.concurrent_state_mut(); + let caller_thread = concurrent_state.current_guest_thread()?; let transmit_id = concurrent_state.get_mut(transmit_handle)?.state; let transmit = concurrent_state.get_mut(transmit_id)?; log::trace!( @@ -3694,7 +3696,8 @@ impl Instance { count, handle, instance: self, - caller, + caller_instance, + caller_thread, }; Ok::<_, crate::Error>(()) }; @@ -3737,7 +3740,8 @@ impl Instance { write_ty, write_options, write_address, - caller, + caller_instance, + caller_thread, ty, options, address, @@ -3747,8 +3751,8 @@ impl Instance { let instance = self.id().get_mut(store.0); let types = instance.component().types(); - let item_size = match payload(ty, types) { - Some(ty) => usize::try_from(types.canonical_abi(&ty).size32)?, + let item_size = match ty.payload(types) { + Some(ty) => usize::try_from(types.canonical_abi(ty).size32)?, None => 0, }; let concurrent_state = store.0.concurrent_state_mut(); @@ -3887,13 +3891,13 @@ impl Instance { transmit.read, transmit.write ); + let waitable = Waitable::Transmit(transmit.write_handle); - let code = if let Some(event) = - Waitable::Transmit(transmit.write_handle).take_event(state)? - { + let code = if let Some(event) = waitable.take_event(state)? { let (Event::FutureWrite { code, .. } | Event::StreamWrite { code, .. }) = event else { bail_bug!("expected either a stream or future write event") }; + waitable.on_delivery(store, self, event)?; match (code, event) { (ReturnCode::Completed(count), Event::StreamWrite { .. }) => { ReturnCode::Cancelled(count) @@ -3975,12 +3979,12 @@ impl Instance { transmit.write ); - let code = if let Some(event) = - Waitable::Transmit(transmit.read_handle).take_event(state)? - { + let waitable = Waitable::Transmit(transmit.read_handle); + let code = if let Some(event) = waitable.take_event(state)? { let (Event::FutureRead { code, .. } | Event::StreamRead { code, .. }) = event else { bail_bug!("expected either a stream or future read event") }; + waitable.on_delivery(store, self, event)?; match (code, event) { (ReturnCode::Completed(count), Event::StreamRead { .. }) => { ReturnCode::Cancelled(count) @@ -4201,11 +4205,15 @@ impl Instance { let lower_cx = &mut LowerContext::new(store, options, self); let debug_msg_address = usize::try_from(debug_msg_address)?; - // Lower the string into the component's memory + // Lower the string into the component's memory. + // + // Note that the "8" here is the size of a WIT `string` in linear + // memory, the ptr+length. This'll need to be updated when `memory64` + // comes along. (FIXME(#4311)) let offset = lower_cx .as_slice_mut() .get(debug_msg_address..) - .and_then(|b| b.get(..debug_msg.bytes().len())) + .and_then(|b| b.get(..8)) .map(|_| debug_msg_address) .ok_or_else(|| crate::format_err!("invalid debug message pointer: out of bounds"))?; debug_msg @@ -4659,31 +4667,21 @@ impl Waitable { instance: Instance, event: Event, ) -> Result<()> { - match event { + let instance = instance.id().get_mut(store); + let (rep, state, code) = match event { Event::FutureRead { pending: Some((ty, handle)), - .. + code, } | Event::FutureWrite { pending: Some((ty, handle)), - .. + code, } => { - let instance = instance.id().get_mut(store); let runtime_instance = instance.component().types()[ty].instance; let (rep, state) = instance.instance_states().0[runtime_instance] .handle_table() .future_rep(ty, handle)?; - if rep != self.rep() { - bail_bug!("unexpected rep mismatch"); - } - if *state != TransmitLocalState::Busy { - bail_bug!("expected state to be busy"); - } - *state = match event { - Event::FutureRead { .. } => TransmitLocalState::Read { done: false }, - Event::FutureWrite { .. } => TransmitLocalState::Write { done: false }, - _ => bail_bug!("unexpected event for future"), - }; + (rep, state, code) } Event::StreamRead { pending: Some((ty, handle)), @@ -4693,37 +4691,41 @@ impl Waitable { pending: Some((ty, handle)), code, } => { - let instance = instance.id().get_mut(store); let runtime_instance = instance.component().types()[ty].instance; let (rep, state) = instance.instance_states().0[runtime_instance] .handle_table() .stream_rep(ty, handle)?; - if rep != self.rep() { - bail_bug!("unexpected rep mismatch"); - } - if *state != TransmitLocalState::Busy { - bail_bug!("expected state to be busy"); - } - let done = matches!(code, ReturnCode::Dropped(_)); - *state = match event { - Event::StreamRead { .. } => TransmitLocalState::Read { done }, - Event::StreamWrite { .. } => TransmitLocalState::Write { done }, - _ => bail_bug!("unexpected event for stream"), - }; + (rep, state, code) + } + _ => return Ok(()), + }; + if rep != self.rep() { + bail_bug!("unexpected rep mismatch"); + } + if *state != TransmitLocalState::Busy { + bail_bug!("expected state to be busy"); + } + let done = matches!(code, ReturnCode::Dropped(_)); + *state = match event { + Event::FutureRead { .. } | Event::StreamRead { .. } => { + TransmitLocalState::Read { done } + } + Event::FutureWrite { .. } | Event::StreamWrite { .. } => { + TransmitLocalState::Write { done } + } + _ => bail_bug!("unexpected event for stream"), + }; - let transmit_handle = TableId::::new(rep); - let state = store.concurrent_state_mut(); - let transmit_id = state.get_mut(transmit_handle)?.state; - let transmit = state.get_mut(transmit_id)?; + let transmit_handle = TableId::::new(rep); + let state = store.concurrent_state_mut(); + let transmit_id = state.get_mut(transmit_handle)?.state; + let transmit = state.get_mut(transmit_id)?; - match event { - Event::StreamRead { .. } => { - transmit.read = ReadState::Open; - } - Event::StreamWrite { .. } => transmit.write = WriteState::Open, - _ => bail_bug!("unexpected event for stream"), - }; + match event { + Event::StreamRead { .. } => { + transmit.read = ReadState::Open; } + Event::StreamWrite { .. } => transmit.write = WriteState::Open, _ => {} } Ok(()) @@ -4733,7 +4735,7 @@ impl Waitable { /// Determine whether an intra-component read/write is allowed for the specified /// `stream` or `future` payload type according to the component model /// specification. -fn allow_intra_component_read_write(ty: Option) -> bool { +fn allow_intra_component_read_write(ty: Option<&InterfaceType>) -> bool { matches!( ty, None | Some( diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams/buffers.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams/buffers.rs index 8a7f2f14c7a2..894ba19c8cf7 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams/buffers.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams/buffers.rs @@ -256,7 +256,7 @@ unsafe impl WriteBuffer for SliceBuffer { // always be sound. fun(unsafe { mem::transmute::<&[u8], &[MaybeUninit]>( - &self.buffer[self.offset - count..self.limit], + &self.buffer[self.offset - count..self.offset], ) }); } @@ -330,7 +330,7 @@ unsafe impl WriteBuffer for VecBuffer { // ensure that if `fun` panics that the items are still considered // transferred. self.offset += count; - fun(&self.buffer[self.offset - count..]); + fun(&self.buffer[self.offset - count..self.offset]); } } @@ -396,7 +396,7 @@ unsafe impl WriteBuffer for Cursor { fn take(&mut self, count: usize, fun: &mut dyn FnMut(&[MaybeUninit])) { assert!(count <= self.remaining().len()); - fun(unsafe_byte_slice(self.remaining())); + fun(unsafe_byte_slice(&self.remaining()[..count])); self.skip(count); } } @@ -420,7 +420,7 @@ unsafe impl WriteBuffer for Cursor { fn take(&mut self, count: usize, fun: &mut dyn FnMut(&[MaybeUninit])) { assert!(count <= self.remaining().len()); - fun(unsafe_byte_slice(self.remaining())); + fun(unsafe_byte_slice(&self.remaining()[..count])); self.skip(count); } } @@ -453,3 +453,66 @@ fn unsafe_byte_slice(slice: &[u8]) -> &[MaybeUninit] { // possibly-initialized slice of items. unsafe { mem::transmute::<&[u8], &[MaybeUninit]>(slice) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::prelude::*; + + #[test] + fn test_vec_buffer_take() { + let mut buf = VecBuffer::from(vec!["a".to_string(), "b".to_string(), "c".to_string()]); + let mut dst = Vec::new(); + dst.reserve(1); + dst.move_from(&mut buf, 1); + assert_eq!(buf.remaining().len(), 2); + assert_eq!(dst.len(), 1); + None.move_from(&mut buf, 1); + assert_eq!(buf.remaining().len(), 1); + assert_eq!(dst.len(), 1); + } + + #[test] + fn test_slice_buffer_take() { + let mut buf = SliceBuffer::new(vec![1, 2, 3], 0, 3); + let mut dst = Vec::new(); + dst.reserve(1); + dst.move_from(&mut buf, 1); + assert_eq!(buf.remaining().len(), 2); + assert_eq!(dst.len(), 1); + } + + #[test] + #[cfg(feature = "component-model-async-bytes")] + fn test_cursor_bytes_take() { + let mut buf = Cursor::new(Bytes::from(&b"123"[..])); + let mut dst = Vec::new(); + dst.reserve(1); + dst.move_from(&mut buf, 1); + assert_eq!(buf.remaining().len(), 2); + assert_eq!(dst.len(), 1); + + let mut dst = BytesMut::new(); + dst.reserve(1); + dst.move_from(&mut buf, 1); + assert_eq!(buf.remaining().len(), 1); + assert_eq!(dst.len(), 1); + } + + #[test] + #[cfg(feature = "component-model-async-bytes")] + fn test_cursor_bytes_mut_take() { + let mut buf = Cursor::new(BytesMut::from(&b"123"[..])); + let mut dst = Vec::new(); + dst.reserve(1); + dst.move_from(&mut buf, 1); + assert_eq!(buf.remaining().len(), 2); + assert_eq!(dst.len(), 1); + + let mut dst = BytesMut::new(); + dst.reserve(1); + dst.move_from(&mut buf, 1); + assert_eq!(buf.remaining().len(), 1); + assert_eq!(dst.len(), 1); + } +} diff --git a/crates/wasmtime/src/runtime/component/concurrent_disabled.rs b/crates/wasmtime/src/runtime/component/concurrent_disabled.rs index 1a61387df3bd..cf8691fd97d3 100644 --- a/crates/wasmtime/src/runtime/component/concurrent_disabled.rs +++ b/crates/wasmtime/src/runtime/component/concurrent_disabled.rs @@ -11,11 +11,11 @@ use wasmtime_environ::component::{CanonicalAbiInfo, InterfaceType}; pub enum ConcurrentState {} impl ConcurrentState { - pub fn call_context(&mut self, _: u32) -> &mut CallContext { + pub fn call_context(&mut self, _: u32) -> Result<&mut CallContext> { match *self {} } - pub fn current_call_context_scope_id(&self) -> u32 { + pub fn current_call_context_scope_id(&self) -> Result { match *self {} } } diff --git a/crates/wasmtime/src/runtime/component/func/options.rs b/crates/wasmtime/src/runtime/component/func/options.rs index 76f55f1e8ec3..265ad9ff8d51 100644 --- a/crates/wasmtime/src/runtime/component/func/options.rs +++ b/crates/wasmtime/src/runtime/component/func/options.rs @@ -10,6 +10,7 @@ use crate::runtime::vm::VMFuncRef; use crate::runtime::vm::component::{ComponentInstance, HandleTable, ResourceTables}; use crate::store::{StoreId, StoreOpaque}; use alloc::sync::Arc; +use core::fmt; use core::pin::Pin; use core::ptr::NonNull; use wasmtime_environ::component::{ @@ -480,11 +481,32 @@ impl<'a> LiftContext<'a> { pub fn consume_fuel(&mut self, amt: usize) -> Result<()> { match self.hostcall_fuel.checked_sub(amt) { Some(new) => self.hostcall_fuel = new, - None => bail!( - "too much data is being copied between the host and the guest: \ - fuel allocated for hostcalls has been exhausted" - ), + None => bail!(HostcallFuelExhausted), } Ok(()) } + + /// Same as [`Self::consume_fuel`], but safely multiplies `len` and `size` + /// together before calling that. + pub fn consume_fuel_array(&mut self, len: usize, size: usize) -> Result<()> { + match len.checked_mul(size) { + Some(bytes) => self.consume_fuel(bytes), + None => bail!(HostcallFuelExhausted), + } + } } + +#[derive(Debug)] +struct HostcallFuelExhausted; + +impl fmt::Display for HostcallFuelExhausted { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "too much data is being copied between the host and the guest: \ + fuel allocated for hostcalls has been exhausted" + ) + } +} + +impl core::error::Error for HostcallFuelExhausted {} diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs index fe2dee93e9ca..26502343504e 100644 --- a/crates/wasmtime/src/runtime/component/func/typed.rs +++ b/crates/wasmtime/src/runtime/component/func/typed.rs @@ -2,17 +2,19 @@ use crate::component::Instance; use crate::component::func::{Func, LiftContext, LowerContext}; use crate::component::matching::InstanceType; use crate::component::storage::{storage_as_slice, storage_as_slice_mut}; +use crate::hash_map::HashMap; use crate::prelude::*; use crate::{AsContextMut, StoreContext, StoreContextMut, ValRaw}; use alloc::borrow::Cow; use core::fmt; +use core::hash::Hash; use core::iter; use core::marker; use core::mem::{self, MaybeUninit}; use core::str; use wasmtime_environ::component::{ - CanonicalAbiInfo, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, OptionsIndex, - StringEncoding, VariantInfo, + CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, + OptionsIndex, StringEncoding, TypeMap, VariantInfo, }; #[cfg(feature = "component-model-async")] @@ -617,6 +619,7 @@ pub unsafe trait ComponentNamedList: ComponentType {} /// | `result` | `Result` | /// | `string` | `String`, `&str`, or [`WasmStr`] | /// | `list` | `Vec`, `&[T]`, or [`WasmList`] | +/// | `map` | `HashMap` | /// | `own`, `borrow` | [`Resource`] or [`ResourceAny`] | /// | `record` | [`#[derive(ComponentType)]`][d-cm] | /// | `variant` | [`#[derive(ComponentType)]`][d-cm] | @@ -1433,6 +1436,49 @@ unsafe impl Lift for char { } } +fn lift_pointer_pair_from_flat( + cx: &mut LiftContext<'_>, + src: &[ValRaw; 2], +) -> Result<(usize, usize)> { + // FIXME(#4311): needs memory64 treatment + let _ = cx; // this will be needed for memory64 in the future + let ptr = src[0].get_u32(); + let len = src[1].get_u32(); + Ok((usize::try_from(ptr)?, usize::try_from(len)?)) +} + +fn lift_pointer_pair_from_memory(cx: &mut LiftContext<'_>, bytes: &[u8]) -> Result<(usize, usize)> { + // FIXME(#4311): needs memory64 treatment + let _ = cx; // this will be needed for memory64 in the future + let ptr = u32::from_le_bytes(bytes[..4].try_into().unwrap()); + let len = u32::from_le_bytes(bytes[4..].try_into().unwrap()); + Ok((usize::try_from(ptr)?, usize::try_from(len)?)) +} + +fn lower_pointer_pair_to_flat( + cx: &mut LowerContext, + dst: &mut MaybeUninit<[ValRaw; 2]>, + ptr: usize, + len: usize, +) { + // See "WRITEPTR64" above for why this is always storing a 64-bit + // integer. + let _ = cx; // this will eventually be needed for memory64 information. + map_maybe_uninit!(dst[0]).write(ValRaw::i64(ptr as i64)); + map_maybe_uninit!(dst[1]).write(ValRaw::i64(len as i64)); +} + +fn lower_pointer_pair_to_memory( + cx: &mut LowerContext, + offset: usize, + ptr: usize, + len: usize, +) { + // FIXME(#4311): needs memory64 handling + *cx.get(offset + 0) = u32::try_from(ptr).unwrap().to_le_bytes(); + *cx.get(offset + 4) = u32::try_from(len).unwrap().to_le_bytes(); +} + // FIXME(#4311): these probably need different constants for memory64 const UTF16_TAG: usize = 1 << 31; const MAX_STRING_BYTE_LENGTH: usize = (1 << 31) - 1; @@ -1461,10 +1507,7 @@ unsafe impl Lower for str { ) -> Result<()> { debug_assert!(matches!(ty, InterfaceType::String)); let (ptr, len) = lower_string(cx, self)?; - // See "WRITEPTR64" above for why this is always storing a 64-bit - // integer. - map_maybe_uninit!(dst[0]).write(ValRaw::i64(ptr as i64)); - map_maybe_uninit!(dst[1]).write(ValRaw::i64(len as i64)); + lower_pointer_pair_to_flat(cx, dst, ptr, len); Ok(()) } @@ -1477,9 +1520,7 @@ unsafe impl Lower for str { debug_assert!(matches!(ty, InterfaceType::String)); debug_assert!(offset % (Self::ALIGN32 as usize) == 0); let (ptr, len) = lower_string(cx, self)?; - // FIXME(#4311): needs memory64 handling - *cx.get(offset + 0) = u32::try_from(ptr).unwrap().to_le_bytes(); - *cx.get(offset + 4) = u32::try_from(len).unwrap().to_le_bytes(); + lower_pointer_pair_to_memory(cx, offset, ptr, len); Ok(()) } } @@ -1639,17 +1680,21 @@ pub struct WasmStr { impl WasmStr { pub(crate) fn new(ptr: usize, len: usize, cx: &mut LiftContext<'_>) -> Result { - let byte_len = match cx.options().string_encoding { - StringEncoding::Utf8 => Some(len), - StringEncoding::Utf16 => len.checked_mul(2), + let (byte_len, align) = match cx.options().string_encoding { + StringEncoding::Utf8 => (Some(len), 1_usize), + StringEncoding::Utf16 => (len.checked_mul(2), 2), StringEncoding::CompactUtf16 => { if len & UTF16_TAG == 0 { - Some(len) + (Some(len), 2) } else { - (len ^ UTF16_TAG).checked_mul(2) + ((len ^ UTF16_TAG).checked_mul(2), 2) } } }; + debug_assert!(align.is_power_of_two()); + if ptr & (align - 1) != 0 { + bail!("string pointer not aligned to {align}"); + } match byte_len.and_then(|len| ptr.checked_add(len)) { Some(n) if n <= cx.memory().len() => cx.consume_fuel(n - ptr)?, _ => bail!("string pointer/length out of bounds of memory"), @@ -1761,10 +1806,7 @@ unsafe impl Lift for WasmStr { src: &Self::Lower, ) -> Result { debug_assert!(matches!(ty, InterfaceType::String)); - // FIXME(#4311): needs memory64 treatment - let ptr = src[0].get_u32(); - let len = src[1].get_u32(); - let (ptr, len) = (usize::try_from(ptr)?, usize::try_from(len)?); + let (ptr, len) = lift_pointer_pair_from_flat(cx, src)?; WasmStr::new(ptr, len, cx) } @@ -1776,10 +1818,7 @@ unsafe impl Lift for WasmStr { ) -> Result { debug_assert!(matches!(ty, InterfaceType::String)); debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0); - // FIXME(#4311): needs memory64 treatment - let ptr = u32::from_le_bytes(bytes[..4].try_into().unwrap()); - let len = u32::from_le_bytes(bytes[4..].try_into().unwrap()); - let (ptr, len) = (usize::try_from(ptr)?, usize::try_from(len)?); + let (ptr, len) = lift_pointer_pair_from_memory(cx, bytes)?; WasmStr::new(ptr, len, cx) } } @@ -1815,10 +1854,7 @@ where _ => bad_type_info(), }; let (ptr, len) = lower_list(cx, elem, self)?; - // See "WRITEPTR64" above for why this is always storing a 64-bit - // integer. - map_maybe_uninit!(dst[0]).write(ValRaw::i64(ptr as i64)); - map_maybe_uninit!(dst[1]).write(ValRaw::i64(len as i64)); + lower_pointer_pair_to_flat(cx, dst, ptr, len); Ok(()) } @@ -1834,8 +1870,7 @@ where }; debug_assert!(offset % (Self::ALIGN32 as usize) == 0); let (ptr, len) = lower_list(cx, elem, self)?; - *cx.get(offset + 0) = u32::try_from(ptr).unwrap().to_le_bytes(); - *cx.get(offset + 4) = u32::try_from(len).unwrap().to_le_bytes(); + lower_pointer_pair_to_memory(cx, offset, ptr, len); Ok(()) } } @@ -1909,7 +1944,7 @@ impl WasmList { .checked_mul(T::SIZE32) .and_then(|len| ptr.checked_add(len)) { - Some(n) if n <= cx.memory().len() => cx.consume_fuel(n - ptr)?, + Some(n) if n <= cx.memory().len() => cx.consume_fuel_array(len, size_of::())?, _ => bail!("list pointer/length out of bounds of memory"), } if ptr % usize::try_from(T::ALIGN32)? != 0 { @@ -2057,10 +2092,7 @@ unsafe impl Lift for WasmList { InterfaceType::List(i) => cx.types[i].element, _ => bad_type_info(), }; - // FIXME(#4311): needs memory64 treatment - let ptr = src[0].get_u32(); - let len = src[1].get_u32(); - let (ptr, len) = (usize::try_from(ptr)?, usize::try_from(len)?); + let (ptr, len) = lift_pointer_pair_from_flat(cx, src)?; WasmList::new(ptr, len, cx, elem) } @@ -2074,14 +2106,242 @@ unsafe impl Lift for WasmList { _ => bad_type_info(), }; debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0); - // FIXME(#4311): needs memory64 treatment - let ptr = u32::from_le_bytes(bytes[..4].try_into().unwrap()); - let len = u32::from_le_bytes(bytes[4..].try_into().unwrap()); - let (ptr, len) = (usize::try_from(ptr)?, usize::try_from(len)?); + let (ptr, len) = lift_pointer_pair_from_memory(cx, bytes)?; WasmList::new(ptr, len, cx, elem) } } +// ============================================================================= +// HashMap support for component model `map` +// +// Maps are represented as `list>` in the canonical ABI, so the +// lowered form is a (pointer, length) pair just like lists. + +fn map_abi<'a>(ty: InterfaceType, types: &'a ComponentTypes) -> &'a TypeMap { + match ty { + InterfaceType::Map(i) => &types[i], + _ => bad_type_info(), + } +} + +unsafe impl ComponentType for HashMap +where + K: ComponentType, + V: ComponentType, +{ + type Lower = [ValRaw; 2]; + + const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR; + + fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> { + TryHashMap::::typecheck(ty, types) + } +} + +unsafe impl Lower for HashMap +where + K: Lower, + V: Lower, +{ + fn linear_lower_to_flat( + &self, + cx: &mut LowerContext<'_, U>, + ty: InterfaceType, + dst: &mut MaybeUninit<[ValRaw; 2]>, + ) -> Result<()> { + let map = map_abi(ty, &cx.types); + let (ptr, len) = lower_map_iter(cx, map, self.len(), self.iter())?; + lower_pointer_pair_to_flat(cx, dst, ptr, len); + Ok(()) + } + + fn linear_lower_to_memory( + &self, + cx: &mut LowerContext<'_, U>, + ty: InterfaceType, + offset: usize, + ) -> Result<()> { + let map = map_abi(ty, &cx.types); + debug_assert!(offset % (CanonicalAbiInfo::POINTER_PAIR.align32 as usize) == 0); + let (ptr, len) = lower_map_iter(cx, map, self.len(), self.iter())?; + lower_pointer_pair_to_memory(cx, offset, ptr, len); + Ok(()) + } +} + +unsafe impl Lift for HashMap +where + K: Lift + Eq + Hash, + V: Lift, +{ + fn linear_lift_from_flat( + cx: &mut LiftContext<'_>, + ty: InterfaceType, + src: &Self::Lower, + ) -> Result { + Ok(TryHashMap::::linear_lift_from_flat(cx, ty, src)?.into()) + } + + fn linear_lift_from_memory( + cx: &mut LiftContext<'_>, + ty: InterfaceType, + bytes: &[u8], + ) -> Result { + Ok(TryHashMap::::linear_lift_from_memory(cx, ty, bytes)?.into()) + } +} + +fn lower_map_iter<'a, K, V, U>( + cx: &mut LowerContext<'_, U>, + map: &TypeMap, + len: usize, + iter: impl Iterator, +) -> Result<(usize, usize)> +where + K: Lower + 'a, + V: Lower + 'a, +{ + let size = len + .checked_mul(usize::try_from(map.entry_abi.size32)?) + .ok_or_else(|| format_err!("size overflow copying a map"))?; + let ptr = cx.realloc(0, 0, map.entry_abi.align32, size)?; + + let mut entry_offset = ptr; + for (key, value) in iter { + // Keys are the first field in each entry tuple. + ::linear_lower_to_memory(key, cx, map.key, entry_offset)?; + // Values start at the precomputed value offset within the tuple. + ::linear_lower_to_memory( + value, + cx, + map.value, + entry_offset + usize::try_from(map.value_offset32)?, + )?; + entry_offset += usize::try_from(map.entry_abi.size32)?; + } + + Ok((ptr, len)) +} + +unsafe impl ComponentType for TryHashMap +where + K: ComponentType, + V: ComponentType, +{ + type Lower = [ValRaw; 2]; + + const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR; + + fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> { + match ty { + InterfaceType::Map(t) => { + let map_ty = &types.types[*t]; + K::typecheck(&map_ty.key, types)?; + V::typecheck(&map_ty.value, types)?; + Ok(()) + } + other => bail!("expected `map` found `{}`", desc(other)), + } + } +} + +unsafe impl Lower for TryHashMap +where + K: Lower, + V: Lower, +{ + fn linear_lower_to_flat( + &self, + cx: &mut LowerContext<'_, U>, + ty: InterfaceType, + dst: &mut MaybeUninit<[ValRaw; 2]>, + ) -> Result<()> { + let map = map_abi(ty, &cx.types); + let (ptr, len) = lower_map_iter(cx, map, self.len(), self.iter())?; + lower_pointer_pair_to_flat(cx, dst, ptr, len); + Ok(()) + } + + fn linear_lower_to_memory( + &self, + cx: &mut LowerContext<'_, U>, + ty: InterfaceType, + offset: usize, + ) -> Result<()> { + let map = map_abi(ty, &cx.types); + debug_assert!(offset % (CanonicalAbiInfo::POINTER_PAIR.align32 as usize) == 0); + let (ptr, len) = lower_map_iter(cx, map, self.len(), self.iter())?; + lower_pointer_pair_to_memory(cx, offset, ptr, len); + Ok(()) + } +} + +unsafe impl Lift for TryHashMap +where + K: Lift + Eq + Hash, + V: Lift, +{ + fn linear_lift_from_flat( + cx: &mut LiftContext<'_>, + ty: InterfaceType, + src: &Self::Lower, + ) -> Result { + let map = map_abi(ty, &cx.types); + let (ptr, len) = lift_pointer_pair_from_flat(cx, src)?; + lift_try_map(cx, map, ptr, len) + } + + fn linear_lift_from_memory( + cx: &mut LiftContext<'_>, + ty: InterfaceType, + bytes: &[u8], + ) -> Result { + let map = map_abi(ty, &cx.types); + debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0); + let (ptr, len) = lift_pointer_pair_from_memory(cx, bytes)?; + lift_try_map(cx, map, ptr, len) + } +} + +fn lift_try_map( + cx: &mut LiftContext<'_>, + map: &TypeMap, + ptr: usize, + len: usize, +) -> Result> +where + K: Lift + Eq + Hash, + V: Lift, +{ + let mut result = TryHashMap::with_capacity(len)?; + + match len + .checked_mul(usize::try_from(map.entry_abi.size32)?) + .and_then(|total| ptr.checked_add(total)) + { + Some(n) if n <= cx.memory().len() => cx.consume_fuel_array(len, size_of::<(K, V)>())?, + _ => bail!("map pointer/length out of bounds of memory"), + } + if ptr % (map.entry_abi.align32 as usize) != 0 { + bail!("map pointer is not aligned"); + } + + for i in 0..len { + let entry_base = ptr + (i * usize::try_from(map.entry_abi.size32)?); + + let key_bytes = &cx.memory()[entry_base..][..K::SIZE32]; + let key = K::linear_lift_from_memory(cx, map.key, key_bytes)?; + + let value_bytes = + &cx.memory()[entry_base + usize::try_from(map.value_offset32)?..][..V::SIZE32]; + let value = V::linear_lift_from_memory(cx, map.value, value_bytes)?; + + result.insert(key, value)?; + } + + Ok(result) +} + /// Verify that the given wasm type is a tuple with the expected fields in the right order. fn typecheck_tuple( ty: &InterfaceType, @@ -2909,6 +3169,7 @@ pub fn desc(ty: &InterfaceType) -> &'static str { InterfaceType::Future(_) => "future", InterfaceType::Stream(_) => "stream", InterfaceType::ErrorContext(_) => "error-context", + InterfaceType::Map(_) => "map", InterfaceType::FixedLengthList(_) => "list<_, N>", } } diff --git a/crates/wasmtime/src/runtime/component/instance.rs b/crates/wasmtime/src/runtime/component/instance.rs index 7ad45d9c6905..c701630580f8 100644 --- a/crates/wasmtime/src/runtime/component/instance.rs +++ b/crates/wasmtime/src/runtime/component/instance.rs @@ -823,7 +823,7 @@ impl<'a> Instantiator<'a> { // `args` list is already in the right order. InstantiateModule::Static(idx, args) => { module = self.component.static_module(*idx); - self.build_imports(store.0, module, args.iter()) + self.build_imports(store.0, module, args.iter())? } // With imports, unlike upvars, we need to do runtime @@ -842,7 +842,7 @@ impl<'a> Instantiator<'a> { let args = module .imports() .map(|import| &args[import.module()][import.name()]); - self.build_imports(store.0, module, args) + self.build_imports(store.0, module, args)? } }; @@ -985,9 +985,9 @@ impl<'a> Instantiator<'a> { store: &mut StoreOpaque, module: &Module, args: impl Iterator, - ) -> &OwnedImports { + ) -> Result<&OwnedImports, OutOfMemory> { self.core_imports.clear(); - self.core_imports.reserve(module); + self.core_imports.reserve(module)?; let mut imports = module.compiled_module().module().imports(); for arg in args { @@ -1005,11 +1005,11 @@ impl<'a> Instantiator<'a> { // directly from an instance which should only give us valid export // items. let export = lookup_vmdef(store, self.id, arg); - self.core_imports.push_export(store, &export); + self.core_imports.push_export(store, &export)?; } debug_assert!(imports.next().is_none()); - &self.core_imports + Ok(&self.core_imports) } fn assert_type_matches( @@ -1046,7 +1046,7 @@ impl<'a> Instantiator<'a> { return; } - let val = crate::Extern::from_wasmtime_export(export, store); + let val = crate::Extern::from_wasmtime_export(export, store.engine()); let ty = DefinitionType::from(store, &val); crate::types::matching::MatchCx::new(module.engine()) .definition(&expected, &ty) diff --git a/crates/wasmtime/src/runtime/component/resources/host_tables.rs b/crates/wasmtime/src/runtime/component/resources/host_tables.rs index 625035296bde..8b83e1b58acd 100644 --- a/crates/wasmtime/src/runtime/component/resources/host_tables.rs +++ b/crates/wasmtime/src/runtime/component/resources/host_tables.rs @@ -177,7 +177,7 @@ impl<'a> HostResourceTables<'a> { // precise error, such as a lift operation. if let Some(actual) = actual { if actual.generation != idx.generation() { - bail!("host-owned resource is being used with the wrong type"); + bail!("host-owned resource was already de-allocated"); } } diff --git a/crates/wasmtime/src/runtime/component/store.rs b/crates/wasmtime/src/runtime/component/store.rs index def9d7f3e60c..f1acb5130d61 100644 --- a/crates/wasmtime/src/runtime/component/store.rs +++ b/crates/wasmtime/src/runtime/component/store.rs @@ -1,4 +1,6 @@ use crate::prelude::*; +#[cfg(feature = "component-model-async")] +use crate::runtime::component::ResourceTable; use crate::runtime::component::concurrent::ConcurrentState; use crate::runtime::component::{HostResourceData, Instance}; use crate::runtime::vm; @@ -415,6 +417,15 @@ impl StoreOpaque { pub(crate) fn set_hostcall_fuel(&mut self, fuel: usize) { self.component_data_mut().hostcall_fuel = fuel; } + + #[cfg(feature = "component-model-async")] + fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> { + if self.concurrency_support() { + Some(self.concurrent_state_mut().table()) + } else { + None + } + } } impl Store { @@ -455,6 +466,17 @@ impl Store { pub fn set_hostcall_fuel(&mut self, fuel: usize) { self.as_context_mut().set_hostcall_fuel(fuel) } + + /// Returns the underlying [`ResourceTable`] that the implementation of + /// concurrency in the component model is using. + /// + /// Returns `None` if [`Config::concurrency_support`] is disabled. + /// + /// [`Config::concurrency_support`]: crate::Config::concurrency_support + #[cfg(feature = "component-model-async")] + pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> { + self.as_context_mut().0.concurrent_resource_table() + } } impl StoreContextMut<'_, T> { @@ -467,6 +489,12 @@ impl StoreContextMut<'_, T> { pub fn set_hostcall_fuel(&mut self, fuel: usize) { self.0.set_hostcall_fuel(fuel) } + + /// See [`Store::concurrent_resource_table`]. + #[cfg(feature = "component-model-async")] + pub fn concurrent_resource_table(&mut self) -> Option<&mut ResourceTable> { + self.0.concurrent_resource_table() + } } #[derive(Default)] @@ -475,18 +503,16 @@ pub struct ComponentTasksNotConcurrent { } impl ComponentTaskState { - pub fn call_context(&mut self, id: u32) -> &mut CallContext { + pub fn call_context(&mut self, id: u32) -> Result<&mut CallContext> { match self { - ComponentTaskState::NotConcurrent(state) => &mut state.scopes[id as usize], + ComponentTaskState::NotConcurrent(state) => Ok(&mut state.scopes[id as usize]), ComponentTaskState::Concurrent(state) => state.call_context(id), } } - pub fn current_call_context_scope_id(&self) -> u32 { + pub fn current_call_context_scope_id(&self) -> Result { match self { - ComponentTaskState::NotConcurrent(state) => { - u32::try_from(state.scopes.len() - 1).unwrap() - } + ComponentTaskState::NotConcurrent(state) => Ok(u32::try_from(state.scopes.len() - 1)?), ComponentTaskState::Concurrent(state) => state.current_call_context_scope_id(), } } diff --git a/crates/wasmtime/src/runtime/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs index a14cd16764b7..241eb46d196f 100644 --- a/crates/wasmtime/src/runtime/component/types.rs +++ b/crates/wasmtime/src/runtime/component/types.rs @@ -10,9 +10,9 @@ use core::ops::Deref; use wasmtime_environ::component::{ ComponentTypes, Export, InterfaceType, ResourceIndex, TypeComponentIndex, TypeComponentInstanceIndex, TypeDef, TypeEnumIndex, TypeFlagsIndex, TypeFuncIndex, - TypeFutureIndex, TypeFutureTableIndex, TypeListIndex, TypeModuleIndex, TypeOptionIndex, - TypeRecordIndex, TypeResourceTable, TypeResourceTableIndex, TypeResultIndex, TypeStreamIndex, - TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, + TypeFutureIndex, TypeFutureTableIndex, TypeListIndex, TypeMapIndex, TypeModuleIndex, + TypeOptionIndex, TypeRecordIndex, TypeResourceTable, TypeResourceTableIndex, TypeResultIndex, + TypeStreamIndex, TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, }; use wasmtime_environ::{PanicOnOom as _, PrimaryMap}; @@ -108,6 +108,8 @@ impl TypeChecker<'_> { (InterfaceType::Borrow(_), _) => false, (InterfaceType::List(l1), InterfaceType::List(l2)) => self.lists_equal(l1, l2), (InterfaceType::List(_), _) => false, + (InterfaceType::Map(m1), InterfaceType::Map(m2)) => self.maps_equal(m1, m2), + (InterfaceType::Map(_), _) => false, (InterfaceType::Record(r1), InterfaceType::Record(r2)) => self.records_equal(r1, r2), (InterfaceType::Record(_), _) => false, (InterfaceType::Variant(v1), InterfaceType::Variant(v2)) => self.variants_equal(v1, v2), @@ -168,6 +170,12 @@ impl TypeChecker<'_> { self.interface_types_equal(a.element, b.element) } + fn maps_equal(&self, m1: TypeMapIndex, m2: TypeMapIndex) -> bool { + let a = &self.a_types[m1]; + let b = &self.b_types[m2]; + self.interface_types_equal(a.key, b.key) && self.interface_types_equal(a.value, b.value) + } + fn resources_equal(&self, o1: TypeResourceTableIndex, o2: TypeResourceTableIndex) -> bool { match (&self.a_types[o1], &self.b_types[o2]) { // Concrete resource types are the same if they map back to the @@ -325,6 +333,34 @@ impl List { } } +/// A `map` interface type +#[derive(Clone, Debug)] +pub struct Map(Handle); + +impl PartialEq for Map { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::maps_equal) + } +} + +impl Eq for Map {} + +impl Map { + pub(crate) fn from(index: TypeMapIndex, ty: &InstanceType<'_>) -> Self { + Map(Handle::new(index, ty)) + } + + /// Retrieve the key type of this `map`. + pub fn key(&self) -> Type { + Type::from(&self.0.types[self.0.index].key, &self.0.instance()) + } + + /// Retrieve the value type of this `map`. + pub fn value(&self) -> Type { + Type::from(&self.0.types[self.0.index].value, &self.0.instance()) + } +} + /// A field declaration belonging to a `record` #[derive(Debug)] pub struct Field<'a> { @@ -684,6 +720,7 @@ pub enum Type { Char, String, List(List), + Map(Map), Record(Record), Tuple(Tuple), Variant(Variant), @@ -844,6 +881,7 @@ impl Type { InterfaceType::Char => Type::Char, InterfaceType::String => Type::String, InterfaceType::List(index) => Type::List(List::from(*index, instance)), + InterfaceType::Map(index) => Type::Map(Map::from(*index, instance)), InterfaceType::Record(index) => Type::Record(Record::from(*index, instance)), InterfaceType::Tuple(index) => Type::Tuple(Tuple::from(*index, instance)), InterfaceType::Variant(index) => Type::Variant(Variant::from(*index, instance)), @@ -876,6 +914,7 @@ impl Type { Type::Char => "char", Type::String => "string", Type::List(_) => "list", + Type::Map(_) => "map", Type::Record(_) => "record", Type::Tuple(_) => "tuple", Type::Variant(_) => "variant", diff --git a/crates/wasmtime/src/runtime/component/values.rs b/crates/wasmtime/src/runtime/component/values.rs index 1049d1c12712..099122505c12 100644 --- a/crates/wasmtime/src/runtime/component/values.rs +++ b/crates/wasmtime/src/runtime/component/values.rs @@ -7,8 +7,8 @@ use core::mem::MaybeUninit; use core::slice::{Iter, IterMut}; use wasmtime_component_util::{DiscriminantSize, FlagsSize}; use wasmtime_environ::component::{ - CanonicalAbiInfo, InterfaceType, TypeEnum, TypeFlags, TypeListIndex, TypeOption, TypeResult, - TypeVariant, VariantInfo, + CanonicalAbiInfo, InterfaceType, TypeEnum, TypeFlags, TypeListIndex, TypeMap, TypeMapIndex, + TypeOption, TypeResult, TypeVariant, VariantInfo, }; /// Represents possible runtime values which a component function can either @@ -79,6 +79,9 @@ pub enum Val { Char(char), String(String), List(Vec), + /// A map type represented as a list of key-value pairs. + /// Duplicate keys are allowed and follow "last value wins" semantics. + Map(Vec<(Val, Val)>), Record(Vec<(String, Val)>), Tuple(Vec), Variant(String, Option>), @@ -121,11 +124,13 @@ impl Val { &[*next(src), *next(src)], )?), InterfaceType::List(i) => { - // FIXME(#4311): needs memory64 treatment - let ptr = u32::linear_lift_from_flat(cx, InterfaceType::U32, next(src))? as usize; - let len = u32::linear_lift_from_flat(cx, InterfaceType::U32, next(src))? as usize; + let (ptr, len) = lift_flat_pointer_pair(cx, src)?; load_list(cx, i, ptr, len)? } + InterfaceType::Map(i) => { + let (ptr, len) = lift_flat_pointer_pair(cx, src)?; + load_map(cx, i, ptr, len)? + } InterfaceType::Record(i) => Val::Record( cx.types[i] .fields @@ -239,11 +244,13 @@ impl Val { Val::Resource(ResourceAny::linear_lift_from_memory(cx, ty, bytes)?) } InterfaceType::List(i) => { - // FIXME(#4311): needs memory64 treatment - let ptr = u32::from_le_bytes(bytes[..4].try_into().unwrap()) as usize; - let len = u32::from_le_bytes(bytes[4..].try_into().unwrap()) as usize; + let (ptr, len) = load_flat_pointer_pair(bytes); load_list(cx, i, ptr, len)? } + InterfaceType::Map(i) => { + let (ptr, len) = load_flat_pointer_pair(bytes); + load_map(cx, i, ptr, len)? + } InterfaceType::Record(i) => { let mut offset = 0; @@ -425,6 +432,14 @@ impl Val { Ok(()) } (InterfaceType::List(_), _) => unexpected(ty, self), + (InterfaceType::Map(ty), Val::Map(pairs)) => { + let map_ty = &cx.types[ty]; + let (ptr, len) = lower_map(cx, map_ty, pairs)?; + next_mut(dst).write(ValRaw::i64(ptr as i64)); + next_mut(dst).write(ValRaw::i64(len as i64)); + Ok(()) + } + (InterfaceType::Map(_), _) => unexpected(ty, self), (InterfaceType::Record(ty), Val::Record(values)) => { let ty = &cx.types[ty]; if ty.fields.len() != values.len() { @@ -554,6 +569,15 @@ impl Val { Ok(()) } (InterfaceType::List(_), _) => unexpected(ty, self), + (InterfaceType::Map(ty_idx), Val::Map(values)) => { + let map_ty = &cx.types[ty_idx]; + let (ptr, len) = lower_map(cx, map_ty, values)?; + // FIXME(#4311): needs memory64 handling + *cx.get(offset + 0) = u32::try_from(ptr).unwrap().to_le_bytes(); + *cx.get(offset + 4) = u32::try_from(len).unwrap().to_le_bytes(); + Ok(()) + } + (InterfaceType::Map(_), _) => unexpected(ty, self), (InterfaceType::Record(ty), Val::Record(values)) => { let ty = &cx.types[ty]; if ty.fields.len() != values.len() { @@ -666,6 +690,7 @@ impl Val { Val::Float64(_) => "f64", Val::Char(_) => "char", Val::List(_) => "list", + Val::Map(_) => "map", Val::String(_) => "string", Val::Record(_) => "record", Val::Enum(_) => "enum", @@ -740,6 +765,8 @@ impl PartialEq for Val { (Self::String(_), _) => false, (Self::List(l), Self::List(r)) => l == r, (Self::List(_), _) => false, + (Self::Map(l), Self::Map(r)) => l == r, + (Self::Map(_), _) => false, (Self::Record(l), Self::Record(r)) => l == r, (Self::Record(_), _) => false, (Self::Tuple(l), Self::Tuple(r)) => l == r, @@ -909,6 +936,22 @@ impl GenericVariant<'_> { } } +fn lift_flat_pointer_pair( + cx: &mut LiftContext<'_>, + src: &mut Iter<'_, ValRaw>, +) -> Result<(usize, usize)> { + // FIXME(#4311): needs memory64 treatment + let ptr = u32::linear_lift_from_flat(cx, InterfaceType::U32, next(src))? as usize; + let len = u32::linear_lift_from_flat(cx, InterfaceType::U32, next(src))? as usize; + Ok((ptr, len)) +} + +fn load_flat_pointer_pair(bytes: &[u8]) -> (usize, usize) { + let ptr = u32::from_le_bytes(bytes[..4].try_into().unwrap()) as usize; + let len = u32::from_le_bytes(bytes[4..].try_into().unwrap()) as usize; + (ptr, len) +} + fn load_list(cx: &mut LiftContext<'_>, ty: TypeListIndex, ptr: usize, len: usize) -> Result { let elem = cx.types[ty].element; let abi = cx.types.canonical_abi(&elem); @@ -919,7 +962,7 @@ fn load_list(cx: &mut LiftContext<'_>, ty: TypeListIndex, ptr: usize, len: usize .checked_mul(element_size) .and_then(|len| ptr.checked_add(len)) { - Some(n) if n <= cx.memory().len() => cx.consume_fuel(n - ptr)?, + Some(n) if n <= cx.memory().len() => cx.consume_fuel_array(len, size_of::())?, _ => bail!("list pointer/length out of bounds of memory"), } if ptr % usize::try_from(element_alignment)? != 0 { @@ -939,6 +982,48 @@ fn load_list(cx: &mut LiftContext<'_>, ty: TypeListIndex, ptr: usize, len: usize )) } +fn load_map(cx: &mut LiftContext<'_>, ty: TypeMapIndex, ptr: usize, len: usize) -> Result { + // Maps are stored as list> in canonical ABI + let map_ty = &cx.types[ty]; + let key_ty = map_ty.key; + let value_ty = map_ty.value; + + let key_abi = cx.types.canonical_abi(&key_ty); + let value_abi = cx.types.canonical_abi(&value_ty); + let key_size = usize::try_from(key_abi.size32).unwrap(); + let value_size = usize::try_from(value_abi.size32).unwrap(); + let value_offset = usize::try_from(map_ty.value_offset32).unwrap(); + let tuple_alignment = map_ty.entry_abi.align32; + let tuple_size = usize::try_from(map_ty.entry_abi.size32).unwrap(); + + // Bounds check + match len + .checked_mul(tuple_size) + .and_then(|len| ptr.checked_add(len)) + { + Some(n) if n <= cx.memory().len() => cx.consume_fuel_array(len, size_of::<(Val, Val)>())?, + _ => bail!("map pointer/length out of bounds of memory"), + } + if ptr % usize::try_from(tuple_alignment)? != 0 { + bail!("map pointer is not aligned") + } + + // Load each tuple (key, value) into a Vec + let mut map = Vec::with_capacity(len); + for index in 0..len { + let tuple_ptr = ptr + (index * tuple_size); + let key = Val::load(cx, key_ty, &cx.memory()[tuple_ptr..][..key_size])?; + let value = Val::load( + cx, + value_ty, + &cx.memory()[tuple_ptr + value_offset..][..value_size], + )?; + map.push((key, value)); + } + + Ok(Val::Map(map)) +} + fn load_variant( cx: &mut LiftContext<'_>, info: &VariantInfo, @@ -960,13 +1045,10 @@ fn load_variant( u32::linear_lift_from_memory(cx, InterfaceType::U32, &bytes[..4])? } }; - let case_ty = types.nth(discriminant as usize).ok_or_else(|| { - format_err!( - "discriminant {} out of range [0..{})", - discriminant, - types.len() - ) - })?; + let len = types.len(); + let case_ty = types + .nth(discriminant as usize) + .ok_or_else(|| format_err!("discriminant {discriminant} out of range [0..{len})"))?; let value = match case_ty { Some(case_ty) => { let payload_offset = usize::try_from(info.payload_offset32).unwrap(); @@ -1029,8 +1111,38 @@ fn lower_list( Ok((ptr, items.len())) } +/// Lower a map as list> with the specified key and value types. +fn lower_map( + cx: &mut LowerContext<'_, T>, + map_ty: &TypeMap, + pairs: &[(Val, Val)], +) -> Result<(usize, usize)> { + let key_type = map_ty.key; + let value_type = map_ty.value; + let value_offset = usize::try_from(map_ty.value_offset32).unwrap(); + let tuple_align = map_ty.entry_abi.align32; + let tuple_size = usize::try_from(map_ty.entry_abi.size32).unwrap(); + + let size = pairs + .len() + .checked_mul(tuple_size) + .ok_or_else(|| crate::format_err!("size overflow copying a map"))?; + let ptr = cx.realloc(0, 0, tuple_align, size)?; + + let mut tuple_ptr = ptr; + for (key, value) in pairs { + // Store key at tuple_ptr + key.store(cx, key_type, tuple_ptr)?; + // Store value at tuple_ptr + value_offset (properly aligned) + value.store(cx, value_type, tuple_ptr + value_offset)?; + tuple_ptr += tuple_size; + } + + Ok((ptr, pairs.len())) +} + fn push_flags(ty: &TypeFlags, flags: &mut Vec, mut offset: u32, mut bits: u32) { - while bits > 0 { + while bits > 0 && usize::try_from(offset).unwrap() < ty.names.len() { if bits & 1 != 0 { flags.push(ty.names[offset as usize].clone()); } diff --git a/crates/wasmtime/src/runtime/debug.rs b/crates/wasmtime/src/runtime/debug.rs index b4ddc8da16c2..c91d369cf697 100644 --- a/crates/wasmtime/src/runtime/debug.rs +++ b/crates/wasmtime/src/runtime/debug.rs @@ -20,6 +20,8 @@ use alloc::vec::Vec; use core::{ffi::c_void, ptr::NonNull}; #[cfg(feature = "gc")] use wasmtime_environ::FrameTable; +// Re-export ModulePC so downstream crates can use it. +pub use wasmtime_environ::ModulePC; use wasmtime_environ::{ DefinedFuncIndex, EntityIndex, FrameInstPos, FrameStackShape, FrameStateSlot, FrameStateSlotOffset, FrameTableBreakpointData, FrameTableDescriptorIndex, FrameValType, @@ -54,7 +56,7 @@ impl Store { /// purposes. /// /// Guest debugging must be enabled for this accessor to return - /// any instances. If it is not, an empty vector is returend. + /// any instances. If it is not, an empty vector is returned. pub fn debug_all_instances(&mut self) -> Vec { self.as_store_opaque().debug_all_instances() } @@ -63,7 +65,7 @@ impl Store { /// purposes. /// /// Guest debugging must be enabled for this accessor to return - /// any modules. If it is not, an empty vector is returend. + /// any modules. If it is not, an empty vector is returned. pub fn debug_all_modules(&mut self) -> Vec { self.as_store_opaque().debug_all_modules() } @@ -346,7 +348,7 @@ impl Instance { // this instance as we fetched the instance directly from // the store above. let export = unsafe { instance.get_export_by_index_mut(registry, store_id, index) }; - Some(Extern::from_wasmtime_export(export, store)) + Some(Extern::from_wasmtime_export(export, store.engine())) } } @@ -537,13 +539,13 @@ impl FrameHandle { } /// Get the raw function index associated with the current frame, and the - /// PC as an offset within its code section, if it is a Wasm - /// function directly from the given `Module` (rather than a - /// trampoline). + /// module-relative PC as an offset within the module binary, if + /// this is a Wasm function directly from the given `Module` + /// (rather than a trampoline). pub fn wasm_function_index_and_pc( &self, mut store: impl AsContextMut, - ) -> Result> { + ) -> Result> { let mut store = store.as_context_mut(); let frame_data = self.frame_data(store.0.as_store_opaque())?; let FuncKey::DefinedWasmFunction(module, func) = frame_data.func_key else { @@ -659,8 +661,8 @@ impl FrameDataCache { /// This represents one frame as produced by the progpoint lookup /// (Wasm PC, frame descriptor index, stack shape). struct VirtualFrame { - /// The Wasm PC for this frame. - wasm_pc: u32, + /// The module-relative Wasm PC for this frame. + wasm_pc: ModulePC, /// The frame descriptor for this frame. frame_descriptor: FrameTableDescriptorIndex, /// The stack shape for this frame. @@ -697,7 +699,7 @@ impl VirtualFrame { struct FrameData { slot_to_fp_offset: usize, func_key: FuncKey, - wasm_pc: u32, + wasm_pc: ModulePC, /// Shape of locals in this frame. /// /// We need to store this locally because `FrameView` cannot @@ -943,23 +945,31 @@ pub trait DebugHandler: Clone + Send + Sync + 'static { pub(crate) struct BreakpointState { /// Single-step mode. single_step: bool, - /// Breakpoints added individually. - breakpoints: BTreeSet, + /// Breakpoints added individually. Maps from the actual + /// (possibly slipped-forward) breakpoint key to a reference + /// count. Multiple requested PCs may map to the same actual + /// breakpoint when they are slipped forward. + breakpoints: BTreeMap, + /// When a requested breakpoint PC does not exactly match an + /// opcode boundary, we "slip" it forward to the next available + /// PC. This map records the redirect from the requested key to + /// the actual key so that `remove_breakpoint` can undo it. + breakpoint_redirects: BTreeMap, } /// A breakpoint. pub struct Breakpoint { /// Reference to the module in which we are setting the breakpoint. pub module: Module, - /// Wasm PC offset within the module. - pub pc: u32, + /// Module-relative Wasm PC offset. + pub pc: ModulePC, } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct BreakpointKey(CompiledModuleId, u32); +struct BreakpointKey(CompiledModuleId, ModulePC); impl BreakpointKey { - fn from_raw(module: &Module, pc: u32) -> BreakpointKey { + fn from_raw(module: &Module, pc: ModulePC) -> BreakpointKey { BreakpointKey(module.id(), pc) } @@ -1003,7 +1013,7 @@ impl BreakpointState { &'a self, registry: &'a ModuleRegistry, ) -> impl Iterator + 'a { - self.breakpoints.iter().map(|key| key.get(registry)) + self.breakpoints.keys().map(|key| key.get(registry)) } pub(crate) fn is_single_step(&self) -> bool { @@ -1068,18 +1078,35 @@ impl<'a> BreakpointEdit<'a> { /// Add a breakpoint in the given module at the given PC in that /// module. /// + /// If the requested PC does not fall exactly on an opcode + /// boundary, the breakpoint is "slipped" forward to the next + /// available opcode PC. + /// /// No effect if the breakpoint is already set. - pub fn add_breakpoint(&mut self, module: &Module, pc: u32) -> Result<()> { - let key = BreakpointKey::from_raw(module, pc); - self.state.breakpoints.insert(key); - log::trace!("patching in breakpoint {key:?}"); - let mem = - Self::get_code_memory(self.state, self.registry, &mut self.dirty_modules, module)?; + pub fn add_breakpoint(&mut self, module: &Module, pc: ModulePC) -> Result<()> { let frame_table = module .frame_table() .expect("Frame table must be present when guest-debug is enabled"); - let patches = frame_table.lookup_breakpoint_patches_by_pc(pc); - Self::patch(patches, mem, true); + let actual_pc = frame_table.nearest_breakpoint(pc).unwrap_or(pc); + let requested_key = BreakpointKey::from_raw(module, pc); + let actual_key = BreakpointKey::from_raw(module, actual_pc); + + if actual_pc != pc { + log::trace!("slipping breakpoint from {requested_key:?} to {actual_key:?}"); + self.state + .breakpoint_redirects + .insert(requested_key, actual_key); + } + + let refcount = self.state.breakpoints.entry(actual_key).or_insert(0); + *refcount += 1; + if *refcount == 1 { + // First reference: actually patch the code. + let mem = + Self::get_code_memory(self.state, self.registry, &mut self.dirty_modules, module)?; + let patches = frame_table.lookup_breakpoint_patches_by_pc(actual_pc); + Self::patch(patches, mem, true); + } Ok(()) } @@ -1087,17 +1114,33 @@ impl<'a> BreakpointEdit<'a> { /// that module. /// /// No effect if the breakpoint was not set. - pub fn remove_breakpoint(&mut self, module: &Module, pc: u32) -> Result<()> { - let key = BreakpointKey::from_raw(module, pc); - self.state.breakpoints.remove(&key); - if !self.state.single_step { - let mem = - Self::get_code_memory(self.state, self.registry, &mut self.dirty_modules, module)?; - let frame_table = module - .frame_table() - .expect("Frame table must be present when guest-debug is enabled"); - let patches = frame_table.lookup_breakpoint_patches_by_pc(pc); - Self::patch(patches, mem, false); + pub fn remove_breakpoint(&mut self, module: &Module, pc: ModulePC) -> Result<()> { + let requested_key = BreakpointKey::from_raw(module, pc); + let actual_key = self + .state + .breakpoint_redirects + .remove(&requested_key) + .unwrap_or(requested_key); + let actual_pc = actual_key.1; + + if let Some(refcount) = self.state.breakpoints.get_mut(&actual_key) { + *refcount -= 1; + if *refcount == 0 { + self.state.breakpoints.remove(&actual_key); + if !self.state.single_step { + let mem = Self::get_code_memory( + self.state, + self.registry, + &mut self.dirty_modules, + module, + )?; + let frame_table = module + .frame_table() + .expect("Frame table must be present when guest-debug is enabled"); + let patches = frame_table.lookup_breakpoint_patches_by_pc(actual_pc); + Self::patch(patches, mem, false); + } + } } Ok(()) } @@ -1141,7 +1184,7 @@ impl<'a> BreakpointEdit<'a> { let mem = Self::get_code_memory(self.state, self.registry, &mut self.dirty_modules, &module)?; Self::apply_single_step(mem, &module, enabled, |key| { - self.state.breakpoints.contains(key) + self.state.breakpoints.contains_key(key) })?; } diff --git a/crates/wasmtime/src/runtime/externals.rs b/crates/wasmtime/src/runtime/externals.rs index 35c9eb91ee84..c290574ad510 100644 --- a/crates/wasmtime/src/runtime/externals.rs +++ b/crates/wasmtime/src/runtime/externals.rs @@ -127,13 +127,13 @@ impl Extern { pub(crate) fn from_wasmtime_export( wasmtime_export: crate::runtime::vm::Export, - store: &StoreOpaque, + engine: &Engine, ) -> Extern { match wasmtime_export { crate::runtime::vm::Export::Function(f) => Extern::Func(f), crate::runtime::vm::Export::Memory(m) => Extern::Memory(m), crate::runtime::vm::Export::SharedMemory(m, _) => { - Extern::SharedMemory(crate::SharedMemory::from_raw(m, store.engine().clone())) + Extern::SharedMemory(crate::SharedMemory::from_raw(m, engine.clone())) } crate::runtime::vm::Export::Global(g) => Extern::Global(g), crate::runtime::vm::Export::Table(t) => Extern::Table(t), diff --git a/crates/wasmtime/src/runtime/externals/table.rs b/crates/wasmtime/src/runtime/externals/table.rs index 18469b12f8a6..39b13e7bd5f4 100644 --- a/crates/wasmtime/src/runtime/externals/table.rs +++ b/crates/wasmtime/src/runtime/externals/table.rs @@ -346,7 +346,7 @@ impl Table { /// /// # Panics /// - /// This function will panic when if the store doens't own the table. + /// This function will panic when if the store doesn't own the table. #[cfg(feature = "async")] pub async fn grow_async( &self, diff --git a/crates/wasmtime/src/runtime/func.rs b/crates/wasmtime/src/runtime/func.rs index 66044e2de3af..d9d1079d03f9 100644 --- a/crates/wasmtime/src/runtime/func.rs +++ b/crates/wasmtime/src/runtime/func.rs @@ -372,12 +372,30 @@ impl Func { /// Panics if the given function type is not associated with this store's /// engine. pub fn new( - mut store: impl AsContextMut, + store: impl AsContextMut, ty: FuncType, func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, ) -> Self { + Self::try_new_(store, ty, func).panic_on_oom() + } + + /// Same as [`Func::new`] but returns an error instead of panicking on + /// allocation failure. + pub fn try_new( + store: impl AsContextMut, + ty: FuncType, + func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, + ) -> Result { + Ok(Self::try_new_(store, ty, func)?) + } + + fn try_new_( + mut store: impl AsContextMut, + ty: FuncType, + func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, + ) -> Result { let store = store.as_context_mut().0; - let host = HostFunc::new(store.engine(), ty, func).panic_on_oom(); + let host = HostFunc::new(store.engine(), ty, func)?; // SAFETY: the `T` used by `func` matches the `T` of the store we're // inserting into via this function's type signature. @@ -425,7 +443,7 @@ impl Func { // SAFETY: the `T` used by `func` matches the `T` of the store we're // inserting into via this function's type signature. - unsafe { host.into_func(store) } + unsafe { host.into_func(store).panic_on_oom() } } /// Creates a new host-defined WebAssembly function which, when called, @@ -516,7 +534,7 @@ impl Func { // SAFETY: the `T` used by `func` matches the `T` of the store we're // inserting into via this function's type signature. - unsafe { host.into_func(store) } + unsafe { host.into_func(store).panic_on_oom() } } /// Creates a new `Func` from a store and a funcref within that store. @@ -783,19 +801,33 @@ impl Func { /// # } /// ``` pub fn wrap( - mut store: impl AsContextMut, + store: impl AsContextMut, func: impl IntoFunc, ) -> Func + where + T: 'static, + { + Self::try_wrap(store, func).expect( + "allocation failure during `Func::wrap` (use `Func::try_wrap` to handle such errors)", + ) + } + + /// Fallible version of [`Func::wrap`] that returns an error on + /// out-of-memory instead of panicking. + pub fn try_wrap( + mut store: impl AsContextMut, + func: impl IntoFunc, + ) -> Result where T: 'static, { let store = store.as_context_mut().0; let engine = store.engine(); - let host = func.into_func(engine).panic_on_oom(); + let host = func.into_func(engine)?; // SAFETY: The `T` the closure takes is the same as the `T` of the store // we're inserting into via the type signature above. - unsafe { host.into_func(store) } + Ok(unsafe { host.into_func(store)? }) } /// Same as [`Func::wrap`], except the closure asynchronously produces the @@ -817,7 +849,7 @@ impl Func { // SAFETY: The `T` the closure takes is the same as the `T` of the store // we're inserting into via the type signature above. - unsafe { host.into_func(store) } + unsafe { host.into_func(store).panic_on_oom() } } /// Returns the underlying wasm type that this `Func` has. @@ -1129,7 +1161,7 @@ impl Func { let values_vec_size = params.len().max(ty.results().len()); let mut values_vec = store.0.take_wasm_val_raw_storage(); debug_assert!(values_vec.is_empty()); - values_vec.resize_with(values_vec_size, || ValRaw::v128(0)); + values_vec.resize_with(values_vec_size, || ValRaw::v128(0))?; for (arg, slot) in params.iter().cloned().zip(&mut values_vec) { *slot = arg.to_raw(&mut *store)?; } @@ -1516,9 +1548,12 @@ impl EntryStoreContext { // // After we've got the stack limit then we store it into the `stack_limit` // variable. - let wasm_stack_limit = stack_pointer - .checked_sub(store.engine().config().max_wasm_stack) - .unwrap(); + // + // Also note that `saturating_sub` is used here since if the user + // said that the function gets nigh-infinite stack well then by + // golly it'll get nigh-infinite stack in which case the limit is 0. + let wasm_stack_limit = + stack_pointer.saturating_sub(store.engine().config().max_wasm_stack); let prev_stack = unsafe { mem::replace( &mut *store.0.vm_store_context().stack_limit.get(), @@ -2158,7 +2193,7 @@ pub struct HostFunc { ctx: StoreBox, /// Whether or not this function was defined with an `async` host function, - /// meaning that it is only invokable when wasm is itself on a fiber to + /// meaning that it is only invocable when wasm is itself on a fiber to /// support suspension on `Poll::Pending`. /// /// This is used to propagate to an `InstancePre` and then eventually into a @@ -2625,13 +2660,13 @@ impl HostFunc { /// /// Can only be inserted into stores with a matching `T` relative to when /// this `HostFunc` was first created. - pub unsafe fn to_func(self: &Arc, store: &mut StoreOpaque) -> Func { + pub unsafe fn to_func(self: &Arc, store: &mut StoreOpaque) -> Result { self.validate_store(store); let (funcrefs, modules) = store.func_refs_and_modules(); - let funcref = funcrefs.push_arc_host(self.clone(), modules); + let funcref = funcrefs.push_arc_host(self.clone(), modules)?; // SAFETY: this funcref was just pushed within the store, so it's safe // to say this store owns it. - unsafe { Func::from_vm_func_ref(store.id(), funcref) } + Ok(unsafe { Func::from_vm_func_ref(store.id(), funcref) }) } /// Inserts this `HostFunc` into a `Store`, returning the `Func` pointing to @@ -2685,7 +2720,7 @@ impl HostFunc { } /// Same as [`HostFunc::to_func`], different ownership. - unsafe fn into_func(self, store: &mut StoreOpaque) -> Func { + unsafe fn into_func(self, store: &mut StoreOpaque) -> Result { self.validate_store(store); // This function could be called by a guest at any time, and it requires @@ -2693,10 +2728,10 @@ impl HostFunc { store.set_async_required(self.asyncness); let (funcrefs, modules) = store.func_refs_and_modules(); - let funcref = funcrefs.push_box_host(Box::new(self), modules); + let funcref = funcrefs.push_box_host(try_new::>(self)?, modules)?; // SAFETY: this funcref was just pushed within `store`, so it's safe to // say it's owned by the store's id. - unsafe { Func::from_vm_func_ref(store.id(), funcref) } + Ok(unsafe { Func::from_vm_func_ref(store.id(), funcref) }) } fn validate_store(&self, store: &mut StoreOpaque) { diff --git a/crates/wasmtime/src/runtime/gc/enabled/anyref.rs b/crates/wasmtime/src/runtime/gc/enabled/anyref.rs index 561cb6fbef33..497e5953974d 100644 --- a/crates/wasmtime/src/runtime/gc/enabled/anyref.rs +++ b/crates/wasmtime/src/runtime/gc/enabled/anyref.rs @@ -453,13 +453,16 @@ impl AnyRef { /// # Panics /// /// Panics if this reference is associated with a different store. - pub fn as_eqref(&self, store: impl AsContext) -> Result>> { - self._as_eqref(store.as_context().0) + pub fn as_eqref(&self, mut store: impl AsContextMut) -> Result>> { + self._as_eqref(store.as_context_mut().0) } - pub(crate) fn _as_eqref(&self, store: &StoreOpaque) -> Result>> { + pub(crate) fn _as_eqref(&self, store: &mut StoreOpaque) -> Result>> { if self._is_eqref(store)? { - Ok(Some(Rooted::from_gc_root_index(self.inner))) + let gc_ref = self.inner.try_gc_ref(store)?.unchecked_copy(); + let gc_ref = store.clone_gc_ref(&gc_ref); + let mut store = AutoAssertNoGc::new(store); + Ok(Some(Rooted::new(&mut store, gc_ref))) } else { Ok(None) } @@ -476,11 +479,11 @@ impl AnyRef { /// /// Panics if this reference is associated with a different store, or if /// this `anyref` is not an `eqref`. - pub fn unwrap_eqref(&self, store: impl AsContext) -> Result> { - self._unwrap_eqref(store.as_context().0) + pub fn unwrap_eqref(&self, mut store: impl AsContextMut) -> Result> { + self._unwrap_eqref(store.as_context_mut().0) } - pub(crate) fn _unwrap_eqref(&self, store: &StoreOpaque) -> Result> { + pub(crate) fn _unwrap_eqref(&self, store: &mut StoreOpaque) -> Result> { Ok(self ._as_eqref(store)? .expect("AnyRef::unwrap_eqref on non-eqref")) diff --git a/crates/wasmtime/src/runtime/instance.rs b/crates/wasmtime/src/runtime/instance.rs index 104d1d65d55c..0da714e47e4a 100644 --- a/crates/wasmtime/src/runtime/instance.rs +++ b/crates/wasmtime/src/runtime/instance.rs @@ -16,8 +16,7 @@ use alloc::sync::Arc; use core::ptr::NonNull; use wasmparser::WasmFeatures; use wasmtime_environ::{ - EntityIndex, EntityType, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, TableIndex, TagIndex, - TypeTrace, collections, + EntityIndex, EntityType, FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TagIndex, TypeTrace, }; /// An instantiated WebAssembly module. @@ -230,9 +229,9 @@ impl Instance { let (funcrefs, modules) = store.func_refs_and_modules(); funcrefs.fill(modules); - let mut owned_imports = OwnedImports::new(module); + let mut owned_imports = OwnedImports::new(module)?; for import in imports { - owned_imports.push(import, store); + owned_imports.push(import, store)?; } Ok(owned_imports) } @@ -408,29 +407,30 @@ impl Instance { /// /// # Panics /// - /// Panics if `store` does not own this instance. + /// Panics if `store` does not own this instance, or if memory allocation + /// fails. pub fn exports<'a, T: 'static>( &'a self, store: impl Into>, ) -> impl ExactSizeIterator> + 'a { - self._exports(store.into().0) - } - - fn _exports<'a>( - &'a self, - store: &'a mut StoreOpaque, - ) -> impl ExactSizeIterator> + 'a { - let module = store[self.id].env_module().clone(); - let mut items = Vec::new(); - for (_name, entity) in module.exports.iter() { - items.push(self._get_export(store, *entity)); - } - let module = store[self.id].env_module(); - module - .exports - .iter() - .zip(items) - .map(|((name, _), item)| Export::new(&module.strings[name], item)) + let store = store.into().0; + let store_id = store.id(); + let engine = store.engine().clone(); + + let (instance, registry) = store.instance_and_module_registry_mut(self.id()); + let (module, mut instance) = instance.module_and_self(); + module.exports.iter().map(move |(name, entity)| { + // SAFETY: the `store_id` owns this instance and all exports + // contained within. + let export = unsafe { + instance + .as_mut() + .get_export_by_index_mut(registry, store_id, *entity) + }; + + let ext = Extern::from_wasmtime_export(export, &engine); + Export::new(&module.strings[name], ext) + }) } /// Looks up an exported [`Extern`] value by name. @@ -495,7 +495,7 @@ impl Instance { let (instance, registry) = self.id.get_mut_and_module_registry(store); instance.get_export_by_index_mut(registry, id, entity) }; - Extern::from_wasmtime_export(export, store) + Extern::from_wasmtime_export(export, store.engine()) } /// Looks up an exported [`Func`] value by name. @@ -657,37 +657,38 @@ impl Instance { } pub(crate) struct OwnedImports { - functions: PrimaryMap, - tables: PrimaryMap, - memories: PrimaryMap, - globals: PrimaryMap, - tags: PrimaryMap, + functions: TryPrimaryMap, + tables: TryPrimaryMap, + memories: TryPrimaryMap, + globals: TryPrimaryMap, + tags: TryPrimaryMap, } impl OwnedImports { - fn new(module: &Module) -> OwnedImports { + fn new(module: &Module) -> Result { let mut ret = OwnedImports::empty(); - ret.reserve(module); - return ret; + ret.reserve(module)?; + Ok(ret) } pub(crate) fn empty() -> OwnedImports { OwnedImports { - functions: PrimaryMap::new(), - tables: PrimaryMap::new(), - memories: PrimaryMap::new(), - globals: PrimaryMap::new(), - tags: PrimaryMap::new(), + functions: TryPrimaryMap::new(), + tables: TryPrimaryMap::new(), + memories: TryPrimaryMap::new(), + globals: TryPrimaryMap::new(), + tags: TryPrimaryMap::new(), } } - pub(crate) fn reserve(&mut self, module: &Module) { + pub(crate) fn reserve(&mut self, module: &Module) -> Result<(), OutOfMemory> { let raw = module.compiled_module().module(); - self.functions.reserve(raw.num_imported_funcs); - self.tables.reserve(raw.num_imported_tables); - self.memories.reserve(raw.num_imported_memories); - self.globals.reserve(raw.num_imported_globals); - self.tags.reserve(raw.num_imported_tags); + self.functions.reserve(raw.num_imported_funcs)?; + self.tables.reserve(raw.num_imported_tables)?; + self.memories.reserve(raw.num_imported_memories)?; + self.globals.reserve(raw.num_imported_globals)?; + self.tags.reserve(raw.num_imported_tags)?; + Ok(()) } #[cfg(feature = "component-model")] @@ -699,33 +700,38 @@ impl OwnedImports { self.tags.clear(); } - fn push(&mut self, item: &Extern, store: &mut StoreOpaque) { + fn push(&mut self, item: &Extern, store: &mut StoreOpaque) -> Result<(), OutOfMemory> { match item { Extern::Func(i) => { - self.functions.push(i.vmimport(store)); + self.functions.push(i.vmimport(store))?; } Extern::Global(i) => { - self.globals.push(i.vmimport(store)); + self.globals.push(i.vmimport(store))?; } Extern::Table(i) => { - self.tables.push(i.vmimport(store)); + self.tables.push(i.vmimport(store))?; } Extern::Memory(i) => { - self.memories.push(i.vmimport(store)); + self.memories.push(i.vmimport(store))?; } Extern::SharedMemory(i) => { - self.memories.push(i.vmimport(store)); + self.memories.push(i.vmimport(store))?; } Extern::Tag(i) => { - self.tags.push(i.vmimport(store)); + self.tags.push(i.vmimport(store))?; } } + Ok(()) } /// Note that this is unsafe as the validity of `item` is not verified and /// it contains a bunch of raw pointers. #[cfg(feature = "component-model")] - pub(crate) fn push_export(&mut self, store: &StoreOpaque, item: &crate::runtime::vm::Export) { + pub(crate) fn push_export( + &mut self, + store: &StoreOpaque, + item: &crate::runtime::vm::Export, + ) -> Result<(), OutOfMemory> { match item { crate::runtime::vm::Export::Function(f) => { // SAFETY: the funcref associated with a `Func` is valid to use @@ -735,24 +741,25 @@ impl OwnedImports { wasm_call: f.wasm_call.unwrap(), array_call: f.array_call, vmctx: f.vmctx, - }); + })?; } crate::runtime::vm::Export::Global(g) => { - self.globals.push(g.vmimport(store)); + self.globals.push(g.vmimport(store))?; } crate::runtime::vm::Export::Table(t) => { - self.tables.push(t.vmimport(store)); + self.tables.push(t.vmimport(store))?; } crate::runtime::vm::Export::Memory(m) => { - self.memories.push(m.vmimport(store)); + self.memories.push(m.vmimport(store))?; } crate::runtime::vm::Export::SharedMemory(_, vmimport) => { - self.memories.push(*vmimport); + self.memories.push(*vmimport)?; } crate::runtime::vm::Export::Tag(t) => { - self.tags.push(t.vmimport(store)); + self.tags.push(t.vmimport(store))?; } } + Ok(()) } pub(crate) fn as_ref(&self) -> Imports<'_> { @@ -790,7 +797,7 @@ pub struct InstancePre { /// Note that this is stored as an `Arc` to quickly move a strong reference /// to everything internally into a `Store` without having to clone each /// individual item. - items: Arc>, + items: Arc>, /// A count of `Definition::HostFunc` entries in `items` above to /// preallocate space in a `Store` up front for all entries to be inserted. @@ -802,7 +809,7 @@ pub struct InstancePre { /// instantiation time. /// /// This is an `Arc` for the same reason as `items`. - func_refs: Arc>, + func_refs: Arc>, /// Whether or not any import in `items` is flagged as needing async. /// @@ -836,13 +843,10 @@ impl InstancePre { /// This method is unsafe as the `T` of the `InstancePre` is not /// guaranteed to be the same as the `T` within the `Store`, the caller must /// verify that. - pub(crate) unsafe fn new( - module: &Module, - items: collections::Vec, - ) -> Result> { + pub(crate) unsafe fn new(module: &Module, items: TryVec) -> Result> { typecheck(module, &items, |cx, ty, item| cx.definition(ty, &item.ty()))?; - let mut func_refs = collections::Vec::with_capacity(items.len())?; + let mut func_refs = TryVec::with_capacity(items.len())?; let mut host_funcs = 0; let mut asyncness = Asyncness::No; for item in &items { @@ -962,9 +966,9 @@ impl InstancePre { fn pre_instantiate_raw( store: &mut StoreOpaque, module: &Module, - items: &Arc>, + items: &Arc>, host_funcs: usize, - func_refs: &Arc>, + func_refs: &Arc>, asyncness: Asyncness, ) -> Result { // Register this module and use it to fill out any funcref wasm_call holes @@ -979,20 +983,20 @@ fn pre_instantiate_raw( // will insert a function into the store automatically as part of // instantiation, so reserve space here to make insertion more efficient // as it won't have to realloc during the instantiation. - funcrefs.reserve_storage(host_funcs); + funcrefs.reserve_storage(host_funcs)?; // The usage of `to_extern_store_rooted` requires that the items are // rooted via another means, which happens here by cloning the list of // items into the store once. This avoids cloning each individual item // below. - funcrefs.push_instance_pre_definitions(items.clone()); - funcrefs.push_instance_pre_func_refs(func_refs.clone()); + funcrefs.push_instance_pre_definitions(items.clone())?; + funcrefs.push_instance_pre_func_refs(func_refs.clone())?; } store.set_async_required(asyncness); let mut func_refs = func_refs.iter().map(|f| NonNull::from(f)); - let mut imports = OwnedImports::new(module); + let mut imports = OwnedImports::new(module)?; for import in items.iter() { if !import.comes_from_same_store(store) { bail!("cross-`Store` instantiation is not currently supported"); @@ -1015,7 +1019,7 @@ fn pre_instantiate_raw( .into() }, }; - imports.push(&item, store); + imports.push(&item, store)?; } Ok(imports) diff --git a/crates/wasmtime/src/runtime/linker.rs b/crates/wasmtime/src/runtime/linker.rs index 27c6ca17d6fb..35e5cb16c01c 100644 --- a/crates/wasmtime/src/runtime/linker.rs +++ b/crates/wasmtime/src/runtime/linker.rs @@ -13,10 +13,7 @@ use core::future::Future; use core::marker; use core::mem::MaybeUninit; use log::warn; -use wasmtime_environ::{ - Atom, StringPool, - collections::{HashMap, Vec}, -}; +use wasmtime_environ::{Atom, PanicOnOom, StringPool}; /// Structure used to link wasm modules/instances together. /// @@ -87,7 +84,7 @@ use wasmtime_environ::{ pub struct Linker { engine: Engine, pool: StringPool, - map: HashMap, + map: TryHashMap, allow_shadowing: bool, allow_unknown_exports: bool, _marker: marker::PhantomData T>, @@ -163,7 +160,7 @@ impl Linker { pub fn new(engine: &Engine) -> Linker { Linker { engine: engine.clone(), - map: HashMap::new(), + map: TryHashMap::new(), pool: StringPool::new(), allow_shadowing: false, allow_unknown_exports: false, @@ -636,7 +633,7 @@ impl Linker { T: 'static, { let mut store = store.as_context_mut(); - let exports: Vec<_> = instance + let exports: TryVec<_> = instance .exports(&mut store) .map(|e| { Ok(( @@ -991,7 +988,7 @@ impl Linker { pub fn alias_module(&mut self, module: &str, as_module: &str) -> Result<()> { let module = self.pool.insert(module)?; let as_module = self.pool.insert(as_module)?; - let items: Vec<_> = self + let items: TryVec<_> = self .map .iter() .filter(|(key, _def)| key.module == module) @@ -1180,7 +1177,7 @@ impl Linker { where T: 'static, { - let mut imports: Vec<_> = module + let mut imports: TryVec<_> = module .imports() .map(|import| Ok(self._get_by_import(&import)?)) .try_collect::<_, Error>()?; @@ -1219,7 +1216,7 @@ impl Linker { &self.pool[key.module], &self.pool[key.name.unwrap()], // Should be safe since `T` is connecting the linker and store - unsafe { item.to_extern(store.0) }, + unsafe { item.to_extern(store.0).panic_on_oom() }, ) }) } @@ -1227,7 +1224,7 @@ impl Linker { /// Looks up a previously defined value in this [`Linker`], identified by /// the names provided. /// - /// Returns `None` if this name was not previously defined in this + /// Returns an error if this name was not previously defined in this /// [`Linker`]. /// /// # Panics @@ -1239,13 +1236,16 @@ impl Linker { mut store: impl AsContextMut, module: &str, name: &str, - ) -> Option + ) -> Result where T: 'static, { let store = store.as_context_mut().0; - // Should be safe since `T` is connecting the linker and store - Some(unsafe { self._get(module, name)?.to_extern(store) }) + match self._get(module, name) { + // Safety: `T` is connecting the linker and store. + Some(def) => Ok(unsafe { def.to_extern(store)? }), + None => bail!("missing definition for `{module}::{name}`"), + } } fn _get(&self, module: &str, name: &str) -> Option<&Definition> { @@ -1267,15 +1267,32 @@ impl Linker { /// same [`Engine`] that this linker was created with. pub fn get_by_import( &self, - mut store: impl AsContextMut, + store: impl AsContextMut, import: &ImportType, ) -> Option + where + T: 'static, + { + self.try_get_by_import(store, import) + .expect("out of memory") + } + + /// Same as [`Linker::get_by_import`] but returns an error instead of + /// panicking on allocation failure. + pub fn try_get_by_import( + &self, + mut store: impl AsContextMut, + import: &ImportType, + ) -> Result> where T: 'static, { let store = store.as_context_mut().0; - // Should be safe since `T` is connecting the linker and store - Some(unsafe { self._get_by_import(import).ok()?.to_extern(store) }) + match self._get_by_import(import) { + // Should be safe since `T` is connecting the linker and store + Ok(def) => Ok(Some(unsafe { def.to_extern(store)? })), + Err(_) => Ok(None), + } } fn _get_by_import(&self, import: &ImportType) -> Result { @@ -1299,7 +1316,13 @@ impl Linker { where T: 'static, { - if let Some(external) = self.get(&mut store, module, "") { + if let Some(external) = self.get(&mut store, module, "").map(Some).or_else(|e| { + if e.is::() { + Err(e) + } else { + Ok(None) + } + })? { if let Extern::Func(func) = external { return Ok(func); } @@ -1307,7 +1330,17 @@ impl Linker { } // For compatibility, also recognize "_start". - if let Some(external) = self.get(&mut store, module, "_start") { + if let Some(external) = self + .get(&mut store, module, "_start") + .map(Some) + .or_else(|e| { + if e.is::() { + Err(e) + } else { + Ok(None) + } + })? + { if let Extern::Func(func) = external { return Ok(func); } @@ -1345,13 +1378,13 @@ impl Definition { /// Note the unsafety here is due to calling `HostFunc::to_func`. The /// requirement here is that the `T` that was originally used to create the /// `HostFunc` matches the `T` on the store. - pub(crate) unsafe fn to_extern(&self, store: &mut StoreOpaque) -> Extern { + pub(crate) unsafe fn to_extern(&self, store: &mut StoreOpaque) -> Result { match self { - Definition::Extern(e, _) => e.clone(), + Definition::Extern(e, _) => Ok(e.clone()), // SAFETY: the contract of this function is the same as what's // required of `to_func`, that `T` of the store matches the `T` of // this original definition. - Definition::HostFunc(func) => unsafe { func.to_func(store).into() }, + Definition::HostFunc(func) => unsafe { Ok(func.to_func(store)?.into()) }, } } diff --git a/crates/wasmtime/src/runtime/module.rs b/crates/wasmtime/src/runtime/module.rs index ada8d58a061e..beb5aa1f5965 100644 --- a/crates/wasmtime/src/runtime/module.rs +++ b/crates/wasmtime/src/runtime/module.rs @@ -523,7 +523,7 @@ impl Module { // Package up all our data into an `EngineCode` and delegate to the final // step of module compilation. - let code = try_new::>(EngineCode::new(code_memory, signatures, types.into()))?; + let code = try_new::>(EngineCode::new(code_memory, signatures, types.into())?)?; let index = try_new::>(index)?; Module::from_parts_raw(engine, code, info, index, true) } diff --git a/crates/wasmtime/src/runtime/module/registry.rs b/crates/wasmtime/src/runtime/module/registry.rs index 3e0d32918331..c16d12cb6c56 100644 --- a/crates/wasmtime/src/runtime/module/registry.rs +++ b/crates/wasmtime/src/runtime/module/registry.rs @@ -6,15 +6,13 @@ use crate::component::Component; use crate::runtime::vm::VMWasmCallFunction; use crate::sync::{OnceLock, RwLock}; use crate::vm::CompiledModuleId; -use crate::{Engine, prelude::*}; -use crate::{FrameInfo, Module, code_memory::CodeMemory}; -use alloc::collections::btree_map::{BTreeMap, Entry}; +use crate::{Engine, FrameInfo, Module, code_memory::CodeMemory, prelude::*}; use alloc::sync::Arc; #[cfg(not(feature = "debug"))] use core::marker::PhantomData; use core::ops::Range; use core::ptr::NonNull; -use wasmtime_environ::VMSharedTypeIndex; +use wasmtime_environ::{VMSharedTypeIndex, collections::btree_map::Entry}; /// Used for registering modules with a store. /// @@ -63,18 +61,18 @@ pub struct ModuleRegistry { /// then take the last element of the range. That picks the /// highest start address <= the query, and we can check whether /// it contains the address. - loaded_code: BTreeMap, + loaded_code: TryBTreeMap, /// Map from EngineCodePC start to StoreCodePC start. We use this /// to memoize the store-code creation process: each EngineCode is /// instantiated to a StoreCode only once per store. - store_code: BTreeMap, + store_code: TryBTreeMap, /// Modules instantiated in this registry. /// /// Every module is placed in this map, but not every module will /// be in a LoadedCode entry, because the module may have no text. - modules: BTreeMap, + modules: TryBTreeMap, } struct LoadedCode { @@ -82,7 +80,7 @@ struct LoadedCode { code: StoreCode, /// Map by starting text offset of Modules in this code region. - modules: BTreeMap, + modules: TryBTreeMap, } /// An identifier of a module that has previously been inserted into a @@ -93,9 +91,12 @@ struct LoadedCode { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct RegisteredModuleId(CompiledModuleId); -fn assert_no_overlap(loaded_code: &BTreeMap, range: Range) { +fn assert_no_overlap( + loaded_code: &TryBTreeMap, + range: Range, +) { if let Some((start, _)) = loaded_code.range(range.start..).next() { - assert!(*start >= range.end); + assert!(start >= range.end); } if let Some((_, code)) = loaded_code.range(..range.end).next_back() { assert!(code.code.text_range().end <= range.start); @@ -121,12 +122,12 @@ impl<'a> RegisterBreakpointState<'a> { impl ModuleRegistry { /// Get a previously-registered module by id. pub fn module_by_id(&self, id: RegisteredModuleId) -> Option<&Module> { - self.modules.get(&id) + self.modules.get(id) } /// Get a module by CompiledModuleId, if present. pub fn module_by_compiled_id(&self, id: CompiledModuleId) -> Option<&Module> { - self.modules.get(&RegisteredModuleId(id)) + self.modules.get(RegisteredModuleId(id)) } /// Fetches a registered StoreCode and module and an offset within @@ -138,7 +139,7 @@ impl ModuleRegistry { .next_back()?; let offset = StoreCodePC::offset_of(code.code.text_range(), pc)?; let (_, module_id) = code.modules.range(..=offset).next_back()?; - let module = self.modules.get(&module_id)?; + let module = self.modules.get(*module_id)?; Some((ModuleWithCode::from_raw(module, &code.code), offset)) } @@ -151,9 +152,7 @@ impl ModuleRegistry { /// Fetches the base `StoreCodePC` for a given `EngineCode`. pub fn store_code_base(&self, engine_code: &EngineCode) -> Option { - self.store_code - .get(&engine_code.text_range().start) - .cloned() + self.store_code.get(engine_code.text_range().start).cloned() } /// Fetches the base `StoreCodePC` for a given `EngineCode` with @@ -164,11 +163,11 @@ impl ModuleRegistry { breakpoint_state: RegisterBreakpointState, ) -> Result { let key = module.engine_code().text_range().start; - if !self.store_code.contains_key(&key) { + if !self.store_code.contains_key(key) { let engine = module.engine().clone(); self.register_module(module, &engine, breakpoint_state)?; } - Ok(*self.store_code.get(&key).unwrap()) + Ok(*self.store_code.get(key).unwrap()) } /// Fetches a mutable `StoreCode` for a given base `StoreCodePC`. @@ -228,11 +227,13 @@ impl ModuleRegistry { breakpoint_state: RegisterBreakpointState, ) -> Result> { // Register the module, if any. - let id = module.map(|module| { + let id = if let Some(module) = module { let id = RegisteredModuleId(compiled_id); - self.modules.entry(id).or_insert_with(|| module.clone()); - id - }); + self.modules.entry(id).or_insert_with(|| module.clone())?; + Some(id) + } else { + None + }; // Create a StoreCode if one does not already exist. let store_code_pc = match self.store_code.entry(code.text_range().start) { @@ -244,10 +245,10 @@ impl ModuleRegistry { store_code_pc, LoadedCode { code: store_code, - modules: BTreeMap::default(), + modules: TryBTreeMap::default(), }, - ); - *v.insert(store_code_pc) + )?; + *v.insert(store_code_pc)? } Entry::Occupied(o) => *o.get(), }; @@ -257,9 +258,9 @@ impl ModuleRegistry { if let Some((_, range)) = module.compiled_module().finished_function_ranges().next() { let loaded_code = self .loaded_code - .get_mut(&store_code_pc) + .get_mut(store_code_pc) .expect("loaded_code must have entry for StoreCodePC"); - loaded_code.modules.insert(range.start, id); + loaded_code.modules.insert(range.start, id)?; breakpoint_state.update(&mut loaded_code.code, module)?; } } @@ -287,7 +288,7 @@ impl ModuleRegistry { let (_, module_id) = code.modules.range(..=text_offset).next_back()?; let module = self .modules - .get(&module_id) + .get(*module_id) .expect("referenced module ID not found"); let info = FrameInfo::new(module.clone(), text_offset)?; let module_with_code = ModuleWithCode::from_raw(module, &code.code); @@ -332,7 +333,7 @@ fn global_code() -> &'static RwLock { GLOBAL_CODE.get_or_init(Default::default) } -type GlobalRegistry = BTreeMap)>; +type GlobalRegistry = TryBTreeMap)>; /// Find which registered region of code contains the given program counter, and /// what offset that PC is within that module's code. @@ -351,14 +352,15 @@ pub fn lookup_code(pc: usize) -> Option<(Arc, usize)> { /// This is required to enable traps to work correctly since the signal handler /// will lookup in the `GLOBAL_CODE` list to determine which a particular pc /// is a trap or not. -pub fn register_code(image: &Arc, address: Range) { +pub fn register_code(image: &Arc, address: Range) -> Result<(), OutOfMemory> { if address.is_empty() { - return; + return Ok(()); } let start = address.start; let end = address.end - 1; - let prev = global_code().write().insert(end, (start, image.clone())); + let prev = global_code().write().insert(end, (start, image.clone()))?; assert!(prev.is_none()); + Ok(()) } /// Unregisters a code mmap from the global map. @@ -369,7 +371,7 @@ pub fn unregister_code(address: Range) { return; } let end = address.end - 1; - let code = global_code().write().remove(&end); + let code = global_code().write().remove(end); assert!(code.is_some()); } diff --git a/crates/wasmtime/src/runtime/native_debug.rs b/crates/wasmtime/src/runtime/native_debug.rs index e241b6617851..c3045db3424c 100644 --- a/crates/wasmtime/src/runtime/native_debug.rs +++ b/crates/wasmtime/src/runtime/native_debug.rs @@ -5,7 +5,7 @@ use object::endian::{BigEndian, Endian, Endianness, LittleEndian}; use object::read::elf::{FileHeader, SectionHeader}; use object::{ File, NativeEndian as NE, Object, ObjectSection, ObjectSymbol, RelocationEncoding, - RelocationKind, RelocationTarget, U64Bytes, + RelocationKind, RelocationTarget, U64, }; use wasmtime_environ::obj; @@ -67,7 +67,7 @@ fn relocate_dwarf_sections(bytes: &mut [u8], code_region: (*const u8, usize)) -> let (loc, _) = offset .try_into() .ok() - .and_then(|offset| object::from_bytes_mut::>(&mut bytes[offset..]).ok()) + .and_then(|offset| object::from_bytes_mut::>(&mut bytes[offset..]).ok()) .ok_or_else(|| format_err!("invalid dwarf relocations"))?; loc.set(NE, value); } diff --git a/crates/wasmtime/src/runtime/store.rs b/crates/wasmtime/src/runtime/store.rs index 97e2fd5ba2ae..557428e1e76d 100644 --- a/crates/wasmtime/src/runtime/store.rs +++ b/crates/wasmtime/src/runtime/store.rs @@ -112,7 +112,7 @@ use core::num::NonZeroU64; use core::ops::{Deref, DerefMut}; use core::pin::Pin; use core::ptr::NonNull; -use wasmtime_environ::{DefinedGlobalIndex, DefinedTableIndex, EntityRef, PrimaryMap, TripleExt}; +use wasmtime_environ::{DefinedGlobalIndex, DefinedTableIndex, EntityRef, TripleExt}; mod context; pub use self::context::*; @@ -473,12 +473,12 @@ pub struct StoreOpaque { #[cfg(feature = "stack-switching")] continuations: Vec>, - instances: wasmtime_environ::collections::PrimaryMap, + instances: TryPrimaryMap, signal_handler: Option, modules: ModuleRegistry, func_refs: FuncRefs, - host_globals: PrimaryMap>, + host_globals: TryPrimaryMap>, // GC-related fields. gc_store: Option, gc_roots: RootSet, @@ -527,7 +527,7 @@ pub struct StoreOpaque { hostcall_val_storage: Vec, /// Same as `hostcall_val_storage`, but for the direction of the host /// calling wasm. - wasm_val_raw_storage: Vec, + wasm_val_raw_storage: TryVec, /// Keep track of what protection key is being used during allocation so /// that the right memory pages can be enabled when entering WebAssembly @@ -744,7 +744,7 @@ impl Store { vm_store_context: Default::default(), #[cfg(feature = "stack-switching")] continuations: Vec::new(), - instances: wasmtime_environ::collections::PrimaryMap::new(), + instances: TryPrimaryMap::new(), signal_handler: None, gc_store: None, gc_roots: RootSet::default(), @@ -756,7 +756,7 @@ impl Store { pending_exception: None, modules: ModuleRegistry::default(), func_refs: FuncRefs::default(), - host_globals: PrimaryMap::new(), + host_globals: TryPrimaryMap::new(), instance_count: 0, instance_limit: crate::DEFAULT_INSTANCE_LIMIT, memory_count: 0, @@ -771,7 +771,7 @@ impl Store { traitobj: StorePtr(None), default_caller_vmctx: SendSyncPtr::new(NonNull::dangling()), hostcall_val_storage: Vec::new(), - wasm_val_raw_storage: Vec::new(), + wasm_val_raw_storage: TryVec::new(), pkey, executor: Executor::new(engine)?, #[cfg(feature = "debug")] @@ -1306,6 +1306,35 @@ impl Store { pub fn clear_debug_handler(&mut self) { self.inner.debug_handler = None; } + + /// Register a [`Module`] with this store's module registry for + /// debugging, without instantiating it. + /// + /// This makes the module visible to debuggers (via + /// `debug_all_modules`) before the module is actually + /// instantiated. This is useful for guest-debug workflows where + /// the debugger needs to see modules to set breakpoints before + /// the first Wasm instruction executes. + #[cfg(feature = "debug")] + pub fn debug_register_module(&mut self, module: &crate::Module) -> crate::Result<()> { + let (modules, engine, breakpoints) = self.inner.modules_and_engine_and_breakpoints_mut(); + modules.register_module(module, engine, breakpoints)?; + Ok(()) + } + + /// Register all inner modules of a [`Component`](crate::component::Component) + /// with this store's module registry for debugging, without instantiating + /// the component. + #[cfg(all(feature = "debug", feature = "component-model"))] + pub fn debug_register_component( + &mut self, + component: &crate::component::Component, + ) -> crate::Result<()> { + for module in component.static_modules() { + self.debug_register_module(module)?; + } + Ok(()) + } } impl<'a, T> StoreContext<'a, T> { @@ -1536,7 +1565,7 @@ impl StoreInner { // noop shim so code can assume this always exists. } - /// Splits this `StoreInner` into a `limiter`/`StoerOpaque` borrow while + /// Splits this `StoreInner` into a `limiter`/`StoreOpaque` borrow while /// validating that an async limiter is not configured. /// /// This is used for sync entrypoints which need to fail if an async limiter @@ -1687,13 +1716,13 @@ impl StoreOpaque { pub(crate) fn host_globals( &self, - ) -> &PrimaryMap> { + ) -> &TryPrimaryMap> { &self.host_globals } pub(crate) fn host_globals_mut( &mut self, - ) -> &mut PrimaryMap> { + ) -> &mut TryPrimaryMap> { &mut self.host_globals } @@ -2377,14 +2406,14 @@ impl StoreOpaque { /// Same as `take_hostcall_val_storage`, but for the direction of the host /// calling wasm. #[inline] - pub fn take_wasm_val_raw_storage(&mut self) -> Vec { + pub fn take_wasm_val_raw_storage(&mut self) -> TryVec { mem::take(&mut self.wasm_val_raw_storage) } /// Same as `save_hostcall_val_storage`, but for the direction of the host /// calling wasm. #[inline] - pub fn save_wasm_val_raw_storage(&mut self, storage: Vec) { + pub fn save_wasm_val_raw_storage(&mut self, storage: TryVec) { if storage.capacity() > self.wasm_val_raw_storage.capacity() { self.wasm_val_raw_storage = storage; } diff --git a/crates/wasmtime/src/runtime/store/func_refs.rs b/crates/wasmtime/src/runtime/store/func_refs.rs index d212553392a5..1c16ebd7560a 100644 --- a/crates/wasmtime/src/runtime/store/func_refs.rs +++ b/crates/wasmtime/src/runtime/store/func_refs.rs @@ -7,8 +7,8 @@ use crate::prelude::*; use crate::runtime::HostFunc; use crate::runtime::vm::{AlwaysMut, SendSyncPtr, VMArrayCallHostFuncContext, VMFuncRef}; use alloc::sync::Arc; +use core::mem::size_of; use core::ptr::NonNull; -use wasmtime_environ::collections; /// An arena of `VMFuncRef`s. /// @@ -23,11 +23,11 @@ pub struct FuncRefs { /// Pointers into `self.bump` for entries that need `wasm_call` field filled /// in. - with_holes: Vec>, + with_holes: TryVec>, /// General-purpose storage of "function things" that need to live as long /// as the entire store. - storage: Vec, + storage: TryVec, } /// Various items to place in `FuncRefs::storage` @@ -40,7 +40,7 @@ enum Storage { /// inside them, etc. InstancePreDefinitions { #[expect(dead_code, reason = "only here to keep the original value alive")] - defs: Arc>, + defs: Arc>, }, /// Pinned `VMFuncRef`s that had their `wasm_call` field @@ -48,7 +48,7 @@ enum Storage { /// keep alive for our owning store's lifetime. InstancePreFuncRefs { #[expect(dead_code, reason = "only here to keep the original value alive")] - funcs: Arc>, + funcs: Arc>, }, /// A uniquely-owned host function within a `Store`. This comes about with @@ -91,17 +91,21 @@ impl FuncRefs { &mut self, func_ref: VMFuncRef, modules: &ModuleRegistry, - ) -> NonNull { + ) -> Result, OutOfMemory> { debug_assert!(func_ref.wasm_call.is_none()); - let func_ref = self.bump.get_mut().alloc(func_ref); + let func_ref = self + .bump + .get_mut() + .try_alloc(func_ref) + .map_err(|_| OutOfMemory::new(size_of::()))?; // SAFETY: it's a contract of this function itself that `func_ref` has a // valid vmctx field to read. let has_hole = unsafe { !try_fill(func_ref, modules) }; let unpatched = SendSyncPtr::from(func_ref); if has_hole { - self.with_holes.push(unpatched); + self.with_holes.push(unpatched)?; } - unpatched.as_non_null() + Ok(unpatched.as_non_null()) } /// Patch any `VMFuncRef::wasm_call`s that need filling in. @@ -111,16 +115,19 @@ impl FuncRefs { } /// Reserves `amt` space for extra items in "storage" for this store. - pub fn reserve_storage(&mut self, amt: usize) { - self.storage.reserve(amt); + pub fn reserve_storage(&mut self, amt: usize) -> Result<(), OutOfMemory> { + self.storage.reserve(amt) } /// Push pre-patched `VMFuncRef`s from an `InstancePre`. /// /// This is used to ensure that the store itself persists the entire list of /// `funcs` for the entire lifetime of the store. - pub fn push_instance_pre_func_refs(&mut self, funcs: Arc>) { - self.storage.push(Storage::InstancePreFuncRefs { funcs }); + pub fn push_instance_pre_func_refs( + &mut self, + funcs: Arc>, + ) -> Result<(), OutOfMemory> { + self.storage.push(Storage::InstancePreFuncRefs { funcs }) } /// Push linker definitions into storage, keeping them alive for the entire @@ -128,8 +135,11 @@ impl FuncRefs { /// /// This is used to keep linker-defined functions' vmctx values alive, for /// example. - pub fn push_instance_pre_definitions(&mut self, defs: Arc>) { - self.storage.push(Storage::InstancePreDefinitions { defs }); + pub fn push_instance_pre_definitions( + &mut self, + defs: Arc>, + ) -> Result<(), OutOfMemory> { + self.storage.push(Storage::InstancePreDefinitions { defs }) } /// Pushes a shared host function into this store. @@ -148,12 +158,12 @@ impl FuncRefs { &mut self, func: Arc, modules: &ModuleRegistry, - ) -> NonNull { + ) -> Result, OutOfMemory> { debug_assert!(func.func_ref().wasm_call.is_none()); // SAFETY: the vmctx field in the funcref of `HostFunc` is safe to read. - let ret = unsafe { self.push(func.func_ref().clone(), modules) }; - self.storage.push(Storage::ArcHost { func }); - ret + let ret = unsafe { self.push(func.func_ref().clone(), modules)? }; + self.storage.push(Storage::ArcHost { func })?; + Ok(ret) } /// Same as `push_arc_host`, but for owned host functions. @@ -161,12 +171,12 @@ impl FuncRefs { &mut self, func: Box, modules: &ModuleRegistry, - ) -> NonNull { + ) -> Result, OutOfMemory> { debug_assert!(func.func_ref().wasm_call.is_none()); // SAFETY: the vmctx field in the funcref of `HostFunc` is safe to read. - let ret = unsafe { self.push(func.func_ref().clone(), modules) }; - self.storage.push(Storage::BoxHost { func }); - ret + let ret = unsafe { self.push(func.func_ref().clone(), modules)? }; + self.storage.push(Storage::BoxHost { func })?; + Ok(ret) } } diff --git a/crates/wasmtime/src/runtime/store/gc.rs b/crates/wasmtime/src/runtime/store/gc.rs index 10e1805d08ad..465686657838 100644 --- a/crates/wasmtime/src/runtime/store/gc.rs +++ b/crates/wasmtime/src/runtime/store/gc.rs @@ -56,11 +56,16 @@ impl StoreOpaque { bytes_needed: Option, asyncness: Asyncness, ) { - if let Some(n) = bytes_needed { + if let Some(n) = bytes_needed + // The gc_zeal's allocation counter will pass `bytes_needed == 0` to + // signify that we shouldn't grow the GC heap, just do a collection. + && n > 0 + { if self.grow_gc_heap(limiter, n).await.is_ok() { return; } } + self.do_gc(asyncness).await; } diff --git a/crates/wasmtime/src/runtime/trampoline/global.rs b/crates/wasmtime/src/runtime/trampoline/global.rs index 58e22dd3a902..169c284c535c 100644 --- a/crates/wasmtime/src/runtime/trampoline/global.rs +++ b/crates/wasmtime/src/runtime/trampoline/global.rs @@ -83,6 +83,6 @@ pub fn generate_global_export( } } - let index = store.host_globals_mut().push(ctx); + let index = store.host_globals_mut().push(ctx)?; Ok(crate::Global::from_host(store.id(), index)) } diff --git a/crates/wasmtime/src/runtime/trampoline/memory.rs b/crates/wasmtime/src/runtime/trampoline/memory.rs index 175e77b137e1..3ef0c4be3e3a 100644 --- a/crates/wasmtime/src/runtime/trampoline/memory.rs +++ b/crates/wasmtime/src/runtime/trampoline/memory.rs @@ -9,6 +9,8 @@ use crate::runtime::vm::{ }; use crate::store::{AllocateInstanceKind, InstanceId, StoreOpaque, StoreResourceLimiter}; use alloc::sync::Arc; +use core::future::Future; +use core::pin::Pin; use wasmtime_environ::{ DefinedMemoryIndex, DefinedTableIndex, EntityIndex, HostPtr, Module, StaticModuleIndex, Tunables, VMOffsets, @@ -42,7 +44,7 @@ pub async fn create_memory( module .exports .insert(name, EntityIndex::Memory(memory_id))?; - let info = ModuleRuntimeInfo::bare(Arc::new(module))?; + let info = ModuleRuntimeInfo::bare(try_new::>(module)?)?; // We create an instance in the on-demand allocator when creating handles // associated with external objects. The configured instance allocator @@ -126,7 +128,6 @@ struct SingleMemoryInstance<'a> { ondemand: OnDemandInstanceAllocator, } -#[async_trait::async_trait] unsafe impl InstanceAllocator for SingleMemoryInstance<'_> { #[cfg(feature = "component-model")] fn validate_component<'a>( @@ -170,12 +171,12 @@ unsafe impl InstanceAllocator for SingleMemoryInstance<'_> { self.ondemand.decrement_core_instance_count(); } - async fn allocate_memory( - &self, - request: &mut InstanceAllocationRequest<'_, '_>, - ty: &wasmtime_environ::Memory, + fn allocate_memory<'a, 'b: 'a, 'c: 'a>( + &'a self, + request: &'a mut InstanceAllocationRequest<'b, 'c>, + ty: &'a wasmtime_environ::Memory, memory_index: Option, - ) -> Result<(MemoryAllocationIndex, Memory)> { + ) -> Pin> + Send + 'a>> { if cfg!(debug_assertions) { let module = request.runtime_info.env_module(); let offsets = request.runtime_info.offsets(); @@ -184,15 +185,13 @@ unsafe impl InstanceAllocator for SingleMemoryInstance<'_> { } match self.preallocation { - Some(shared_memory) => Ok(( - MemoryAllocationIndex::default(), - shared_memory.clone().as_memory(), - )), - None => { - self.ondemand - .allocate_memory(request, ty, memory_index) - .await - } + Some(shared_memory) => crate::runtime::box_future(async move { + Ok(( + MemoryAllocationIndex::default(), + shared_memory.clone().as_memory(), + )) + }), + None => self.ondemand.allocate_memory(request, ty, memory_index), } } @@ -208,13 +207,13 @@ unsafe impl InstanceAllocator for SingleMemoryInstance<'_> { } } - async fn allocate_table( - &self, - req: &mut InstanceAllocationRequest<'_, '_>, - ty: &wasmtime_environ::Table, + fn allocate_table<'a, 'b: 'a, 'c: 'a>( + &'a self, + req: &'a mut InstanceAllocationRequest<'b, 'c>, + ty: &'a wasmtime_environ::Table, table_index: DefinedTableIndex, - ) -> Result<(TableAllocationIndex, Table)> { - self.ondemand.allocate_table(req, ty, table_index).await + ) -> Pin> + Send + 'a>> { + self.ondemand.allocate_table(req, ty, table_index) } unsafe fn deallocate_table( diff --git a/crates/wasmtime/src/runtime/trampoline/table.rs b/crates/wasmtime/src/runtime/trampoline/table.rs index b32cac5bfd99..6700b78ac1ab 100644 --- a/crates/wasmtime/src/runtime/trampoline/table.rs +++ b/crates/wasmtime/src/runtime/trampoline/table.rs @@ -32,7 +32,7 @@ pub async fn create_table( unsafe { let allocator = OnDemandInstanceAllocator::new(store.engine().config().mem_creator.clone(), 0, false); - let module = Arc::new(module); + let module = try_new::>(module)?; store .allocate_instance( limiter, diff --git a/crates/wasmtime/src/runtime/trampoline/tag.rs b/crates/wasmtime/src/runtime/trampoline/tag.rs index 91de74e999db..4f7a1d898fdd 100644 --- a/crates/wasmtime/src/runtime/trampoline/tag.rs +++ b/crates/wasmtime/src/runtime/trampoline/tag.rs @@ -36,7 +36,7 @@ pub fn create_tag(store: &mut StoreOpaque, ty: &TagType) -> Result { unsafe { let allocator = OnDemandInstanceAllocator::new(store.engine().config().mem_creator.clone(), 0, false); - let module = Arc::new(module); + let module = try_new::>(module)?; // Note that `assert_ready` should be valid here because this module // doesn't allocate tables or memories meaning it shouldn't need a diff --git a/crates/wasmtime/src/runtime/type_registry.rs b/crates/wasmtime/src/runtime/type_registry.rs index 054bb768d2c2..3149987922f3 100644 --- a/crates/wasmtime/src/runtime/type_registry.rs +++ b/crates/wasmtime/src/runtime/type_registry.rs @@ -25,7 +25,7 @@ use wasmtime_core::slab::{Id as SlabId, Slab}; use wasmtime_environ::{ EngineOrModuleTypeIndex, EntityRef, GcLayout, ModuleInternedTypeIndex, ModuleTypes, TypeTrace, Undo, VMSharedTypeIndex, WasmRecGroup, WasmSubType, - collections::{HashSet, PrimaryMap, SecondaryMap, TryCow, Vec}, + collections::TryCow, iter_entity_range, packed_option::{PackedOption, ReservedValue}, }; @@ -88,9 +88,9 @@ use wasmtime_environ::{ /// when dropped. pub struct TypeCollection { engine: Engine, - rec_groups: Vec, - types: PrimaryMap, - trampolines: SecondaryMap>, + rec_groups: TryVec, + types: TryPrimaryMap, + trampolines: TrySecondaryMap>, } impl Debug for TypeCollection { @@ -148,7 +148,7 @@ impl Engine { // module-index in a compiled module, and doing this engine-to-module // resolution now means we don't need to do it on the function call hot // path. - let mut trampolines = SecondaryMap::with_capacity(types.len())?; + let mut trampolines = TrySecondaryMap::with_capacity(types.len())?; for (module_ty, module_trampoline_ty) in module_types.trampoline_types() { let shared_ty = types[module_ty]; let trampoline_shared_ty = registry.trampoline_type(shared_ty); @@ -184,7 +184,7 @@ impl TypeCollection { /// /// This is used for looking up module shared type indexes during module /// instantiation. - pub fn as_module_map(&self) -> &PrimaryMap { + pub fn as_module_map(&self) -> &TryPrimaryMap { &self.types } @@ -592,7 +592,7 @@ struct TypeRegistryInner { // registered. Before registering new rec groups, we first check this map to // see if we've already registered an identical rec group that we should // reuse instead. - hash_consing_map: HashSet, + hash_consing_map: TryHashSet, // A map from `VMSharedTypeIndex::bits()` to the type index's associated // Wasm type. @@ -607,7 +607,7 @@ struct TypeRegistryInner { // A map that lets you walk backwards from a `VMSharedTypeIndex` to its // `RecGroupEntry`. - type_to_rec_group: SecondaryMap>, + type_to_rec_group: TrySecondaryMap>, // A map from a registered type to its complete list of supertypes. // @@ -618,7 +618,7 @@ struct TypeRegistryInner { // Types without any supertypes are omitted from this map. This means that // we never allocate any backing storage for this map when Wasm GC is not in // use. - type_to_supertypes: SecondaryMap>>, + type_to_supertypes: TrySecondaryMap>>, // A map from each registered function type to its trampoline type. // @@ -628,19 +628,19 @@ struct TypeRegistryInner { // backing storage for this map. As a nice bonus, this also avoids cycles (a // function type referencing itself) that our naive reference counting // doesn't play well with. - type_to_trampoline: SecondaryMap>, + type_to_trampoline: TrySecondaryMap>, // A map from each registered GC type to its layout. // // Function types do not have an entry in this map. Similar to the // `type_to_{supertypes,trampoline}` maps, we completely omit the `None` // entries for these types as a memory optimization. - type_to_gc_layout: SecondaryMap>, + type_to_gc_layout: TrySecondaryMap>, // An explicit stack of entries that we are in the middle of dropping. Used // to avoid recursion when dropping a type that is holding the last // reference to another type, etc... - drop_stack: Vec, + drop_stack: TryVec, } impl TypeRegistryInner { @@ -681,19 +681,19 @@ impl TypeRegistryInner { types: &ModuleTypes, ) -> Result< ( - Vec, - PrimaryMap, + TryVec, + TryPrimaryMap, ), OutOfMemory, > { log::trace!("Start registering module types"); // The engine's type registry entries for these module types. - let mut entries = Vec::with_capacity(types.rec_groups().len())?; + let mut entries = TryVec::with_capacity(types.rec_groups().len())?; // The map from a module type index to an engine type index for these // module types. - let mut map = PrimaryMap::::with_capacity( + let mut map = TryPrimaryMap::::with_capacity( types.wasm_types().len(), )?; @@ -751,7 +751,7 @@ impl TypeRegistryInner { fn register_rec_group( &mut self, gc_runtime: Option<&dyn GcRuntime>, - map: &PrimaryMap, + map: &TryPrimaryMap, range: Range, types: impl ExactSizeIterator>, ) -> Result { @@ -766,7 +766,7 @@ impl TypeRegistryInner { // canonicalize for runtime usage in this engine, we must still eagerly // clone and set aside the original, non-canonicalized types for that // potential engine canonicalization eventuality. - let mut non_canon_types = Vec::with_capacity(types.len())?; + let mut non_canon_types = TryVec::with_capacity(types.len())?; let hash_consing_key = WasmRecGroup { types: types .zip(iter_entity_range(range.clone())) @@ -809,10 +809,10 @@ impl TypeRegistryInner { fn register_new_rec_group( &mut self, gc_runtime: Option<&(dyn GcRuntime + 'static)>, - map: &PrimaryMap, + map: &TryPrimaryMap, range: Range, hash_consing_key: WasmRecGroup, - mut non_canon_types: Vec<(ModuleInternedTypeIndex, WasmSubType)>, + mut non_canon_types: TryVec<(ModuleInternedTypeIndex, WasmSubType)>, ) -> Result { debug_assert!(hash_consing_key.is_canonicalized_for_hash_consing()); debug_assert_eq!(self.hash_consing_map.contains(&hash_consing_key), false); @@ -824,7 +824,7 @@ impl TypeRegistryInner { // much as possible. let num_types = non_canon_types.len(); self.reserve_capacity_for_rec_group(num_types)?; - let mut shared_type_indices = Vec::new(); + let mut shared_type_indices = TryVec::new(); shared_type_indices.reserve_exact(num_types)?; let entry_inner = RecGroupEntry::new_inner()?; @@ -1158,7 +1158,7 @@ impl TypeRegistryInner { fn assign_shared_type_indices( &mut self, non_canon_types: &[(ModuleInternedTypeIndex, WasmSubType)], - mut shared_type_indices: Vec, + mut shared_type_indices: TryVec, ) -> Box<[VMSharedTypeIndex]> { debug_assert_eq!(non_canon_types.len(), shared_type_indices.capacity()); debug_assert!(shared_type_indices.is_empty()); @@ -1258,7 +1258,7 @@ impl TypeRegistryInner { /// this registry. fn canonicalize_entry_types_for_runtime_usage<'a>( &self, - map: &PrimaryMap, + map: &TryPrimaryMap, entry: &RecGroupEntry, sub_tys: impl ExactSizeIterator, range: Range, @@ -1272,7 +1272,7 @@ impl TypeRegistryInner { /// Canonicalize one type for runtime usage within this registry. fn canonicalize_type_for_runtime_usage( &self, - map: &PrimaryMap, + map: &TryPrimaryMap, entry: &RecGroupEntry, engine_index: VMSharedTypeIndex, ty: &mut WasmSubType, @@ -1346,7 +1346,7 @@ impl TypeRegistryInner { // This type doesn't have any module-level type references, since it is // already canonicalized for runtime usage in this registry, so an empty // map suffices. - let map = PrimaryMap::default(); + let map = TryPrimaryMap::default(); // This must have `range.len() == 1`, even though we know this type // doesn't have any intra-group type references, to satisfy diff --git a/crates/wasmtime/src/runtime/types.rs b/crates/wasmtime/src/runtime/types.rs index 54b9ade2bf94..a45f5d997624 100644 --- a/crates/wasmtime/src/runtime/types.rs +++ b/crates/wasmtime/src/runtime/types.rs @@ -12,7 +12,6 @@ use wasmtime_environ::{ PanicOnOom as _, Table, Tag, TypeTrace, VMSharedTypeIndex, WasmArrayType, WasmCompositeInnerType, WasmCompositeType, WasmFieldType, WasmFuncType, WasmHeapType, WasmRefType, WasmStorageType, WasmStructType, WasmSubType, WasmValType, - collections::Vec as FallibleVec, }; pub(crate) mod matching; @@ -1926,8 +1925,8 @@ impl StructType { // from being reclaimed while constructing this struct type. let mut registrations = smallvec::SmallVec::<[_; 4]>::new(); - let fields = fields - .map(|ty: FieldType| { + let fields: Box<[WasmFieldType]> = fields + .map(|ty: FieldType| -> Result<_, Error> { assert!(ty.comes_from_same_engine(engine)); if supertype.is_some() { @@ -1940,9 +1939,9 @@ impl StructType { } } - ty.to_wasm_field_type() + Ok(ty.to_wasm_field_type()) }) - .collect(); + .try_collect()?; if let Some(supertype) = supertype { ensure!( @@ -2207,7 +2206,7 @@ impl ArrayType { finality.is_final(), supertype.map(|ty| ty.type_index().into()), wasm_ty, - )) + )?) } /// Get the engine that this array type is associated with. @@ -2324,7 +2323,7 @@ impl ArrayType { is_final: bool, supertype: Option, ty: WasmArrayType, - ) -> ArrayType { + ) -> Result { let ty = RegisteredType::new( engine, WasmSubType { @@ -2335,11 +2334,10 @@ impl ArrayType { inner: WasmCompositeInnerType::Array(ty), }, }, - ) - .panic_on_oom(); - Self { + )?; + Ok(Self { registered_type: ty, - } + }) } pub(crate) fn from_shared_type_index(engine: &Engine, index: VMSharedTypeIndex) -> ArrayType { @@ -2448,7 +2446,7 @@ impl FuncType { let params = params.into_iter(); let results = results.into_iter(); - let mut wasmtime_params = FallibleVec::with_capacity({ + let mut wasmtime_params = TryVec::with_capacity({ let size_hint = params.size_hint(); let cap = size_hint.1.unwrap_or(size_hint.0); // Only reserve space if we have a supertype, as that is the only time @@ -2456,7 +2454,7 @@ impl FuncType { supertype.is_some() as usize * cap })?; - let mut wasmtime_results = FallibleVec::with_capacity({ + let mut wasmtime_results = TryVec::with_capacity({ let size_hint = results.size_hint(); let cap = size_hint.1.unwrap_or(size_hint.0); // Same as above. @@ -2468,10 +2466,10 @@ impl FuncType { // the only thing keeping a type in the registry, we don't want to // unregister it when we convert the `ValType` into a `WasmValType` just // before we register our new `WasmFuncType` that will reference it. - let mut registrations = FallibleVec::new(); + let mut registrations = TryVec::new(); let mut to_wasm_type = - |ty: ValType, vec: &mut FallibleVec<_>| -> Result { + |ty: ValType, vec: &mut TryVec<_>| -> Result { assert!(ty.comes_from_same_engine(engine)); if supertype.is_some() { @@ -2819,13 +2817,13 @@ impl ExnType { /// signature implies a tag type, and when instantiated at /// runtime, it must be associated with a tag of that type. pub fn new(engine: &Engine, fields: impl IntoIterator) -> Result { - let fields = fields.into_iter().collect::>(); + let fields: TryVec<_> = fields.into_iter().try_collect()?; // First, construct/intern a FuncType: we need this to exist // so we can hand out a TagType, and it also roots any nested registrations. - let func_ty = FuncType::new(engine, fields.clone(), []); + let func_ty = FuncType::try_new(engine, fields.iter().cloned(), [])?; - Ok(Self::_new(engine, fields, func_ty)) + Self::_new(engine, fields, func_ty) } /// Create a new `ExnType` from an existing `TagType`. @@ -2843,28 +2841,22 @@ impl ExnType { "Cannot create an exception type from a tag type with results in the signature" ); - Ok(Self::_new( - tag.ty.engine(), - func_ty.params(), - func_ty.clone(), - )) + Self::_new(tag.ty.engine(), func_ty.params(), func_ty.clone()) } fn _new( engine: &Engine, fields: impl IntoIterator, func_ty: FuncType, - ) -> ExnType { - let fields = fields - .into_iter() - .map(|ty| { - assert!(ty.comes_from_same_engine(engine)); - WasmFieldType { - element_type: WasmStorageType::Val(ty.to_wasm_type()), - mutable: false, - } - }) - .collect(); + ) -> Result { + let mut wasm_fields = TryVec::new(); + for ty in fields.into_iter() { + assert!(ty.comes_from_same_engine(engine)); + wasm_fields.push(WasmFieldType { + element_type: WasmStorageType::Val(ty.to_wasm_type()), + mutable: false, + })?; + } let ty = RegisteredType::new( engine, @@ -2875,17 +2867,16 @@ impl ExnType { shared: false, inner: WasmCompositeInnerType::Exn(WasmExnType { func_ty: EngineOrModuleTypeIndex::Engine(func_ty.type_index()), - fields, + fields: wasm_fields.into_boxed_slice()?, }), }, }, - ) - .panic_on_oom(); + )?; - Self { + Ok(ExnType { func_ty, registered_type: ty, - } + }) } /// Get the tag type that this exception type is associated with. diff --git a/crates/wasmtime/src/runtime/vm/component/libcalls.rs b/crates/wasmtime/src/runtime/vm/component/libcalls.rs index c6f4f6f284d6..d9c7588468c0 100644 --- a/crates/wasmtime/src/runtime/vm/component/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/component/libcalls.rs @@ -171,9 +171,9 @@ fn assert_no_overlap(a: &[T], b: &[U]) { let b_end = b_start + (b.len() * core::mem::size_of::()); if a_start < b_start { - assert!(a_end < b_start); + assert!(a_end <= b_start); } else { - assert!(b_end < a_start); + assert!(b_end <= a_start); } } diff --git a/crates/wasmtime/src/runtime/vm/component/resources.rs b/crates/wasmtime/src/runtime/vm/component/resources.rs index 56deccdd43be..7c8e29587c5e 100644 --- a/crates/wasmtime/src/runtime/vm/component/resources.rs +++ b/crates/wasmtime/src/runtime/vm/component/resources.rs @@ -226,7 +226,7 @@ impl ResourceTables<'_> { match self.table_for_index(&index).remove_resource(index)? { RemovedResource::Own { rep } => Ok(Some(rep)), RemovedResource::Borrow { scope } => { - self.task_state.call_context(scope).borrow_count -= 1; + self.task_state.call_context(scope)?.borrow_count -= 1; Ok(None) } } @@ -273,8 +273,8 @@ impl ResourceTables<'_> { pub fn resource_lift_borrow(&mut self, index: TypedResourceIndex) -> Result { let (rep, is_own) = self.table_for_index(&index).resource_lend(index)?; if is_own { - let scope = self.task_state.current_call_context_scope_id(); - self.task_state.call_context(scope).lenders.push(index); + let scope = self.task_state.current_call_context_scope_id()?; + self.task_state.call_context(scope)?.lenders.push(index); } Ok(rep) } @@ -292,8 +292,8 @@ impl ResourceTables<'_> { /// `VMComponentContext` which handles the special case of avoiding borrow /// tracking entirely. pub fn resource_lower_borrow(&mut self, resource: TypedResource) -> Result { - let scope = self.task_state.current_call_context_scope_id(); - let cx = self.task_state.call_context(scope); + let scope = self.task_state.current_call_context_scope_id()?; + let cx = self.task_state.call_context(scope)?; cx.borrow_count = cx.borrow_count.checked_add(1).unwrap(); self.table_for_resource(&resource) .resource_borrow_insert(resource, scope) @@ -306,8 +306,8 @@ impl ResourceTables<'_> { /// resources that were originally passed in. #[inline] pub fn validate_scope_exit(&mut self) -> Result<()> { - let current = self.task_state.current_call_context_scope_id(); - let cx = self.task_state.call_context(current); + let current = self.task_state.current_call_context_scope_id()?; + let cx = self.task_state.call_context(current)?; if cx.borrow_count > 0 { bail!("borrow handles still remain at the end of the call") } diff --git a/crates/wasmtime/src/runtime/vm/cow.rs b/crates/wasmtime/src/runtime/vm/cow.rs index 0f3f9d1bb937..987e9c944abe 100644 --- a/crates/wasmtime/src/runtime/vm/cow.rs +++ b/crates/wasmtime/src/runtime/vm/cow.rs @@ -11,14 +11,15 @@ use crate::runtime::vm::{ use alloc::sync::Arc; use core::fmt; use core::ops::Range; -use wasmtime_environ::{DefinedMemoryIndex, MemoryInitialization, Module, PrimaryMap, Tunables}; +use wasmtime_environ::prelude::TryPrimaryMap; +use wasmtime_environ::{DefinedMemoryIndex, MemoryInitialization, Module, Tunables}; /// Backing images for memories in a module. /// /// This is meant to be built once, when a module is first loaded/constructed, /// and then used many times for instantiation. pub struct ModuleMemoryImages { - memories: PrimaryMap>>, + memories: TryPrimaryMap>>, } impl ModuleMemoryImages { @@ -179,7 +180,7 @@ impl ModuleMemoryImages { MemoryInitialization::Static { map } => map, _ => return Ok(None), }; - let mut memories = PrimaryMap::with_capacity(map.len()); + let mut memories = TryPrimaryMap::with_capacity(map.len())?; let page_size = crate::runtime::vm::host_page_size(); let page_size = u32::try_from(page_size).unwrap(); for (memory_index, init) in map { @@ -196,7 +197,7 @@ impl ModuleMemoryImages { let init = match init { Some(init) => init, None => { - memories.push(None); + memories.push(None)?; continue; } }; @@ -233,7 +234,7 @@ impl ModuleMemoryImages { None => return Ok(None), }; - let idx = memories.push(Some(Arc::new(image))); + let idx = memories.push(Some(try_new::>(image)?))?; assert_eq!(idx, defined_memory); } @@ -460,7 +461,7 @@ impl MemoryImageSlot { let host_page_size_log2 = u8::try_from(host_page_size().ilog2()).unwrap(); if initial_size_bytes_page_aligned < self.accessible && (tunables.memory_guard_size > 0 - || ty.can_elide_bounds_check(tunables, host_page_size_log2)) + || ty.can_use_virtual_memory(tunables, host_page_size_log2)) { self.set_protection(initial_size_bytes_page_aligned..self.accessible, false)?; self.accessible = initial_size_bytes_page_aligned; diff --git a/crates/wasmtime/src/runtime/vm/gc.rs b/crates/wasmtime/src/runtime/vm/gc.rs index 6067b565d577..b33365a43c17 100644 --- a/crates/wasmtime/src/runtime/vm/gc.rs +++ b/crates/wasmtime/src/runtime/vm/gc.rs @@ -48,6 +48,18 @@ pub struct GcStore { /// The function-references table for this GC heap. pub func_ref_table: FuncRefTable, + + /// An allocation counter that triggers GC when it reaches zero. + /// + /// Initialized from the `WASMTIME_GC_ZEAL_ALLOC_COUNTER` environment + /// variable. Decremented on every allocation and when it hits zero, a GC is + /// forced and the counter is reset. + #[cfg(all(gc_zeal, feature = "std"))] + gc_zeal_alloc_counter: Option, + + /// The initial value to reset the counter to after it triggers. + #[cfg(all(gc_zeal, feature = "std"))] + gc_zeal_alloc_counter_init: Option, } impl GcStore { @@ -55,11 +67,29 @@ impl GcStore { pub fn new(allocation_index: GcHeapAllocationIndex, gc_heap: Box) -> Self { let host_data_table = ExternRefHostDataTable::default(); let func_ref_table = FuncRefTable::default(); + + #[cfg(all(gc_zeal, feature = "std"))] + let gc_zeal_alloc_counter_init = + std::env::var("WASMTIME_GC_ZEAL_ALLOC_COUNTER") + .ok() + .map(|v| { + v.parse::().unwrap_or_else(|_| { + panic!( + "`WASMTIME_GC_ZEAL_ALLOC_COUNTER` must be a non-zero \ + `u32` value, got: {v}" + ) + }) + }); + Self { allocation_index, gc_heap, host_data_table, func_ref_table, + #[cfg(all(gc_zeal, feature = "std"))] + gc_zeal_alloc_counter: gc_zeal_alloc_counter_init, + #[cfg(all(gc_zeal, feature = "std"))] + gc_zeal_alloc_counter_init, } } @@ -231,6 +261,20 @@ impl GcStore { header: VMGcHeader, layout: Layout, ) -> Result> { + // When gc_zeal is enabled with an allocation counter, decrement it and + // force a GC cycle when it reaches zero by returning a fake OOM. + #[cfg(all(gc_zeal, feature = "std"))] + if let Some(counter) = self.gc_zeal_alloc_counter.take() { + match NonZeroU32::new(counter.get() - 1) { + Some(c) => self.gc_zeal_alloc_counter = Some(c), + None => { + log::trace!("gc_zeal: allocation counter reached zero, forcing GC"); + self.gc_zeal_alloc_counter = self.gc_zeal_alloc_counter_init; + return Ok(Err(0)); + } + } + } + self.gc_heap.alloc_raw(header, layout) } diff --git a/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs b/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs index 94bc99abb9f1..774f4c4c3f51 100644 --- a/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs +++ b/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs @@ -64,7 +64,8 @@ use core::{ }; use wasmtime_environ::drc::{ARRAY_LENGTH_OFFSET, DrcTypeLayouts}; use wasmtime_environ::{ - GcArrayLayout, GcLayout, GcStructLayout, GcTypeLayouts, VMGcKind, VMSharedTypeIndex, + GcArrayLayout, GcLayout, GcStructLayout, GcTypeLayouts, POISON, VMGcKind, VMSharedTypeIndex, + gc_assert, }; #[expect(clippy::cast_possible_truncation, reason = "known to not overflow")] @@ -177,10 +178,15 @@ impl DrcHeap { let drc_ref = drc_ref(&gc_ref); let size = self.index(drc_ref).object_size(); let layout = FreeList::layout(size); - self.free_list - .as_mut() - .unwrap() - .dealloc(gc_ref.as_heap_index().unwrap(), layout); + let index = gc_ref.as_heap_index().unwrap(); + + // Poison the freed memory so that any stale access is detectable. + if cfg!(gc_zeal) { + let index = usize::try_from(index.get()).unwrap(); + self.heap_slice_mut()[index..][..layout.size()].fill(POISON); + } + + self.free_list.as_mut().unwrap().dealloc(index, layout); } /// Increment the ref count for the associated object. @@ -311,6 +317,7 @@ impl DrcHeap { debug_assert!(header.kind().matches(VMGcKind::ExternRef)); return; }; + match self .trace_infos .get(&ty) @@ -321,11 +328,24 @@ impl DrcHeap { let data = self.gc_object_data(gc_ref); for offset in gc_ref_offsets { let raw = data.read_u32(*offset); - if let Some(gc_ref) = VMGcRef::from_raw_u32(raw) { + if let Some(gc_ref) = VMGcRef::from_raw_u32(raw) + && !gc_ref.is_i31() + { + debug_assert!( + { + let header = self.header(&gc_ref); + let kind = header.kind().as_u32(); + VMGcKind::try_from_u32(kind).is_some() + }, + "trace_gc_ref: struct field at offset {offset} references object \ + with invalid `VMGcKind`", + ); + stack.push(gc_ref); } } } + TraceInfo::Array { gc_ref_elems } => { if !*gc_ref_elems { return; @@ -338,7 +358,19 @@ impl DrcHeap { let elem_offset = GC_REF_ARRAY_ELEMS_OFFSET + i * u32::try_from(mem::size_of::()).unwrap(); let raw = data.read_u32(elem_offset); - if let Some(gc_ref) = VMGcRef::from_raw_u32(raw) { + if let Some(gc_ref) = VMGcRef::from_raw_u32(raw) + && !gc_ref.is_i31() + { + debug_assert!( + { + let header = self.header(&gc_ref); + let kind = header.kind().as_u32(); + VMGcKind::try_from_u32(kind).is_some() + }, + "trace_gc_ref: array element at index {i} references object \ + with invalid `VMGcKind`", + ); + stack.push(gc_ref); } } @@ -359,6 +391,64 @@ impl DrcHeap { }) } + /// Assert the integrity of the over-approximated stack roots list. + fn assert_over_approximated_stack_roots_integrity(&self) { + if !cfg!(gc_zeal) { + return; + } + + let mut visited = HashSet::new(); + for gc_ref in self.iter_over_approximated_stack_roots() { + let idx = gc_ref.as_heap_index().unwrap().get(); + + // Each entry must have a valid `VMGcKind`. + let header = self.header(&gc_ref); + let kind = header.kind().as_u32(); + assert!( + VMGcKind::try_from_u32(kind).is_some(), + "over-approx list: entry at heap index {idx} has invalid VMGcKind {kind:#034b}", + ); + + // Each entry must have its in-list bit set. + let drc_header = self.index(drc_ref(&gc_ref)); + assert!( + drc_header.is_in_over_approximated_stack_roots(), + "over-approx list: entry at heap index {idx} does not have in-list bit set", + ); + + // Each entry must have a nonzero ref count. + assert_ne!( + drc_header.ref_count, 0, + "over-approx list: entry at heap index {idx} has zero ref count", + ); + + // No cycles or duplicates. + assert!( + visited.insert(idx), + "over-approx list: cycle or duplicate detected at heap index {idx}", + ); + } + } + + /// Assert that every free block in the free list is filled with the poison + /// pattern. + fn assert_free_blocks_are_poisoned(&self) { + if !cfg!(gc_zeal) { + return; + } + + let free_list = self.free_list.as_ref().unwrap(); + for (index, len) in free_list.iter_free_blocks() { + let start = usize::try_from(index).unwrap(); + let size = usize::try_from(len).unwrap(); + let slice = &self.heap_slice()[start..][..size]; + assert!( + slice.iter().all(|&b| b == POISON), + "free block at heap index {start} (size {size}) is not fully poisoned", + ); + } + } + fn trace(&mut self, roots: &mut GcRootsIter<'_>) { // The `over_approx_set` is used for `debug_assert!`s checking that // every reference we read out from the stack via stack maps is actually @@ -685,6 +775,12 @@ unsafe impl GcHeap for DrcHeap { self.free_list = Some(FreeList::new(len)); self.vmmemory = Some(memory.vmmemory()); self.memory = Some(memory); + + // Poison the entire heap so any access to uninitialized memory is + // detectable. + if cfg!(gc_zeal) { + self.heap_slice_mut().fill(POISON); + } } fn detach(&mut self) -> crate::vm::Memory { @@ -795,11 +891,27 @@ unsafe impl GcHeap for DrcHeap { } fn header(&self, gc_ref: &VMGcRef) -> &VMGcHeader { - self.index(gc_ref.as_typed_unchecked()) + let header: &VMGcHeader = self.index(gc_ref.as_typed_unchecked()); + + debug_assert!( + VMGcKind::try_from_u32(header.kind().as_u32()).is_some(), + "header: invalid VMGcKind {:#010x} at gc_ref {gc_ref:#p}", + header.kind().as_u32(), + ); + + header } fn header_mut(&mut self, gc_ref: &VMGcRef) -> &mut VMGcHeader { - self.index_mut(gc_ref.as_typed_unchecked()) + let header: &mut VMGcHeader = self.index_mut(gc_ref.as_typed_unchecked()); + + debug_assert!( + VMGcKind::try_from_u32(header.kind().as_u32()).is_some(), + "header_mut: invalid VMGcKind {:#010x} at gc_ref {gc_ref:#p}", + header.kind().as_u32(), + ); + + header } fn object_size(&self, gc_ref: &VMGcRef) -> usize { @@ -828,6 +940,19 @@ unsafe impl GcHeap for DrcHeap { Some(index) => VMGcRef::from_heap_index(index).unwrap(), }; + // Assert that the newly-allocated memory is still filled with the + // poison pattern, and hasn't been corrupted since deallocation (or + // initial heap creation). + if cfg!(gc_zeal) { + let start = usize::try_from(gc_ref.as_heap_index().unwrap().get()).unwrap(); + let slice = &self.heap_slice()[start..][..layout.size()]; + gc_assert!( + slice.iter().all(|&b| b == POISON), + "newly allocated GC object at index {start} is not fully poisoned; \ + freed memory was corrupted", + ); + } + *self.index_mut(drc_ref(&gc_ref)) = VMDrcHeader { header, ref_count: 1, @@ -922,6 +1047,16 @@ unsafe impl GcHeap for DrcHeap { self.vmmemory = Some(memory.vmmemory()); self.memory = Some(memory); + // Poison the newly-grown region so stale accesses are detectable. + if cfg!(gc_zeal) { + let old_cap = self.free_list.as_ref().unwrap().current_capacity(); + let new_bytes = usize::try_from(delta_bytes_grown).unwrap(); + let slice = self.heap_slice_mut(); + if old_cap + new_bytes <= slice.len() { + slice[old_cap..old_cap + new_bytes].fill(POISON); + } + } + self.free_list .as_mut() .unwrap() @@ -958,14 +1093,30 @@ impl<'a> GarbageCollection<'a> for DrcCollection<'a> { match self.phase { DrcCollectionPhase::Trace => { log::trace!("Begin DRC trace"); + + self.heap.assert_over_approximated_stack_roots_integrity(); + self.heap.assert_free_blocks_are_poisoned(); + self.heap.trace(&mut self.roots); + + self.heap.assert_over_approximated_stack_roots_integrity(); + self.heap.assert_free_blocks_are_poisoned(); + log::trace!("End DRC trace"); self.phase = DrcCollectionPhase::Sweep; GcProgress::Continue } DrcCollectionPhase::Sweep => { log::trace!("Begin DRC sweep"); + + self.heap.assert_over_approximated_stack_roots_integrity(); + self.heap.assert_free_blocks_are_poisoned(); + self.heap.sweep(self.host_data_table); + + self.heap.assert_over_approximated_stack_roots_integrity(); + self.heap.assert_free_blocks_are_poisoned(); + log::trace!("End DRC sweep"); self.phase = DrcCollectionPhase::Done; GcProgress::Complete diff --git a/crates/wasmtime/src/runtime/vm/gc/enabled/free_list.rs b/crates/wasmtime/src/runtime/vm/gc/enabled/free_list.rs index 7d0fb7c877aa..e638e5ea90a6 100644 --- a/crates/wasmtime/src/runtime/vm/gc/enabled/free_list.rs +++ b/crates/wasmtime/src/runtime/vm/gc/enabled/free_list.rs @@ -40,6 +40,11 @@ impl FreeList { Layout::from_size_align(size, ALIGN_USIZE).unwrap() } + /// Get the current total capacity this free list manages. + pub fn current_capacity(&self) -> usize { + self.capacity + } + /// Create a new `FreeList` for a contiguous region of memory of the given /// size. pub fn new(capacity: usize) -> Self { @@ -340,6 +345,11 @@ impl FreeList { self.check_integrity(); } + /// Iterate over all free blocks as `(index, len)` pairs. + pub fn iter_free_blocks(&self) -> impl Iterator + '_ { + self.free_block_index_to_len.iter().map(|(&i, &l)| (i, l)) + } + /// Assert that the free list is valid: /// /// 1. All blocks are within `ALIGN..self.capacity` diff --git a/crates/wasmtime/src/runtime/vm/instance.rs b/crates/wasmtime/src/runtime/vm/instance.rs index b8ce61912adc..eb459f0f1798 100644 --- a/crates/wasmtime/src/runtime/vm/instance.rs +++ b/crates/wasmtime/src/runtime/vm/instance.rs @@ -37,8 +37,8 @@ use wasmtime_environ::ModuleInternedTypeIndex; use wasmtime_environ::error::OutOfMemory; use wasmtime_environ::{ DataIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, DefinedTagIndex, - ElemIndex, EntityIndex, EntityRef, FuncIndex, GlobalIndex, HostPtr, MemoryIndex, PrimaryMap, - PtrSize, TableIndex, TableInitialValue, TableSegmentElements, TagIndex, Trap, VMCONTEXT_MAGIC, + ElemIndex, EntityIndex, EntityRef, FuncIndex, GlobalIndex, HostPtr, MemoryIndex, PtrSize, + TableIndex, TableInitialValue, TableSegmentElements, TagIndex, Trap, VMCONTEXT_MAGIC, VMOffsets, VMSharedTypeIndex, packed_option::ReservedValue, }; #[cfg(feature = "wmemcheck")] @@ -116,7 +116,7 @@ pub struct Instance { /// The `MemoryAllocationIndex` was given from our `InstanceAllocator` and /// must be given back to the instance allocator when deallocating each /// memory. - memories: PrimaryMap, + memories: TryPrimaryMap, /// WebAssembly table data. /// @@ -126,15 +126,15 @@ pub struct Instance { /// The `TableAllocationIndex` was given from our `InstanceAllocator` and /// must be given back to the instance allocator when deallocating each /// table. - tables: PrimaryMap, + tables: TryPrimaryMap, /// Stores the dropped passive element segments in this instantiation by index. /// If the index is present in the set, the segment has been dropped. - dropped_elements: EntitySet, + dropped_elements: TryEntitySet, /// Stores the dropped passive data segments in this instantiation by index. /// If the index is present in the set, the segment has been dropped. - dropped_data: EntitySet, + dropped_data: TryEntitySet, // TODO: add support for multiple memories; `wmemcheck_state` corresponds to // memory 0. @@ -166,13 +166,13 @@ impl Instance { /// and `tables` must have been allocated for `req.store`. unsafe fn new( req: InstanceAllocationRequest, - memories: PrimaryMap, - tables: PrimaryMap, + memories: TryPrimaryMap, + tables: TryPrimaryMap, ) -> Result { let module = req.runtime_info.env_module(); let memory_tys = &module.memories; - let dropped_elements = EntitySet::with_capacity(module.passive_elements.len())?; - let dropped_data = EntitySet::with_capacity(module.passive_data_map.len())?; + let dropped_elements = TryEntitySet::with_capacity(module.passive_elements.len())?; + let dropped_data = TryEntitySet::with_capacity(module.passive_data_map.len())?; #[cfg(feature = "wmemcheck")] let wmemcheck_state = if req.store.engine().config().wmemcheck { @@ -1626,26 +1626,26 @@ impl Instance { unsafe { &mut self.get_unchecked_mut().store } } - fn dropped_elements_mut(self: Pin<&mut Self>) -> &mut EntitySet { + fn dropped_elements_mut(self: Pin<&mut Self>) -> &mut TryEntitySet { // SAFETY: see `store_mut` above. unsafe { &mut self.get_unchecked_mut().dropped_elements } } - fn dropped_data_mut(self: Pin<&mut Self>) -> &mut EntitySet { + fn dropped_data_mut(self: Pin<&mut Self>) -> &mut TryEntitySet { // SAFETY: see `store_mut` above. unsafe { &mut self.get_unchecked_mut().dropped_data } } fn memories_mut( self: Pin<&mut Self>, - ) -> &mut PrimaryMap { + ) -> &mut TryPrimaryMap { // SAFETY: see `store_mut` above. unsafe { &mut self.get_unchecked_mut().memories } } pub(crate) fn tables_mut( self: Pin<&mut Self>, - ) -> &mut PrimaryMap { + ) -> &mut TryPrimaryMap { // SAFETY: see `store_mut` above. unsafe { &mut self.get_unchecked_mut().tables } } @@ -1714,7 +1714,7 @@ pub struct OwnedVMContext { /// The long version of why this field exists is that the rules that MIRI /// uses to ensure pointers are used correctly have various conditions on /// them depend on how pointers are used. More specifically if `*mut T` is - /// derived from `&mut T`, then that invalidates all prior pointers drived + /// derived from `&mut T`, then that invalidates all prior pointers derived /// from the `&mut T`. This means that while we liberally want to re-acquire /// a `*mut VMContext` throughout the implementation of `Instance` the /// trivial way, a function `fn vmctx(Pin<&mut Instance>) -> *mut VMContext` diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator.rs b/crates/wasmtime/src/runtime/vm/instance/allocator.rs index d7935919cbf5..9b70b73e8b63 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator.rs @@ -8,10 +8,12 @@ use crate::runtime::vm::table::Table; use crate::runtime::vm::{CompiledModuleId, ModuleRuntimeInfo}; use crate::store::{Asyncness, InstanceId, StoreOpaque, StoreResourceLimiter}; use crate::{OpaqueRootScope, Val}; +use core::future::Future; +use core::pin::Pin; use core::{mem, ptr}; use wasmtime_environ::{ DefinedMemoryIndex, DefinedTableIndex, HostPtr, InitMemory, MemoryInitialization, - MemoryInitializer, Module, PrimaryMap, SizeOverflow, TableInitialValue, Trap, VMOffsets, + MemoryInitializer, Module, SizeOverflow, TableInitialValue, Trap, VMOffsets, }; #[cfg(feature = "gc")] @@ -127,7 +129,6 @@ impl GcHeapAllocationIndex { /// /// This trait is unsafe as it requires knowledge of Wasmtime's runtime /// internals to implement correctly. -#[async_trait::async_trait] pub unsafe trait InstanceAllocator: Send + Sync { /// Validate whether a component (including all of its contained core /// modules) is allocatable by this instance allocator. @@ -184,12 +185,15 @@ pub unsafe trait InstanceAllocator: Send + Sync { fn decrement_core_instance_count(&self); /// Allocate a memory for an instance. - async fn allocate_memory( - &self, - request: &mut InstanceAllocationRequest<'_, '_>, - ty: &wasmtime_environ::Memory, + /// + /// Returns `Err(OutOfMemory)` if boxing the future fails. The inner + /// `Result` covers other allocation errors (e.g. resource limits). + fn allocate_memory<'a, 'b: 'a, 'c: 'a>( + &'a self, + request: &'a mut InstanceAllocationRequest<'b, 'c>, + ty: &'a wasmtime_environ::Memory, memory_index: Option, - ) -> Result<(MemoryAllocationIndex, Memory)>; + ) -> Pin> + Send + 'a>>; /// Deallocate an instance's previously allocated memory. /// @@ -206,12 +210,15 @@ pub unsafe trait InstanceAllocator: Send + Sync { ); /// Allocate a table for an instance. - async fn allocate_table( - &self, - req: &mut InstanceAllocationRequest<'_, '_>, - table: &wasmtime_environ::Table, + /// + /// Returns `Err(OutOfMemory)` if boxing the future fails. The inner + /// `Result` covers other allocation errors (e.g. resource limits). + fn allocate_table<'a, 'b: 'a, 'c: 'a>( + &'a self, + req: &'a mut InstanceAllocationRequest<'b, 'c>, + table: &'a wasmtime_environ::Table, table_index: DefinedTableIndex, - ) -> Result<(TableAllocationIndex, Table)>; + ) -> Pin> + Send + 'a>>; /// Deallocate an instance's previously allocated table. /// @@ -326,8 +333,8 @@ impl dyn InstanceAllocator + '_ { let mut guard = DeallocateOnDrop { run_deallocate: true, - memories: PrimaryMap::with_capacity(num_defined_memories), - tables: PrimaryMap::with_capacity(num_defined_tables), + memories: TryPrimaryMap::with_capacity(num_defined_memories)?, + tables: TryPrimaryMap::with_capacity(num_defined_tables)?, allocator: self, }; @@ -349,8 +356,8 @@ impl dyn InstanceAllocator + '_ { struct DeallocateOnDrop<'a> { run_deallocate: bool, - memories: PrimaryMap, - tables: PrimaryMap, + memories: TryPrimaryMap, + tables: TryPrimaryMap, allocator: &'a (dyn InstanceAllocator + 'a), } @@ -394,7 +401,7 @@ impl dyn InstanceAllocator + '_ { async fn allocate_memories( &self, request: &mut InstanceAllocationRequest<'_, '_>, - memories: &mut PrimaryMap, + memories: &mut TryPrimaryMap, ) -> Result<()> { let module = request.runtime_info.env_module(); @@ -411,7 +418,7 @@ impl dyn InstanceAllocator + '_ { let memory = self .allocate_memory(request, ty, Some(memory_index)) .await?; - memories.push(memory); + memories.push(memory)?; } Ok(()) @@ -425,7 +432,7 @@ impl dyn InstanceAllocator + '_ { /// `Self::allocate_memories`. unsafe fn deallocate_memories( &self, - memories: &mut PrimaryMap, + memories: &mut TryPrimaryMap, ) { for (memory_index, (allocation_index, memory)) in mem::take(memories) { // Because deallocating memory is infallible, we don't need to worry @@ -447,7 +454,7 @@ impl dyn InstanceAllocator + '_ { async fn allocate_tables( &self, request: &mut InstanceAllocationRequest<'_, '_>, - tables: &mut PrimaryMap, + tables: &mut TryPrimaryMap, ) -> Result<()> { let module = request.runtime_info.env_module(); @@ -462,7 +469,7 @@ impl dyn InstanceAllocator + '_ { .expect("should be a defined table since we skipped imported ones"); let table = self.allocate_table(request, table, def_index).await?; - tables.push(table); + tables.push(table)?; } Ok(()) @@ -476,7 +483,7 @@ impl dyn InstanceAllocator + '_ { /// `Self::allocate_tables`. unsafe fn deallocate_tables( &self, - tables: &mut PrimaryMap, + tables: &mut TryPrimaryMap, ) { for (table_index, (allocation_index, table)) in mem::take(tables) { // SAFETY: the tables here were allocated from this allocator per @@ -501,16 +508,16 @@ fn check_table_init_bounds( let start = const_evaluator .eval_int(&mut store, context, &segment.offset) .expect("const expression should be valid"); - let start = usize::try_from(start.unwrap_i32().cast_unsigned()).unwrap(); - let end = start.checked_add(usize::try_from(segment.elements.len()).unwrap()); + let start = get_index(start, module.tables[segment.table_index].idx_type); + let end = start.checked_add(segment.elements.len()); let table = store.instance_mut(instance).get_table(segment.table_index); match end { - Some(end) if end <= table.size() => { + Some(end) if end <= u64::try_from(table.size())? => { // Initializer is in bounds } _ => { - bail!("table out of bounds: elements segment does not fit") + bail!(Trap::TableOutOfBounds); } } } @@ -557,10 +564,7 @@ async fn initialize_tables( let start = const_evaluator .eval_int(&mut store, context, &segment.offset) .expect("const expression should be valid"); - let start = get_index( - start, - store.instance(context.instance).env_module().tables[segment.table_index].idx_type, - ); + let start = get_index(start, module.tables[segment.table_index].idx_type); Instance::table_init_segment( &mut store, limiter.as_deref_mut(), @@ -623,7 +627,7 @@ fn check_memory_init_bounds( // Initializer is in bounds } _ => { - bail!("memory out of bounds: data segment does not fit") + bail!(Trap::MemoryOutOfBounds); } } } diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/on_demand.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/on_demand.rs index d3015c70c1b6..b73b78d968be 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/on_demand.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/on_demand.rs @@ -8,6 +8,8 @@ use crate::runtime::vm::memory::{DefaultMemoryCreator, Memory}; use crate::runtime::vm::mpk::ProtectionKey; use crate::runtime::vm::table::Table; use alloc::sync::Arc; +use core::future::Future; +use core::pin::Pin; use wasmtime_environ::{DefinedMemoryIndex, DefinedTableIndex, HostPtr, Module, VMOffsets}; #[cfg(feature = "gc")] @@ -74,7 +76,6 @@ impl Default for OnDemandInstanceAllocator { } } -#[async_trait::async_trait] unsafe impl InstanceAllocator for OnDemandInstanceAllocator { #[cfg(feature = "component-model")] fn validate_component<'a>( @@ -109,33 +110,35 @@ unsafe impl InstanceAllocator for OnDemandInstanceAllocator { fn decrement_core_instance_count(&self) {} - async fn allocate_memory( - &self, - request: &mut InstanceAllocationRequest<'_, '_>, - ty: &wasmtime_environ::Memory, + fn allocate_memory<'a, 'b: 'a, 'c: 'a>( + &'a self, + request: &'a mut InstanceAllocationRequest<'b, 'c>, + ty: &'a wasmtime_environ::Memory, memory_index: Option, - ) -> Result<(MemoryAllocationIndex, Memory)> { + ) -> Pin> + Send + 'a>> { let creator = self .mem_creator .as_deref() .unwrap_or_else(|| &DefaultMemoryCreator); - let image = if let Some(memory_index) = memory_index { - request.runtime_info.memory_image(memory_index)? - } else { - None - }; - - let allocation_index = MemoryAllocationIndex::default(); - let memory = Memory::new_dynamic( - ty, - request.store.engine(), - creator, - image, - request.limiter.as_deref_mut(), - ) - .await?; - Ok((allocation_index, memory)) + crate::runtime::box_future(async move { + let image = if let Some(memory_index) = memory_index { + request.runtime_info.memory_image(memory_index)? + } else { + None + }; + + let allocation_index = MemoryAllocationIndex::default(); + let memory = Memory::new_dynamic( + ty, + request.store.engine(), + creator, + image, + request.limiter.as_deref_mut(), + ) + .await?; + Ok((allocation_index, memory)) + }) } unsafe fn deallocate_memory( @@ -148,20 +151,22 @@ unsafe impl InstanceAllocator for OnDemandInstanceAllocator { // Normal destructors do all the necessary clean up. } - async fn allocate_table( - &self, - request: &mut InstanceAllocationRequest<'_, '_>, - ty: &wasmtime_environ::Table, + fn allocate_table<'a, 'b: 'a, 'c: 'a>( + &'a self, + request: &'a mut InstanceAllocationRequest<'b, 'c>, + ty: &'a wasmtime_environ::Table, _table_index: DefinedTableIndex, - ) -> Result<(TableAllocationIndex, Table)> { - let allocation_index = TableAllocationIndex::default(); - let table = Table::new_dynamic( - ty, - request.store.engine().tunables(), - request.limiter.as_deref_mut(), - ) - .await?; - Ok((allocation_index, table)) + ) -> Pin> + Send + 'a>> { + crate::runtime::box_future(async move { + let allocation_index = TableAllocationIndex::default(); + let table = Table::new_dynamic( + ty, + request.store.engine().tunables(), + request.limiter.as_deref_mut(), + ) + .await?; + Ok((allocation_index, table)) + }) } unsafe fn deallocate_table( diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs index 3c40b8fa4700..b9350e1afe98 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs @@ -55,6 +55,8 @@ use crate::runtime::vm::{ mpk::{self, ProtectionKey, ProtectionMask}, sys::vm::PageMap, }; +use core::future::Future; +use core::pin::Pin; use core::sync::atomic::AtomicUsize; use std::borrow::Cow; use std::fmt::Display; @@ -100,8 +102,8 @@ pub struct InstanceLimits { /// concurrently. pub total_component_instances: u32, - /// The maximum size of a component's `VMComponentContext`, not including - /// any of its inner core modules' `VMContext` sizes. + /// The maximum size of a component's `VMComponentContext`, including + /// the aggregate size of all its inner core modules' `VMContext` sizes. pub component_instance_size: usize, /// The maximum number of core module instances that may be allocated @@ -474,18 +476,21 @@ impl PoolingInstanceAllocator { fn validate_component_instance_size( &self, offsets: &VMComponentOffsets, + core_instances_aggregate_size: usize, ) -> Result<()> { - if usize::try_from(offsets.size_of_vmctx()).unwrap() <= self.limits.component_instance_size - { + let vmcomponentctx_size = usize::try_from(offsets.size_of_vmctx()).unwrap(); + let total_instance_size = core_instances_aggregate_size.saturating_add(vmcomponentctx_size); + if total_instance_size <= self.limits.component_instance_size { return Ok(()); } // TODO: Add context with detailed accounting of what makes up all the // `VMComponentContext`'s space like we do for module instances. bail!( - "instance allocation for this component requires {} bytes of `VMComponentContext` \ - space which exceeds the configured maximum of {} bytes", - offsets.size_of_vmctx(), + "instance allocation for this component requires {total_instance_size} bytes of `VMComponentContext` \ + and aggregated core instance runtime space which exceeds the configured maximum of {} bytes. \ + `VMComponentContext` used {vmcomponentctx_size} bytes, `core module instances` used \ + {core_instances_aggregate_size} bytes.", self.limits.component_instance_size ) } @@ -550,7 +555,6 @@ impl PoolingInstanceAllocator { } } -#[async_trait::async_trait] unsafe impl InstanceAllocator for PoolingInstanceAllocator { #[cfg(feature = "component-model")] fn validate_component<'a>( @@ -559,12 +563,10 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { offsets: &VMComponentOffsets, get_module: &'a dyn Fn(StaticModuleIndex) -> &'a Module, ) -> Result<()> { - self.validate_component_instance_size(offsets) - .context("component instance size does not fit in pooling allocator requirements")?; - let mut num_core_instances = 0; let mut num_memories = 0; let mut num_tables = 0; + let mut core_instances_aggregate_size: usize = 0; for init in &component.initializers { use wasmtime_environ::component::GlobalInitializer::*; use wasmtime_environ::component::InstantiateModule; @@ -577,10 +579,12 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { InstantiateModule(InstantiateModule::Static(static_module_index, _), _) => { let module = get_module(*static_module_index); let offsets = VMOffsets::new(HostPtr, &module); + let layout = Instance::alloc_layout(&offsets); self.validate_module(module, &offsets)?; num_core_instances += 1; num_memories += module.num_defined_memories(); num_tables += module.num_defined_tables(); + core_instances_aggregate_size += layout.size(); } LowerImport { .. } | ExtractMemory(_) @@ -618,6 +622,9 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { ); } + self.validate_component_instance_size(offsets, core_instances_aggregate_size) + .context("component instance size does not fit in pooling allocator requirements")?; + Ok(()) } @@ -672,34 +679,36 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { self.live_core_instances.fetch_sub(1, Ordering::AcqRel); } - async fn allocate_memory( - &self, - request: &mut InstanceAllocationRequest<'_, '_>, - ty: &wasmtime_environ::Memory, + fn allocate_memory<'a, 'b: 'a, 'c: 'a>( + &'a self, + request: &'a mut InstanceAllocationRequest<'b, 'c>, + ty: &'a wasmtime_environ::Memory, memory_index: Option, - ) -> Result<(MemoryAllocationIndex, Memory)> { - async { - // FIXME(rust-lang/rust#145127) this should ideally use a version of - // `with_flush_and_retry` but adapted for async closures instead of only - // sync closures. Right now that won't compile though so this is the - // manually expanded version of the method. - let e = match self.memories.allocate(request, ty, memory_index).await { - Ok(result) => return Ok(result), - Err(e) => e, - }; - - if e.is::() { - let queue = self.decommit_queue.lock().unwrap(); - if self.flush_decommit_queue(queue) { - return self.memories.allocate(request, ty, memory_index).await; + ) -> Pin> + Send + 'a>> { + crate::runtime::box_future(async move { + async { + // FIXME(rust-lang/rust#145127) this should ideally use a version of + // `with_flush_and_retry` but adapted for async closures instead of only + // sync closures. Right now that won't compile though so this is the + // manually expanded version of the method. + let e = match self.memories.allocate(request, ty, memory_index).await { + Ok(result) => return Ok(result), + Err(e) => e, + }; + + if e.is::() { + let queue = self.decommit_queue.lock().unwrap(); + if self.flush_decommit_queue(queue) { + return self.memories.allocate(request, ty, memory_index).await; + } } - } - Err(e) - } - .await - .inspect(|_| { - self.live_memories.fetch_add(1, Ordering::Relaxed); + Err(e) + } + .await + .inspect(|_| { + self.live_memories.fetch_add(1, Ordering::Relaxed); + }) }) } @@ -712,61 +721,83 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { let prev = self.live_memories.fetch_sub(1, Ordering::Relaxed); debug_assert!(prev > 0); - // Reset the image slot. If there is any error clearing the - // image, just drop it here, and let the drop handler for the - // slot unmap in a way that retains the address space - // reservation. + // Reset the image slot. Depending on whether this is successful or not + // the `image` is preserved for future use. On success it's queued up to + // get deallocated later, and on failure the slot is deallocated + // immediately without preserving the image. let mut image = memory.unwrap_static_image(); let mut queue = DecommitQueue::default(); - let bytes_resident = image - .clear_and_remain_ready( - self.pagemap.as_ref(), - self.memories.keep_resident, - |ptr, len| { - // SAFETY: the memory in `image` won't be used until this - // decommit queue is flushed, and by definition the memory is - // not in use when calling this function. - unsafe { - queue.push_raw(ptr, len); - } - }, - ) - .expect("failed to reset memory image"); + let bytes_resident = image.clear_and_remain_ready( + self.pagemap.as_ref(), + self.memories.keep_resident, + |ptr, len| { + // SAFETY: the memory in `image` won't be used until this + // decommit queue is flushed, and by definition the memory is + // not in use when calling this function. + unsafe { + queue.push_raw(ptr, len); + } + }, + ); - // SAFETY: this image is not in use and its memory regions were enqueued - // with `push_raw` above. - unsafe { - queue.push_memory(allocation_index, image, bytes_resident); + match bytes_resident { + Ok(bytes_resident) => { + // SAFETY: this image is not in use and its memory regions were enqueued + // with `push_raw` above. + unsafe { + queue.push_memory(allocation_index, image, bytes_resident); + } + self.merge_or_flush(queue); + } + Err(e) => { + log::warn!("ignoring clear_and_remain_ready error {e}"); + // SAFETY: `allocation_index` comes from this pool, as an unsafe + // contract of this function itself, and it's guaranteed to be no + // longer in use so safe to deallocate. The slot couldn't be + // preserved so it's dropped here. + // + // Note that at this point it's not clear how many bytes are + // resident in memory, so it's inevitably going to leave statistics + // a little off. Also note though that non-Linux platforms don't + // keep track of resident bytes anyway, and this path is only + // reachable on non-Linux platforms because Linux can't return an + // error. + unsafe { + self.memories.deallocate(allocation_index, None, 0); + } + } } - self.merge_or_flush(queue); } - async fn allocate_table( - &self, - request: &mut InstanceAllocationRequest<'_, '_>, - ty: &wasmtime_environ::Table, + fn allocate_table<'a, 'b: 'a, 'c: 'a>( + &'a self, + request: &'a mut InstanceAllocationRequest<'b, 'c>, + ty: &'a wasmtime_environ::Table, _table_index: DefinedTableIndex, - ) -> Result<(super::TableAllocationIndex, Table)> { - async { - // FIXME: see `allocate_memory` above for comments about duplication - // with `with_flush_and_retry`. - let e = match self.tables.allocate(request, ty).await { - Ok(result) => return Ok(result), - Err(e) => e, - }; - - if e.is::() { - let queue = self.decommit_queue.lock().unwrap(); - if self.flush_decommit_queue(queue) { - return self.tables.allocate(request, ty).await; + ) -> Pin> + Send + 'a>> + { + crate::runtime::box_future(async move { + async { + // FIXME: see `allocate_memory` above for comments about duplication + // with `with_flush_and_retry`. + let e = match self.tables.allocate(request, ty).await { + Ok(result) => return Ok(result), + Err(e) => e, + }; + + if e.is::() { + let queue = self.decommit_queue.lock().unwrap(); + if self.flush_decommit_queue(queue) { + return self.tables.allocate(request, ty).await; + } } - } - Err(e) - } - .await - .inspect(|_| { - self.live_tables.fetch_add(1, Ordering::Relaxed); + Err(e) + } + .await + .inspect(|_| { + self.live_tables.fetch_add(1, Ordering::Relaxed); + }) }) } diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/decommit_queue.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/decommit_queue.rs index e1ef2fd17b88..3f90be868dd7 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/decommit_queue.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/decommit_queue.rs @@ -11,6 +11,7 @@ use super::PoolingInstanceAllocator; use crate::vm::{MemoryAllocationIndex, MemoryImageSlot, Table, TableAllocationIndex}; use smallvec::SmallVec; +use std::io; #[cfg(feature = "async")] use wasmtime_fiber::FiberStack; @@ -155,18 +156,14 @@ impl DecommitQueue { self.stacks.push((SendSyncStack(stack), bytes_resident)); } - fn decommit_all_raw(&mut self) { + /// Returns if any decommit call failed. + fn decommit_all_raw(&mut self) -> io::Result<()> { for iovec in self.raw.drain(..) { unsafe { - crate::vm::sys::vm::decommit_pages(iovec.0.iov_base.cast(), iovec.0.iov_len) - .unwrap_or_else(|e| { - panic!( - "failed to decommit ptr={:#p}, len={:#x}: {e}", - iovec.0.iov_base, iovec.0.iov_len - ) - }); + crate::vm::sys::vm::decommit_pages(iovec.0.iov_base.cast(), iovec.0.iov_len)?; } } + Ok(()) } /// Flush this queue, decommitting all enqueued regions in batch. @@ -175,14 +172,26 @@ impl DecommitQueue { /// the associated free lists; `false` if the queue was empty. pub fn flush(mut self, pool: &PoolingInstanceAllocator) -> bool { // First, do the raw decommit syscall(s). - self.decommit_all_raw(); + let decommit_succeeded = self.decommit_all_raw().is_ok(); // Second, restore the various entities to their associated pools' free // lists. This is safe, and they are ready for reuse, now that their // memory regions have been decommitted. + // + // Note that for memory images the images are all dropped here and + // ignored if any decommits failed. This signifies how the state of the + // slot is unknown and needs to be paved over in the future. Also note + // that `bytes_resident` is probably too low, but there's no other + // precise way to know, so it's left here as-is and it'll get reset when + // the slot is reused. let mut deallocated_any = false; for (allocation_index, image, bytes_resident) in self.memories { deallocated_any = true; + let image = if decommit_succeeded { + Some(image) + } else { + None + }; unsafe { pool.memories .deallocate(allocation_index, image, bytes_resident); diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/generic_stack_pool.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/generic_stack_pool.rs index c191580b3760..af883d48b153 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/generic_stack_pool.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/generic_stack_pool.rs @@ -65,7 +65,14 @@ impl StackPool { Ok(stack) => Ok(stack), Err(e) => { self.live_stacks.fetch_sub(1, Ordering::AcqRel); - Err(crate::Error::from(e)) + + #[allow( + clippy::useless_conversion, + reason = "some `cfg`s have `wasmtime::Error` as the error type, others don't" + )] + let e = crate::Error::from(e); + + Err(e) } } } diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/memory_pool.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/memory_pool.rs index c8ea1cdb6942..ed19a02e83be 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/memory_pool.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/memory_pool.rs @@ -167,7 +167,7 @@ enum ImageSlot { /// /// Future use of this slot will use `MemoryImageSlot` to continue to /// re-instantiate and reuse images and such. This state is entered after - /// and allocated slot is successfully deallcoated. + /// and allocated slot is successfully deallocated. PreviouslyUsed(MemoryImageSlot), } @@ -456,6 +456,10 @@ impl MemoryPool { /// Deallocate a previously-allocated memory. /// + /// If `image` is `None` then the state of this memory's slot is left + /// unknown. Otherwise `image` is used to retain information about the state + /// of this slot. + /// /// # Safety /// /// The memory must have been previously allocated from this pool and @@ -463,11 +467,12 @@ impl MemoryPool { /// must never be used again. /// /// The caller must have already called `clear_and_remain_ready` on the - /// memory's image and flushed any enqueued decommits for this memory. + /// memory's image and flushed any enqueued decommits for this memory. Note + /// that if `image` is `None` then this is not required. pub unsafe fn deallocate( &self, allocation_index: MemoryAllocationIndex, - image: MemoryImageSlot, + image: Option, bytes_resident: usize, ) { self.return_memory_image_slot(allocation_index, image); @@ -502,7 +507,7 @@ impl MemoryPool { // associated with a module (not just module and memory). The latter // would require care to make sure that its maintenance wouldn't be too // expensive for normal allocation/free operations. - for stripe in &self.stripes { + for (stripe_index, stripe) in self.stripes.iter().enumerate() { for i in 0..self.memories_per_instance { use wasmtime_environ::EntityRef; let memory_index = DefinedMemoryIndex::new(i); @@ -517,10 +522,11 @@ impl MemoryPool { // If anything fails then the slot will be in an "unknown" // state which means that on next use it'll be remapped with // anonymous memory. - let index = MemoryAllocationIndex(id.0); + let index = StripedAllocationIndex(id.0) + .as_unstriped_slot_index(stripe_index, self.stripes.len()); if let Ok(mut slot) = self.take_memory_image_slot(index) { if slot.remove_image().is_ok() { - self.return_memory_image_slot(index, slot); + self.return_memory_image_slot(index, Some(slot)); } } @@ -587,16 +593,23 @@ impl MemoryPool { } /// Return ownership of the given image slot. + /// + /// If `slot` is not provided then it's reset with `Unknown` meaning a + /// future allocation will need to pave over it to use it. fn return_memory_image_slot( &self, allocation_index: MemoryAllocationIndex, - slot: MemoryImageSlot, + slot: Option, ) { - assert!(!slot.is_dirty()); - let prev = mem::replace( &mut *self.image_slots[allocation_index.index()].lock().unwrap(), - ImageSlot::PreviouslyUsed(slot), + match slot { + Some(slot) => { + assert!(!slot.is_dirty()); + ImageSlot::PreviouslyUsed(slot) + } + None => ImageSlot::Unknown, + }, ); assert!(matches!(prev, ImageSlot::Unknown)); } diff --git a/crates/wasmtime/src/runtime/vm/interpreter.rs b/crates/wasmtime/src/runtime/vm/interpreter.rs index 5c015c2524d6..7d64312d92ee 100644 --- a/crates/wasmtime/src/runtime/vm/interpreter.rs +++ b/crates/wasmtime/src/runtime/vm/interpreter.rs @@ -61,7 +61,7 @@ impl Interpreter { pub fn new(engine: &Engine) -> Result { let ret = Interpreter { pulley: StoreBox::new(VmState { - vm: Vm::with_stack(engine.config().max_wasm_stack), + vm: Vm::with_stack(engine.config().max_wasm_stack)?, resume_at_pc: None, })?, }; diff --git a/crates/wasmtime/src/runtime/vm/libcalls.rs b/crates/wasmtime/src/runtime/vm/libcalls.rs index c547f95a7518..186d1c02acd8 100644 --- a/crates/wasmtime/src/runtime/vm/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/libcalls.rs @@ -1755,7 +1755,6 @@ fn throw_ref( fn breakpoint(store: &mut dyn VMStore, _instance: InstanceId) -> Result<()> { #[cfg(feature = "debug")] { - log::trace!("hit breakpoint"); store.block_on_debug_handler(crate::DebugEvent::Breakpoint)?; } // Avoid unused-argument warning in no-debugger builds. diff --git a/crates/wasmtime/src/runtime/vm/memory.rs b/crates/wasmtime/src/runtime/vm/memory.rs index 11ca43ec82b1..df3919446765 100644 --- a/crates/wasmtime/src/runtime/vm/memory.rs +++ b/crates/wasmtime/src/runtime/vm/memory.rs @@ -141,11 +141,17 @@ impl RuntimeMemoryCreator for DefaultMemoryCreator { || tunables.memory_reservation > 0 || tunables.memory_init_cow { - return Ok(Box::new(MmapMemory::new(ty, tunables, minimum, maximum)?)); + return Ok( + try_new::>(MmapMemory::new(ty, tunables, minimum, maximum)?)? + as Box, + ); } let _ = maximum; - Ok(Box::new(MallocMemory::new(ty, tunables, minimum)?)) + Ok( + try_new::>(MallocMemory::new(ty, tunables, minimum)?)? + as Box, + ) } } @@ -354,15 +360,19 @@ impl Memory { ) })?; + if !ty.allow_growth_to(minimum) { + bail!( + "memory minimum size of {} pages exceeds memory limits", + ty.limits.min + ); + } + Ok((minimum, maximum)) } /// Returns this memory's page size, in bytes. pub fn page_size(&self) -> u64 { - match self { - Memory::Local(mem) => mem.page_size(), - Memory::Shared(mem) => mem.page_size(), - } + self.ty().page_size() } /// Returns the size of this memory, in bytes. @@ -405,6 +415,39 @@ impl Memory { delta_pages: u64, limiter: Option<&mut StoreResourceLimiter<'_>>, ) -> Result, Error> { + let new_size = delta_pages + .checked_mul(self.page_size()) + .and_then(|new_bytes| { + let new_bytes = usize::try_from(new_bytes).ok()?; + self.byte_size().checked_add(new_bytes) + }); + match new_size { + Some(new_size) => { + // FIXME(WebAssembly/custom-page-sizes#45) - what should the + // behavior here be exactly? A trap? Return -1? A smaller limit? + // Unclear! + // + // Trap for now to make this as noisy/conservative as possible. + if !self.ty().allow_growth_to(new_size) { + bail!( + "disallowing growth to {new_size:#x} bytes based on \ + page size" + ) + } + } + + // If the new size in memory isn't representable in a `usize` then + // there's no need to actually try to grow it to that size. It's + // impossible to succeed so just fail it early. + None => { + if let Some(limiter) = limiter { + let err = crate::format_err!("memory growth exceeds address space"); + limiter.memory_grow_failed(err)?; + } + return Ok(None); + } + } + let result = match self { Memory::Local(mem) => mem.grow(delta_pages, limiter).await?, Memory::Shared(mem) => mem.grow(delta_pages)?, @@ -507,6 +550,13 @@ impl Memory { Memory::Shared(mem) => mem.wasm_accessible(), } } + + fn ty(&self) -> &wasmtime_environ::Memory { + match self { + Memory::Local(mem) => mem.ty(), + Memory::Shared(mem) => mem.ty(), + } + } } /// An owned allocation of a wasm linear memory. @@ -573,8 +623,8 @@ impl LocalMemory { }) } - pub fn page_size(&self) -> u64 { - self.ty.page_size() + pub fn ty(&self) -> &wasmtime_environ::Memory { + &self.ty } /// Grows a memory by `delta_pages`. @@ -595,7 +645,7 @@ impl LocalMemory { return Ok(Some((old_byte_size, old_byte_size))); } - let page_size = usize::try_from(self.page_size()).unwrap(); + let page_size = usize::try_from(self.ty().page_size()).unwrap(); // The largest wasm-page-aligned region of memory is possible to // represent in a `usize`. This will be impossible for the system to diff --git a/crates/wasmtime/src/runtime/vm/memory/mmap.rs b/crates/wasmtime/src/runtime/vm/memory/mmap.rs index 810f6441999f..e632e62310be 100644 --- a/crates/wasmtime/src/runtime/vm/memory/mmap.rs +++ b/crates/wasmtime/src/runtime/vm/memory/mmap.rs @@ -115,7 +115,7 @@ impl MmapMemory { } Ok(Self { - mmap: Arc::new(mmap), + mmap: try_new::>(mmap)?, len: minimum, maximum, pre_guard_size: pre_guard_bytes, diff --git a/crates/wasmtime/src/runtime/vm/memory/shared_memory.rs b/crates/wasmtime/src/runtime/vm/memory/shared_memory.rs index 5cafea369394..a34352b73408 100644 --- a/crates/wasmtime/src/runtime/vm/memory/shared_memory.rs +++ b/crates/wasmtime/src/runtime/vm/memory/shared_memory.rs @@ -37,11 +37,9 @@ impl SharedMemory { // `assert_ready` should never panic. let (minimum_bytes, maximum_bytes) = vm::assert_ready(Memory::limit_new(ty, None))?; let mmap_memory = MmapMemory::new(ty, tunables, minimum_bytes, maximum_bytes)?; - Self::wrap( - engine, - ty, - LocalMemory::new(ty, tunables, Box::new(mmap_memory), None)?, - ) + let boxed: Box = + try_new::>(mmap_memory)?; + Self::wrap(engine, ty, LocalMemory::new(ty, tunables, boxed, None)?) } /// Wrap an existing [Memory] with the locking provided by a [SharedMemory]. @@ -58,17 +56,17 @@ impl SharedMemory { if !ty.shared { bail!("shared memory must have a `shared` memory type"); } - Ok(Self(Arc::new(SharedMemoryInner { + Ok(Self(try_new::>(SharedMemoryInner { ty: *ty, spot: ParkingSpot::default(), def: LongTermVMMemoryDefinition(memory.vmmemory()), memory: RwLock::new(memory), - }))) + })?)) } /// Return the memory type for this [`SharedMemory`]. - pub fn ty(&self) -> wasmtime_environ::Memory { - self.0.ty + pub fn ty(&self) -> &wasmtime_environ::Memory { + &self.0.ty } /// Convert this shared memory into a [`Memory`]. @@ -172,10 +170,6 @@ impl SharedMemory { }) } - pub(crate) fn page_size(&self) -> u64 { - self.0.ty.page_size() - } - pub(crate) fn byte_size(&self) -> usize { self.0.memory.read().unwrap().byte_size() } diff --git a/crates/wasmtime/src/runtime/vm/memory/shared_memory_disabled.rs b/crates/wasmtime/src/runtime/vm/memory/shared_memory_disabled.rs index 960d35efb8a0..e9d470acbf61 100644 --- a/crates/wasmtime/src/runtime/vm/memory/shared_memory_disabled.rs +++ b/crates/wasmtime/src/runtime/vm/memory/shared_memory_disabled.rs @@ -15,7 +15,7 @@ impl SharedMemory { bail!("support for shared memories was disabled at compile time"); } - pub fn ty(&self) -> wasmtime_environ::Memory { + pub fn ty(&self) -> &wasmtime_environ::Memory { match *self {} } @@ -53,10 +53,6 @@ impl SharedMemory { match *self {} } - pub(crate) fn page_size(&self) -> u64 { - match *self {} - } - pub(crate) fn byte_size(&self) -> usize { match *self {} } diff --git a/crates/wasmtime/src/runtime/vm/mmap_vec.rs b/crates/wasmtime/src/runtime/vm/mmap_vec.rs index b00888efbcc8..db1d5d86c35f 100644 --- a/crates/wasmtime/src/runtime/vm/mmap_vec.rs +++ b/crates/wasmtime/src/runtime/vm/mmap_vec.rs @@ -303,16 +303,17 @@ impl MmapVec { crate::bail!("Cannot clone an externally-owned code memory."); } #[cfg(has_virtual_memory)] + #[allow( + unused_variables, + reason = "`mmap` and `len` only used with `std` feature" + )] MmapVec::Mmap { mmap, len } => { + #[cfg(feature = "std")] if let Some(original_file) = mmap.original_file() { let mmap = Mmap::from_file(original_file.clone())?; - Ok(MmapVec::Mmap { mmap, len: *len }) - } else { - MmapVec::from_slice_with_alignment( - &self[..], - crate::runtime::vm::host_page_size(), - ) + return Ok(MmapVec::Mmap { mmap, len: *len }); } + MmapVec::from_slice_with_alignment(&self[..], crate::runtime::vm::host_page_size()) } } } diff --git a/crates/wasmtime/src/runtime/vm/stack_switching.rs b/crates/wasmtime/src/runtime/vm/stack_switching.rs index d46e607e62c6..614134b9b89c 100644 --- a/crates/wasmtime/src/runtime/vm/stack_switching.rs +++ b/crates/wasmtime/src/runtime/vm/stack_switching.rs @@ -182,7 +182,7 @@ impl VMHostArray { pub type VMPayloads = VMHostArray; /// Type for a list of handlers, represented by the handled tag. Thus, the -/// stored data is actually `*mut VMTagDefinition`, but we don't havr access to +/// stored data is actually `*mut VMTagDefinition`, but we don't have access to /// that here. pub type VMHandlerList = VMHostArray<*mut u8>; diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/signals.rs b/crates/wasmtime/src/runtime/vm/sys/unix/signals.rs index 5b76473640ea..d1bb4fe94d4e 100644 --- a/crates/wasmtime/src/runtime/vm/sys/unix/signals.rs +++ b/crates/wasmtime/src/runtime/vm/sys/unix/signals.rs @@ -383,6 +383,13 @@ unsafe fn store_handler_in_ucontext(cx: *mut libc::c_void, handler: &Handler) { cx.uc_mcontext.mc_rsp = handler.sp as _; cx.uc_mcontext.mc_rax = 0; cx.uc_mcontext.mc_rdx = 0; + } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] { + let cx = unsafe { cx.cast::().as_mut().unwrap() }; + cx.mc_gpregs.gp_elr = handler.pc as _; + cx.mc_gpregs.gp_sp = handler.sp as _; + cx.mc_gpregs.gp_x[29] = handler.fp as _; + cx.mc_gpregs.gp_x[0] = 0; + cx.mc_gpregs.gp_x[1] = 0; } else if #[cfg(all(target_os = "openbsd", target_arch = "x86_64"))] { let cx = unsafe { cx.cast::().as_mut().unwrap() }; cx.sc_rip = handler.pc as _; diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/unwind.rs b/crates/wasmtime/src/runtime/vm/sys/unix/unwind.rs index 41684d488e6e..768b9a12de72 100644 --- a/crates/wasmtime/src/runtime/vm/sys/unix/unwind.rs +++ b/crates/wasmtime/src/runtime/vm/sys/unix/unwind.rs @@ -3,11 +3,10 @@ use crate::prelude::*; use crate::runtime::vm::SendSyncPtr; use core::ptr::NonNull; -use wasmtime_environ::collections::Vec; /// Represents a registration of function unwind information for System V ABI. pub struct UnwindRegistration { - registrations: Vec>, + registrations: TryVec>, } cfg_if::cfg_if! { @@ -79,7 +78,7 @@ impl UnwindRegistration { "The unwind info must always be aligned to a page" ); - let mut registrations = Vec::new(); + let mut registrations = TryVec::new(); unsafe { if using_libunwind() { // For libunwind, `__register_frame` takes a pointer to a single diff --git a/crates/wasmtime/src/runtime/vm/table.rs b/crates/wasmtime/src/runtime/vm/table.rs index e94029f475dc..e42697479ed8 100644 --- a/crates/wasmtime/src/runtime/vm/table.rs +++ b/crates/wasmtime/src/runtime/vm/table.rs @@ -185,7 +185,7 @@ impl From for DynamicTable { pub struct DynamicFuncTable { /// Dynamically managed storage space for this table. The length of this /// vector is the current size of the table. - elements: Vec, + elements: TryVec, /// Maximum size that `elements` can grow to. maximum: Option, /// Whether elements of this table are initialized lazily. @@ -195,7 +195,7 @@ pub struct DynamicFuncTable { pub struct DynamicGcRefTable { /// Dynamically managed storage space for this table. The length of this /// vector is the current size of the table. - elements: Vec>, + elements: TryVec>, /// Maximum size that `elements` can grow to. maximum: Option, } @@ -203,7 +203,7 @@ pub struct DynamicGcRefTable { pub struct DynamicContTable { /// Dynamically managed storage space for this table. The length of this /// vector is the current size of the table. - elements: Vec, + elements: TryVec, /// Maximum size that `elements` can grow to. maximum: Option, } @@ -291,7 +291,7 @@ pub(crate) fn wasm_to_table_type(ty: WasmRefType) -> TableElementType { /// /// Should only ever be called with a `T` that is a table element type and where /// `Option`'s `None` variant is represented with zero. -unsafe fn alloc_dynamic_table_elements(len: usize) -> Result>> { +unsafe fn alloc_dynamic_table_elements(len: usize) -> Result>> { debug_assert!( unsafe { core::mem::MaybeUninit::>::zeroed() @@ -302,21 +302,25 @@ unsafe fn alloc_dynamic_table_elements(len: usize) -> Result>> ); if len == 0 { - return Ok(vec![]); + return Ok(TryVec::new()); } let align = mem::align_of::>(); let size = mem::size_of::>(); let size = size.next_multiple_of(align); - let size = size.checked_mul(len).unwrap(); + let size = size + .checked_mul(len) + .ok_or_else(|| format_err!("overflow calculating table allocation size"))?; let layout = Layout::from_size_align(size, align)?; let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) }; - ensure!(!ptr.is_null(), "failed to allocate memory for table"); + if ptr.is_null() { + return Err(OutOfMemory::new(size).into()); + } - let elems = unsafe { Vec::>::from_raw_parts(ptr.cast(), len, len) }; + let elems = unsafe { TryVec::>::from_raw_parts(ptr.cast(), len, len) }; debug_assert!(elems.iter().all(|e| e.is_none())); Ok(elems) @@ -340,10 +344,11 @@ impl Table { elements: unsafe { alloc_dynamic_table_elements(minimum)? }, maximum, })), - TableElementType::Cont => Ok(Self::from(DynamicContTable { - elements: vec![None; minimum], - maximum, - })), + TableElementType::Cont => { + let mut elements = TryVec::new(); + elements.resize_with(minimum, || None)?; + Ok(Self::from(DynamicContTable { elements, maximum })) + } } } @@ -728,13 +733,13 @@ impl Table { // that delta is non-zero and the new size doesn't exceed the // maximum mean we can't get here. Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => { - elements.resize(new_size, None); + elements.resize_with(new_size, || None)?; } Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => { - elements.resize_with(new_size, || None); + elements.resize_with(new_size, || None)?; } Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => { - elements.resize(new_size, None); + elements.resize_with(new_size, || None)?; } } diff --git a/crates/wasmtime/src/runtime/vm/traphandlers.rs b/crates/wasmtime/src/runtime/vm/traphandlers.rs index c9f2d80f36f4..ff95153afa98 100644 --- a/crates/wasmtime/src/runtime/vm/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/traphandlers.rs @@ -740,7 +740,7 @@ impl CallThreadState { /// called using, for example, the `unwind` function below. /// /// Note that this is a relatively low-level function and will panic if - /// mis-used. + /// misused. /// /// # Panics /// diff --git a/crates/wasmtime/src/runtime/wave/component.rs b/crates/wasmtime/src/runtime/wave/component.rs index 9849bb455bf6..cc3e8c1b7c2a 100644 --- a/crates/wasmtime/src/runtime/wave/component.rs +++ b/crates/wasmtime/src/runtime/wave/component.rs @@ -45,7 +45,8 @@ impl WasmType for component::Type { | Self::Borrow(_) | Self::Stream(_) | Self::Future(_) - | Self::ErrorContext => WasmTypeKind::Unsupported, + | Self::ErrorContext + | Self::Map(_) => WasmTypeKind::Unsupported, } } @@ -138,9 +139,11 @@ impl WasmValue for component::Val { Self::Option(_) => WasmTypeKind::Option, Self::Result(_) => WasmTypeKind::Result, Self::Flags(_) => WasmTypeKind::Flags, - Self::Resource(_) | Self::Stream(_) | Self::Future(_) | Self::ErrorContext(_) => { - WasmTypeKind::Unsupported - } + Self::Resource(_) + | Self::Stream(_) + | Self::Future(_) + | Self::ErrorContext(_) + | Self::Map(_) => WasmTypeKind::Unsupported, } } diff --git a/crates/wast/src/component.rs b/crates/wast/src/component.rs index 1de0bb1ba3eb..06a8f62a772d 100644 --- a/crates/wast/src/component.rs +++ b/crates/wast/src/component.rs @@ -290,6 +290,7 @@ fn mismatch(expected: &ComponentConst<'_>, actual: &Val) -> Result<()> { Val::Future(..) => "future", Val::Stream(..) => "stream", Val::ErrorContext(..) => "error-context", + Val::Map(..) => "map", }; bail!("expected `{expected}` got `{actual}`") } diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index 48cec7a22586..2dfc07ee66bf 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -171,7 +171,7 @@ impl WastContext { return Ok(Export::Core( self.core_linker .get(&mut self.core_store, module, name) - .ok_or_else(|| format_err!("no item named `{module}::{name}` found"))?, + .with_context(|| format_err!("no item named `{module}::{name}` found"))?, )); } @@ -236,6 +236,17 @@ impl WastContext { Ok(()) } + /// Register the "wasmtime" module, which provides utilities that our misc + /// tests use. + pub fn register_wasmtime(&mut self) -> Result<()> { + self.core_linker + .func_wrap("wasmtime", "gc", |mut caller: Caller<_>| { + caller.gc(None)?; + Ok(()) + })?; + Ok(()) + } + /// Perform the action portion of a command. fn perform_action(&mut self, action: &Action<'_>) -> Result { // Need to simultaneously borrow `self.async_runtime` and a `&mut diff --git a/crates/wiggle/tests/wasi.rs b/crates/wiggle/tests/wasi.rs index 7900d75d1600..bafac8bf3887 100644 --- a/crates/wiggle/tests/wasi.rs +++ b/crates/wiggle/tests/wasi.rs @@ -132,7 +132,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { _memory: &mut GuestMemory<'_>, _fd: types::Fd, _fs_rights_base: types::Rights, - _fs_rights_inherting: types::Rights, + _fs_rights_inheriting: types::Rights, ) -> Result<()> { unimplemented!("fd_fdstat_set_rights") } @@ -315,7 +315,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { _path: GuestPtr, _oflags: types::Oflags, _fs_rights_base: types::Rights, - _fs_rights_inherting: types::Rights, + _fs_rights_inheriting: types::Rights, _fdflags: types::Fdflags, ) -> Result { unimplemented!("path_open") diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index 8ca36d819e62..609eb7d7eb2c 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -1633,9 +1633,9 @@ impl<'a> InterfaceGenerator<'a> { TypeDefKind::Stream(t) => self.type_stream(id, name, t.as_ref(), &ty.docs), TypeDefKind::Handle(handle) => self.type_handle(id, name, handle, &ty.docs), TypeDefKind::Resource => self.type_resource(id, name, ty, &ty.docs), + TypeDefKind::Map(k, v) => self.type_map(id, name, k, v, &ty.docs), TypeDefKind::Unknown => unreachable!(), TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), } } @@ -2204,6 +2204,22 @@ impl<'a> InterfaceGenerator<'a> { } } + fn type_map(&mut self, id: TypeId, _name: &str, key: &Type, value: &Type, docs: &Docs) { + let info = self.info(id); + for (name, mode) in self.modes_of(id) { + let lt = self.lifetime_for(&info, mode); + self.rustdoc(docs); + self.push_str(&format!("pub type {name}")); + self.print_generics(lt); + self.push_str(" = "); + let key_ty = self.ty(key, mode); + let value_ty = self.ty(value, mode); + self.push_str(&format!("std::collections::HashMap<{key_ty}, {value_ty}>")); + self.push_str(";\n"); + self.assert_type(id, &name); + } + } + fn type_stream(&mut self, id: TypeId, name: &str, ty: Option<&Type>, docs: &Docs) { self.rustdoc(docs); self.push_str(&format!("pub type {name}")); @@ -3416,8 +3432,10 @@ fn type_contains_lists(ty: Type, resolve: &Resolve) -> bool { .any(|case| option_type_contains_lists(case.ty, resolve)), TypeDefKind::Type(ty) => type_contains_lists(*ty, resolve), TypeDefKind::List(_) => true, + TypeDefKind::Map(k, v) => { + type_contains_lists(*k, resolve) || type_contains_lists(*v, resolve) + } TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), }, // Technically strings are lists too, but we ignore that here because diff --git a/crates/wit-bindgen/src/rust.rs b/crates/wit-bindgen/src/rust.rs index 2ba39559ed80..d33987812a82 100644 --- a/crates/wit-bindgen/src/rust.rs +++ b/crates/wit-bindgen/src/rust.rs @@ -121,6 +121,7 @@ pub trait RustGenerator<'a> { | TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::List(_) + | TypeDefKind::Map(_, _) | TypeDefKind::Flags(_) | TypeDefKind::Enum(_) | TypeDefKind::Tuple(_) @@ -133,7 +134,6 @@ pub trait RustGenerator<'a> { TypeDefKind::Type(_) => false, TypeDefKind::Unknown => unreachable!(), TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), } } } @@ -188,9 +188,13 @@ pub trait RustGenerator<'a> { TypeDefKind::Resource => unreachable!(), TypeDefKind::Type(t) => self.ty(t, mode), + TypeDefKind::Map(k, v) => { + let key = self.ty(k, mode); + let value = self.ty(v, mode); + format!("std::collections::HashMap<{key}, {value}>") + } TypeDefKind::Unknown => unreachable!(), TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), } } diff --git a/crates/wit-bindgen/src/types.rs b/crates/wit-bindgen/src/types.rs index 200d13a82f93..7dd53a86c1b0 100644 --- a/crates/wit-bindgen/src/types.rs +++ b/crates/wit-bindgen/src/types.rs @@ -148,6 +148,11 @@ impl Types { info = self.type_info(resolve, ty); info.has_list = true; } + TypeDefKind::Map(k, v) => { + info = self.type_info(resolve, k); + info |= self.type_info(resolve, v); + info.has_list = true; + } TypeDefKind::Type(ty) | TypeDefKind::Option(ty) => { info = self.type_info(resolve, ty); } @@ -163,7 +168,6 @@ impl Types { TypeDefKind::Resource => {} TypeDefKind::Unknown => unreachable!(), TypeDefKind::FixedLengthList(..) => todo!(), - TypeDefKind::Map(..) => todo!(), } self.type_info.insert(ty, info); info diff --git a/crates/wizer/README.md b/crates/wizer/README.md index 1f6fdb0d2912..d62566f4f0be 100644 --- a/crates/wizer/README.md +++ b/crates/wizer/README.md @@ -131,7 +131,7 @@ initialization function. Then we record the Wasm instance's state: * What are the values of its globals? * What regions of memory are non-zero? -Then we rewrite the Wasm binary by intializing its globals directly to their +Then we rewrite the Wasm binary by initializing its globals directly to their recorded state, and removing the module's old data segments and replacing them with data segments for each of the non-zero regions of memory we recorded. diff --git a/crates/wizer/src/component/wasmtime.rs b/crates/wizer/src/component/wasmtime.rs index df337456ca40..8c12d86bac22 100644 --- a/crates/wizer/src/component/wasmtime.rs +++ b/crates/wizer/src/component/wasmtime.rs @@ -75,7 +75,7 @@ impl Wizer { } } -/// Impementation of [`ComponentInstanceState`] backed by Wasmtime. +/// Implementation of [`ComponentInstanceState`] backed by Wasmtime. pub struct WasmtimeWizerComponent<'a, T: 'static> { /// The Wasmtime-based store that owns the `instance` field. pub store: &'a mut Store, diff --git a/crates/wizer/src/wasmtime.rs b/crates/wizer/src/wasmtime.rs index 7ce9fd6e1b6f..8a85d0dbcd61 100644 --- a/crates/wizer/src/wasmtime.rs +++ b/crates/wizer/src/wasmtime.rs @@ -86,7 +86,7 @@ impl Wizer { } } -/// Impementation of [`InstanceState`] backed by Wasmtime. +/// Implementation of [`InstanceState`] backed by Wasmtime. pub struct WasmtimeWizer<'a, T: 'static> { /// The Wasmtime-based store that owns the `instance` field. pub store: &'a mut Store, diff --git a/docs/security.md b/docs/security.md index 77c527c0a4ba..94228f10ca26 100644 --- a/docs/security.md +++ b/docs/security.md @@ -134,3 +134,7 @@ Wasmtime implements a few forms of basic spectre mitigations at this time: Mitigating Spectre continues to be a subject of ongoing research, and Wasmtime will likely grow more mitigations in the future as well. + +Note that on aarch64 the `csdb` instruction is disabled by default due to its +significant performance penalty, but this can be additionally enabled through +the `use_csdb` Cranelift setting. diff --git a/docs/stability-platform-support.md b/docs/stability-platform-support.md index db54c0a7ecef..82e1ddd4fb01 100644 --- a/docs/stability-platform-support.md +++ b/docs/stability-platform-support.md @@ -83,6 +83,11 @@ Cargo features are: * `gc` * `component-model` * `pulley` +* `async` +* `debug` +* `debug-builtins` +* `demangle` +* `anyhow` This notably does not include the `default` feature which means that when depending on Wasmtime you'll need to specify `default-features = false`. This diff --git a/docs/stability-wasm-proposals.md b/docs/stability-wasm-proposals.md index 68607b0ae020..6287f1be2b52 100644 --- a/docs/stability-wasm-proposals.md +++ b/docs/stability-wasm-proposals.md @@ -47,9 +47,9 @@ The emoji legend is: | Proposal | Phase 4 | Tests | Finished | Fuzzed | API | C API | |--------------------------|---------|-------|----------|--------|-----|--------| | [`custom-page-sizes`] | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [`exception-handling`] | ✅ | ✅ | ✅ | 🚧[^9] | ✅ | 🚧[^10]| +| [`exception-handling`] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [`function-references`] | ✅ | ✅ | ✅ | 🚧 | ✅ | ❌ | -| [`gc`] [^5] | ✅ | ✅ | 🚧[^6] | 🚧[^7] | ✅ | ❌ | +| [`gc`] [^5] | ✅ | ✅ | 🚧[^6] | 🚧[^7] | ✅ | ✅ | | [`threads`] | ✅ | ✅ | 🚧[^8] | ❌[^4] | ✅ | ✅ | | [`wide-arithmetic`] | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -71,12 +71,6 @@ The emoji legend is: shared memories and the implementation/API in Wasmtime, for example they aren't well integrated with resource-limiting features in `Store`. Additionally `shared` memories aren't supported in the pooling allocator. -[^9]: The exception-handling proposal is fuzzed by our whole-module fuzzer, - but we do not have an exception-specific fuzzer that attempts to create - interesting throw/catch patterns or payloads. -[^10]: The exception-handling proposal can be enabled for exceptions in the guest - via the C API, but exception objects have no C API to examine, clone, - rethrow, or drop exceptions that propagate to the host. [cm-capi-gaps]: https://github.com/bytecodealliance/wasmtime/issues?q=is%3Aissue%20state%3Aopen%20label%3Awasm-proposal%3Acomponent-model%20label%3Awasmtime%3Ac-api diff --git a/examples/min-platform/embedding/src/wasi.rs b/examples/min-platform/embedding/src/wasi.rs index b7a36b0b7991..96f957f53c4e 100644 --- a/examples/min-platform/embedding/src/wasi.rs +++ b/examples/min-platform/embedding/src/wasi.rs @@ -217,6 +217,11 @@ pub fn add_to_linker_async(linker: &mut Linker) -> Result<()> { wasi::cli::stdout::add_to_linker::<_, Data>(linker, |t| t)?; wasi::cli::stderr::add_to_linker::<_, Data>(linker, |t| t)?; wasi::random::random::add_to_linker::<_, Data>(linker, |t| t)?; + wasi::cli::terminal_input::add_to_linker::<_, Data>(linker, |t| t)?; + wasi::cli::terminal_output::add_to_linker::<_, Data>(linker, |t| t)?; + wasi::cli::terminal_stdin::add_to_linker::<_, Data>(linker, |t| t)?; + wasi::cli::terminal_stdout::add_to_linker::<_, Data>(linker, |t| t)?; + wasi::cli::terminal_stderr::add_to_linker::<_, Data>(linker, |t| t)?; wasi::filesystem::preopens::add_to_linker::<_, Data>(linker, |t| t)?; wasi::filesystem::types::add_to_linker::<_, Data>(linker, |t| t)?; Ok(()) @@ -564,6 +569,43 @@ impl wasi::cli::stderr::Host for ExampleCtx { } } +// Terminal resources: nothing is a terminal in this minimal embedding. +impl wasi::cli::terminal_input::Host for ExampleCtx {} +impl wasi::cli::terminal_input::HostTerminalInput for ExampleCtx { + fn drop(&mut self, r: Resource) -> Result<()> { + self.table.delete(r)?; + Ok(()) + } +} +impl wasi::cli::terminal_output::Host for ExampleCtx {} +impl wasi::cli::terminal_output::HostTerminalOutput for ExampleCtx { + fn drop(&mut self, r: Resource) -> Result<()> { + self.table.delete(r)?; + Ok(()) + } +} +impl wasi::cli::terminal_stdin::Host for ExampleCtx { + fn get_terminal_stdin( + &mut self, + ) -> Result>> { + Ok(None) + } +} +impl wasi::cli::terminal_stdout::Host for ExampleCtx { + fn get_terminal_stdout( + &mut self, + ) -> Result>> { + Ok(None) + } +} +impl wasi::cli::terminal_stderr::Host for ExampleCtx { + fn get_terminal_stderr( + &mut self, + ) -> Result>> { + Ok(None) + } +} + // This is obviously bogus and breaks the guarantees given by this interface. // In a real embedding, provide a high quality source of randomness here. impl wasi::random::random::Host for ExampleCtx { diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 6b9f213b5211..499c8fb9ca3c 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -112,6 +112,13 @@ test = false doc = false bench = false +[[bin]] +name = "exception_ops" +path = "fuzz_targets/exception_ops.rs" +test = false +doc = false +bench = false + [[bin]] name = "gc_ops" path = "fuzz_targets/gc_ops.rs" diff --git a/fuzz/fuzz_targets/exception_ops.rs b/fuzz/fuzz_targets/exception_ops.rs new file mode 100644 index 000000000000..4238bb74d0e6 --- /dev/null +++ b/fuzz/fuzz_targets/exception_ops.rs @@ -0,0 +1,58 @@ +#![no_main] + +use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured}; +use libfuzzer_sys::{fuzz_mutator, fuzz_target, fuzzer_mutate}; +use mutatis::Session; +use postcard::{from_bytes, to_slice}; +use rand::{Rng, SeedableRng}; +use wasmtime_fuzzing::generators::ExceptionOps; +use wasmtime_fuzzing::oracles::exception_ops; + +fuzz_target!(|data: &[u8]| { + let Ok((seed, ops)) = postcard::from_bytes::<(u64, ExceptionOps)>(data) else { + return; + }; + + let mut buf = [0u8; 1024]; + let mut rng = rand::rngs::StdRng::seed_from_u64(seed); + rng.fill(&mut buf); + + let u = Unstructured::new(&buf); + let Ok(config) = wasmtime_fuzzing::generators::Config::arbitrary_take_rest(u) else { + return; + }; + + let _ = exception_ops(config, ops); +}); + +fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| { + let _ = env_logger::try_init(); + + // With probability of about 1/8, use default mutator + if seed & 7 == 0 { + return fuzzer_mutate(data, size, max_size); + } + + // Try to decode using postcard; fallback to default input on failure + let mut tuple: (u64, ExceptionOps) = from_bytes(&data[..size]).ok().unwrap_or_default(); + + let mut session = Session::new().seed(seed.into()).shrink(max_size < size); + + if session.mutate(&mut tuple).is_ok() { + loop { + if let Ok(encoded) = to_slice(&tuple, data) { + return encoded.len(); + } + + // Attempt to shrink scenarios if encoding fails (e.g., buffer too small) + if tuple.1.pop() { + continue; + } + + break; + } + } + + // Fallback to default libfuzzer mutator + fuzzer_mutate(data, size, max_size) +}); diff --git a/pulley/Cargo.toml b/pulley/Cargo.toml index 8e4b46c189f4..58ed092fa4e2 100644 --- a/pulley/Cargo.toml +++ b/pulley/Cargo.toml @@ -40,4 +40,4 @@ all-features = true [[example]] name = "profiler-html" -required-features = ["profile"] +required-features = ["disas", "interp", "profile"] diff --git a/pulley/src/disas.rs b/pulley/src/disas.rs index 3e91c8fc1dc6..c0e194f0dfbc 100644 --- a/pulley/src/disas.rs +++ b/pulley/src/disas.rs @@ -321,7 +321,7 @@ macro_rules! impl_disas { )* }; - // Diassembling `br_table` is a bit special as it has trailing byte after + // Disassembling `br_table` is a bit special as it has trailing byte after // the opcode of the branch table itself. ( @one br_table32 = BrTable32 $( { diff --git a/pulley/src/interp.rs b/pulley/src/interp.rs index aac2cbac8551..940d35237b79 100644 --- a/pulley/src/interp.rs +++ b/pulley/src/interp.rs @@ -6,13 +6,14 @@ use crate::imms::*; use crate::profile::{ExecutingPc, ExecutingPcRef}; use crate::regs::*; use alloc::string::ToString; -use alloc::vec::Vec; use core::fmt; use core::mem; use core::ops::ControlFlow; use core::ops::{Index, IndexMut}; use core::ptr::NonNull; use pulley_macros::interp_disable_if_cfg; +use wasmtime_core::alloc::TryVec; +use wasmtime_core::error::OutOfMemory; use wasmtime_core::math::{WasmFloat, f32_cvt_to_int_bounds, f64_cvt_to_int_bounds}; mod debug; @@ -29,24 +30,18 @@ pub struct Vm { executing_pc: ExecutingPc, } -impl Default for Vm { - fn default() -> Self { - Vm::new() - } -} - impl Vm { /// Create a new virtual machine with the default stack size. - pub fn new() -> Self { + pub fn new() -> Result { Self::with_stack(DEFAULT_STACK_SIZE) } /// Create a new virtual machine with the given stack. - pub fn with_stack(stack_size: usize) -> Self { - Self { - state: MachineState::with_stack(stack_size), + pub fn with_stack(stack_size: usize) -> Result { + Ok(Self { + state: MachineState::with_stack(stack_size)?, executing_pc: ExecutingPc::default(), - } + }) } /// Get a shared reference to this VM's machine state. @@ -137,7 +132,7 @@ impl Vm { mem::replace(&mut self.state.lr, HOST_RETURN_ADDR) } - /// Peforms the internal part of [`Vm::call`] where bytecode is actually + /// Performs the internal part of [`Vm::call`] where bytecode is actually /// executed. /// /// # Unsafety @@ -156,7 +151,7 @@ impl Vm { self.state.done_decode(done) } - /// Peforms the tail end of [`Vm::call`] by returning the values as + /// Performs the tail end of [`Vm::call`] by returning the values as /// determined by `rets` according to Pulley's ABI. /// /// The `old_ret` value should have been provided from `call_start` @@ -762,7 +757,7 @@ unsafe impl Sync for MachineState {} /// done with a custom `Vec` internally where `T` has size and align of 16. /// This is manually done with a helper `Align16` type below. struct Stack { - storage: Vec, + storage: TryVec, } /// Helper type used with `Stack` above. @@ -778,14 +773,14 @@ impl Stack { /// Creates a new stack which will have a byte size of at least `size`. /// /// The allocated stack might be slightly larger due to rounding necessary. - fn new(size: usize) -> Stack { - Stack { - // Round up `size` to the nearest multiple of 16. Note that the - // stack is also allocated here but not initialized, and that's - // intentional as pulley bytecode should always initialize the stack - // before use. - storage: Vec::with_capacity((size + 15) / 16), - } + fn new(size: usize) -> Result { + let mut storage = TryVec::new(); + // Round up `size` to the nearest multiple of 16. Note that the + // stack is also allocated here but not initialized, and that's + // intentional as pulley bytecode should always initialize the stack + // before use. + storage.reserve_exact(size.checked_next_multiple_of(16).unwrap_or(usize::MAX) / 16)?; + Ok(Stack { storage }) } /// Returns a pointer to the top of the stack (the highest address). @@ -896,13 +891,13 @@ index_reg!(VReg, VRegVal, v_regs); const HOST_RETURN_ADDR: *mut u8 = usize::MAX as *mut u8; impl MachineState { - fn with_stack(stack_size: usize) -> Self { + fn with_stack(stack_size: usize) -> Result { let mut state = Self { x_regs: [Default::default(); XReg::RANGE.end as usize], f_regs: Default::default(), #[cfg(not(pulley_disable_interp_simd))] v_regs: Default::default(), - stack: Stack::new(stack_size), + stack: Stack::new(stack_size)?, done_reason: None, fp: HOST_RETURN_ADDR, lr: HOST_RETURN_ADDR, @@ -911,7 +906,7 @@ impl MachineState { let sp = state.stack.top(); state[XReg::sp] = XRegVal::new_ptr(sp); - state + Ok(state) } } @@ -1294,7 +1289,7 @@ impl AddressingMode for AddrG32Bne { #[test] fn simple_push_pop() { - let mut state = MachineState::with_stack(16); + let mut state = MachineState::with_stack(16).unwrap(); let pc = ExecutingPc::default(); unsafe { let mut bytecode = [0; 10]; @@ -2193,7 +2188,7 @@ impl OpVisitor for Interpreter<'_> { self.state[operands.dst].set_u32(result); ControlFlow::Continue(()) } - None => self.done_trap_kind::(Some(TrapKind::DivideByZero)), + None => self.done_trap_kind::(Some(TrapKind::DivideByZero)), } } diff --git a/pulley/src/interp/tail_loop.rs b/pulley/src/interp/tail_loop.rs index 55c5b684307a..c9d7e952e954 100644 --- a/pulley/src/interp/tail_loop.rs +++ b/pulley/src/interp/tail_loop.rs @@ -128,9 +128,7 @@ static OPCODE_HANDLER_TABLE: [Handler; Opcode::MAX as usize + 1] = { for_each_op!(define_opcode_handler_table) }; -// same as above, but without a +1 for handling of extended ops as this is the -// extended ops. -static EXTENDED_OPCODE_HANDLER_TABLE: [Handler; ExtendedOpcode::MAX as usize] = { +static EXTENDED_OPCODE_HANDLER_TABLE: [Handler; ExtendedOpcode::MAX as usize + 1] = { macro_rules! define_extended_opcode_handler_table { ($( $( #[$attr:meta] )* diff --git a/pulley/src/lib.rs b/pulley/src/lib.rs index 40d712220621..dba201017df1 100644 --- a/pulley/src/lib.rs +++ b/pulley/src/lib.rs @@ -1392,7 +1392,7 @@ pub mod disas; pub mod encode; #[cfg(feature = "interp")] pub mod interp; -#[cfg(feature = "profile")] +#[cfg(all(feature = "profile", feature = "interp"))] pub mod profile; #[cfg(all(not(feature = "profile"), feature = "interp"))] mod profile_disabled; @@ -1411,7 +1411,7 @@ pub use op::*; pub mod opcode; pub use opcode::*; -#[cfg(any(feature = "encode", feature = "decode"))] +#[cfg(feature = "decode")] pub(crate) unsafe fn unreachable_unchecked() -> ! { #[cfg(debug_assertions)] unreachable!(); diff --git a/pulley/src/opcode.rs b/pulley/src/opcode.rs index 0400922126a7..0aa58ace6e4e 100644 --- a/pulley/src/opcode.rs +++ b/pulley/src/opcode.rs @@ -76,7 +76,7 @@ macro_rules! define_extended_opcode { /// The value of the maximum defined extended opcode. pub const MAX: u16 = $( if true { 1 } else { ExtendedOpcode::$name as u16 } + - )* 0; + )* 0 - 1; } }; } @@ -104,3 +104,16 @@ impl ExtendedOpcode { unsafe { core::mem::transmute(byte) } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn max_values() { + assert!(Opcode::new(Opcode::MAX).is_some()); + assert!(Opcode::new(Opcode::MAX + 1).is_none()); + assert!(ExtendedOpcode::new(ExtendedOpcode::MAX).is_some()); + assert!(ExtendedOpcode::new(ExtendedOpcode::MAX + 1).is_none()); + } +} diff --git a/pulley/src/regs.rs b/pulley/src/regs.rs index c7de6930ad19..f9bad41a2b1d 100644 --- a/pulley/src/regs.rs +++ b/pulley/src/regs.rs @@ -332,7 +332,7 @@ impl<'a, R: Reg> arbitrary::Arbitrary<'a> for UpperRegSet { } } -/// Immediate used for the "o32" addresing mode. +/// Immediate used for the "o32" addressing mode. /// /// This addressing mode represents a host address stored in `self.addr` which /// is byte-offset by `self.offset`. @@ -347,7 +347,7 @@ pub struct AddrO32 { pub offset: i32, } -/// Immediate used for the "z" addresing mode. +/// Immediate used for the "z" addressing mode. /// /// This addressing mode represents a host address stored in `self.addr` which /// is byte-offset by `self.offset`. diff --git a/pulley/tests/all/interp.rs b/pulley/tests/all/interp.rs index 33203240f3dd..5dda64a2adcf 100644 --- a/pulley/tests/all/interp.rs +++ b/pulley/tests/all/interp.rs @@ -37,7 +37,7 @@ unsafe fn assert_one( V: Into, { eprintln!("======================================================="); - let mut vm = Vm::new(); + let mut vm = Vm::new().unwrap(); for (reg, val) in xs { let reg = reg.into(); @@ -797,7 +797,7 @@ fn bitcast_float_from_int_64() { #[test] fn trap() { - let mut vm = Vm::new(); + let mut vm = Vm::new().unwrap(); let dst = XReg::new(0).unwrap(); unsafe { diff --git a/scripts/publish.rs b/scripts/publish.rs index 3bbb1cdc1dc2..2f33c6c56113 100644 --- a/scripts/publish.rs +++ b/scripts/publish.rs @@ -75,8 +75,6 @@ const CRATES_TO_PUBLISH: &[&str] = &[ "wasmtime-wasi-keyvalue", "wasmtime-wasi-threads", "wasmtime-wasi-tls", - "wasmtime-wasi-tls-nativetls", - "wasmtime-wasi-tls-openssl", "wasmtime-wast", "wasmtime-internal-c-api-macros", "wasmtime-c-api-impl", @@ -84,6 +82,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[ "wasmtime-cli-flags", "wasmtime-internal-explorer", "wasmtime-internal-debugger", + "wasmtime-internal-gdbstub-component-artifact", "wasmtime-cli", ]; @@ -98,8 +97,6 @@ const PUBLIC_CRATES: &[&str] = &[ "wasmtime-wasi-io", "wasmtime-wasi", "wasmtime-wasi-tls", - "wasmtime-wasi-tls-nativetls", - "wasmtime-wasi-tls-openssl", "wasmtime-wasi-http", "wasmtime-wasi-nn", "wasmtime-wasi-config", diff --git a/src/commands/objdump.rs b/src/commands/objdump.rs index f85e513340d7..8cc28e57e155 100644 --- a/src/commands/objdump.rs +++ b/src/commands/objdump.rs @@ -16,7 +16,7 @@ use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use wasmtime::{Engine, Result, bail, error::Context as _}; use wasmtime_environ::{ FilePos, FrameInstPos, FrameStackShape, FrameStateSlot, FrameTable, FrameTableDescriptorIndex, - StackMap, Trap, obj, + ModulePC, StackMap, Trap, obj, }; use wasmtime_unwinder::{ExceptionHandler, ExceptionTable}; @@ -573,7 +573,7 @@ struct Decorator<'a> { Item = ( u32, FrameInstPos, - Vec<(u32, FrameTableDescriptorIndex, FrameStackShape)>, + Vec<(ModulePC, FrameTableDescriptorIndex, FrameStackShape)>, ), > + 'a, >, @@ -583,7 +583,7 @@ struct Decorator<'a> { // Breakpoint table, sorted by native offset instead so we can // display inline with disassembly (the table in the image is // sorted by Wasm PC). - breakpoints: Peekable)>>>, + breakpoints: Peekable)>>>, frame_table_descriptors: Option>, } diff --git a/src/commands/run.rs b/src/commands/run.rs index 91aa2bf2435c..2d69b41d819e 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -9,6 +9,8 @@ use crate::common::{Profile, RunCommon, RunTarget}; use clap::Parser; use std::ffi::OsString; use std::path::{Path, PathBuf}; +#[cfg(feature = "debug")] +use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::thread; use wasi_common::sync::{Dir, TcpListener, WasiCtxBuilder, ambient_authority}; @@ -21,17 +23,13 @@ use wasmtime_wasi::{WasiCtxView, WasiView}; #[cfg(feature = "wasi-config")] use wasmtime_wasi_config::{WasiConfig, WasiConfigVariables}; #[cfg(feature = "wasi-http")] -use wasmtime_wasi_http::{ - DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS, DEFAULT_OUTGOING_BODY_CHUNK_SIZE, WasiHttpCtx, -}; +use wasmtime_wasi_http::WasiHttpCtx; #[cfg(feature = "wasi-keyvalue")] use wasmtime_wasi_keyvalue::{WasiKeyValue, WasiKeyValueCtx, WasiKeyValueCtxBuilder}; #[cfg(feature = "wasi-nn")] use wasmtime_wasi_nn::wit::WasiNnView; #[cfg(feature = "wasi-threads")] use wasmtime_wasi_threads::WasiThreadsCtx; -#[cfg(feature = "wasi-tls")] -use wasmtime_wasi_tls::{WasiTls, WasiTlsCtx}; fn parse_preloads(s: &str) -> Result<(String, PathBuf)> { let parts: Vec<&str> = s.splitn(2, '=').collect(); @@ -65,6 +63,14 @@ pub struct RunCommand { #[arg(long)] pub argv0: Option, + /// Override the module bytes loaded from disk. When set, the + /// first positional argument is ignored for loading purposes and + /// these bytes are used instead. This is not a CLI option; it is + /// used internally to inject pre-built bytes (e.g. for an + /// included debug adapter). + #[arg(skip)] + pub module_bytes: Option<&'static [u8]>, + /// The WebAssembly module to run and arguments to pass to it. /// /// Arguments passed to the wasm module will be configured as WASI CLI @@ -74,6 +80,121 @@ pub struct RunCommand { pub module_and_args: Vec, } +impl RunCommand { + /// Split off a sub-command representing the invocation of a + /// debugger component side-car to this execution. + /// + /// This is used to factor out most of the environment bringup for + /// the debugger component environment. + /// + /// This also adjusts the guest options as needed to enable + /// debugging (e.g., implicitly set `-D guest-debug=y`). + #[cfg(feature = "debug")] + pub(crate) fn debugger_run(&mut self) -> Result> { + fn set_implicit_option( + place: &str, + name: &str, + setting: &mut Option, + value: bool, + ) -> Result<()> { + if *setting == Some(!value) { + bail!( + "Explicitly-set option on {place} {name}={} is not compatible with debugging-implied setting {value}", + setting.unwrap() + ); + } + *setting = Some(value); + Ok(()) + } + + // When -g is specified, set up the debugger path and args from + // the built-in gdbstub component. + #[cfg(feature = "gdbstub")] + let override_bytes = if let Some(addr) = self.run.gdbstub.as_deref() { + if self.run.common.debug.debugger.is_some() { + bail!("-g/--gdb cannot be combined with -Ddebugger="); + } + // Accept either a bare port number or a full address:port. + let addr = if addr.parse::().is_ok() { + format!("127.0.0.1:{addr}") + } else { + use std::net::SocketAddr; + addr.parse::() + .with_context(|| format!("invalid gdbstub address: `{addr}`"))?; + addr.to_string() + }; + self.run.common.debug.debugger = Some("".into()); + self.run.common.debug.arg.push(addr); + Some(gdbstub_component_artifact::GDBSTUB_COMPONENT) + } else { + None + }; + #[cfg(not(feature = "gdbstub"))] + let override_bytes = None; + + if let Some(debugger_component_path) = self.run.common.debug.debugger.as_ref() { + set_implicit_option( + "debuggee", + "guest_debug", + &mut self.run.common.debug.guest_debug, + true, + )?; + set_implicit_option( + "debuggee", + "epoch_interruption", + &mut self.run.common.wasm.epoch_interruption, + true, + )?; + + let mut debugger_run = RunCommand::try_parse_from( + ["run".into(), debugger_component_path.into()] + .into_iter() + .chain(self.run.common.debug.arg.iter().map(OsString::from)), + )?; + debugger_run.module_bytes = override_bytes; + + // Explicitly permit TCP sockets for the debugger-main + // environment, if not already set. + debugger_run.run.common.wasi.tcp.get_or_insert(true); + debugger_run + .run + .common + .wasi + .inherit_network + .get_or_insert(true); + + // Copy over stdin/stdout/stderr inheritance settings, + // except default to `false` for the debugger (so it + // doesn't interfere with the debuggee's CLI interface, if + // any). We expect most debug components will serve an + // interface over the network; for those that want a TUI, + // their setup instructions can instruct the user to set + // these flags as needed. + set_implicit_option( + "debugger", + "inherit_stdin", + &mut debugger_run.run.common.wasi.inherit_stdin, + self.run.common.debug.inherit_stdin.unwrap_or(false), + )?; + set_implicit_option( + "debugger", + "inherit_stdout", + &mut debugger_run.run.common.wasi.inherit_stdout, + self.run.common.debug.inherit_stdout.unwrap_or(false), + )?; + set_implicit_option( + "debugger", + "inherit_stderr", + &mut debugger_run.run.common.wasi.inherit_stderr, + self.run.common.debug.inherit_stderr.unwrap_or(false), + )?; + Ok(Some(debugger_run)) + } else { + Ok(None) + } + } +} + #[expect(missing_docs, reason = "don't want to mess with clap doc-strings")] #[derive(Parser, Default, Clone)] pub struct Preloads { @@ -115,12 +236,85 @@ impl RunCommand { runtime.block_on(async { self.run.common.init_logging()?; + #[cfg(feature = "debug")] + let debug_run = self.debugger_run()?; + let engine = self.new_engine()?; - let main = self - .run - .load_module(&engine, self.module_and_args[0].as_ref())?; + let main = self.run.load_module( + &engine, + self.module_and_args[0].as_ref(), + self.module_bytes.as_ref().map(|v| &v[..]), + )?; let (mut store, mut linker) = self.new_store_and_linker(&engine, &main)?; + #[cfg(feature = "debug")] + if let Some(mut debug_run) = debug_run { + let debug_engine = debug_run.new_engine()?; + let debug_main = debug_run.run.load_module( + &debug_engine, + debug_run.module_and_args[0].as_ref(), + debug_run.module_bytes.as_ref().map(|v| &v[..]), + )?; + let (mut debug_store, debug_linker) = + debug_run.new_store_and_linker(&debug_engine, &debug_main)?; + + let debug_component = match debug_main { + RunTarget::Core(_) => wasmtime::bail!( + "Debugger component is a core module; only components are supported" + ), + RunTarget::Component(c) => c, + }; + let mut debug_linker = match debug_linker { + CliLinker::Core(_) => unreachable!(), + CliLinker::Component(l) => l, + }; + debug_run.add_debugger_api(&mut debug_linker)?; + + // Pre-register the main module on the debuggee store + // so that `debug_all_modules()` returns it before any + // Wasm executes. This lets the debugger see modules + // and set breakpoints at the initial stop. + match &main { + RunTarget::Core(m) => { + store.debug_register_module(m)?; + } + #[cfg(feature = "component-model")] + RunTarget::Component(c) => { + store.debug_register_component(c)?; + } + } + + debug_run + .invoke_debugger( + &mut debug_store, + &debug_component, + &mut debug_linker, + store, + move |store| { + Box::pin(async move { + let engine_clone = store.engine().clone(); + let cancel = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let cancel_clone = cancel.clone(); + let epoch_thread = thread::spawn(move || { + while !cancel_clone.load(std::sync::atomic::Ordering::Relaxed) { + thread::sleep(std::time::Duration::from_millis(1)); + engine_clone.increment_epoch(); + } + }); + self.instantiate_and_run(&engine, &mut linker, &main, store) + .await?; + cancel.store(true, std::sync::atomic::Ordering::Relaxed); + epoch_thread + .join() + .map_err(|_| wasmtime::Error::msg("epoch thread panicked"))?; + Ok(()) + }) + }, + ) + .await?; + return Ok(()); + } + self.instantiate_and_run(&engine, &mut linker, &main, &mut store) .await?; Ok(()) @@ -148,7 +342,7 @@ impl RunCommand { Engine::new(&config) } - /// Populatse a new `Store` and `CliLinker` with the configuration in this + /// Populates a new `Store` and `CliLinker` with the configuration in this /// command. /// /// The `engine` provided is used to for the store/linker and the `main` @@ -184,17 +378,7 @@ impl RunCommand { } } - let host = Host { - #[cfg(feature = "wasi-http")] - wasi_http_outgoing_body_buffer_chunks: self - .run - .common - .wasi - .http_outgoing_body_buffer_chunks, - #[cfg(feature = "wasi-http")] - wasi_http_outgoing_body_chunk_size: self.run.common.wasi.http_outgoing_body_chunk_size, - ..Default::default() - }; + let host = Host::default(); let mut store = Store::new(&engine, host); self.populate_with_wasi(&mut linker, &mut store, &main)?; @@ -211,6 +395,15 @@ impl RunCommand { Ok((store, linker)) } + #[cfg(feature = "debug")] + pub(crate) fn add_debugger_api( + &mut self, + linker: &mut wasmtime::component::Linker, + ) -> Result<()> { + wasmtime_debugger::add_to_linker(linker, |x| x.ctx().table)?; + Ok(()) + } + /// Executes the `main` after instantiating it within `store`. /// /// This applies all configuration within `self`, such as timeouts and @@ -238,7 +431,7 @@ impl RunCommand { // Load the preload wasm modules. for (name, path) in self.preloads.modules.iter() { // Read the wasm module binary either as `*.wat` or a raw binary - let preload_target = self.run.load_module(&engine, path)?; + let preload_target = self.run.load_module(&engine, path, None)?; let preload_module = match preload_target { RunTarget::Core(m) => m, #[cfg(feature = "component-model")] @@ -320,7 +513,7 @@ impl RunCommand { Ok(instance) } - fn compute_argv(&self) -> Result> { + pub(crate) fn compute_argv(&self) -> Result> { let mut result = Vec::new(); for (i, arg) in self.module_and_args.iter().enumerate() { @@ -349,30 +542,45 @@ impl RunCommand { store: &mut Store, main_target: &RunTarget, profiled_modules: Vec<(String, Module)>, - ) -> Result)>> { - if let Some(Profile::Guest { path, interval }) = &self.run.profile { - #[cfg(feature = "profiling")] - return Ok(self.setup_guest_profiler( - store, - main_target, - profiled_modules, - path, - *interval, - )?); - #[cfg(not(feature = "profiling"))] - { - let _ = (profiled_modules, path, interval, main_target); - bail!("support for profiling disabled at compile time"); + ) -> Result) + Send>> { + // If a debugger component is attached, we set up epoch + // interruptions in `debugger_run()` above when enabling guest + // instrumentation; we need to ensure that epoch interruptions + // cause a debug event but no trap here. This overrides other + // behavior below. + if self.run.common.debug.debugger.is_some() { + if self.run.profile.is_some() { + bail!("Cannot set profile options together with debugging; they are incompatible"); + } + if self.run.common.wasm.timeout.is_some() { + bail!("Cannot set timeout options together with debugging; they are incompatible"); + } + store.epoch_deadline_async_yield_and_update(1); + } else { + if let Some(Profile::Guest { path, interval }) = &self.run.profile { + #[cfg(feature = "profiling")] + return Ok(self.setup_guest_profiler( + store, + main_target, + profiled_modules, + path, + *interval, + )?); + #[cfg(not(feature = "profiling"))] + { + let _ = (profiled_modules, path, interval, main_target); + bail!("support for profiling disabled at compile time"); + } } - } - if let Some(timeout) = self.run.common.wasm.timeout { - store.set_epoch_deadline(1); - let engine = store.engine().clone(); - thread::spawn(move || { - thread::sleep(timeout); - engine.increment_epoch(); - }); + if let Some(timeout) = self.run.common.wasm.timeout { + store.set_epoch_deadline(1); + let engine = store.engine().clone(); + thread::spawn(move || { + thread::sleep(timeout); + engine.increment_epoch(); + }); + } } Ok(Box::new(|_store| {})) @@ -386,7 +594,7 @@ impl RunCommand { profiled_modules: Vec<(String, Module)>, path: &str, interval: std::time::Duration, - ) -> Result)>> { + ) -> Result) + Send>> { use wasmtime::{AsContext, GuestProfiler, StoreContext, StoreContextMut, UpdateDeadline}; let module_name = self.module_and_args[0].to_str().unwrap_or("
"); @@ -693,6 +901,47 @@ impl RunCommand { } } + /// Invoke a debugger component with a debuggee. + /// + /// The debugger runs in `store` (using run's `Host`), while the + /// debuggee wraps an arbitrary store type `T` and body closure. + #[cfg(feature = "debug")] + pub(crate) async fn invoke_debugger< + T: Send + 'static, + F: for<'a> FnOnce( + &'a mut Store, + ) -> Pin> + Send + 'a>> + + Send + + 'static, + >( + &self, + store: &mut Store, + component: &wasmtime::component::Component, + linker: &mut wasmtime::component::Linker, + debuggee_host: Store, + body: F, + ) -> Result<()> { + let instance = linker.instantiate_async(&mut *store, component).await?; + let command = wasmtime_debugger::DebuggerComponent::new(&mut *store, &instance)?; + let debuggee = wasmtime_debugger::Debuggee::new(debuggee_host, body); + let debuggee = wasmtime_debugger::add_debuggee(store.data_mut().ctx().table, debuggee)?; + { + // Manually construct a borrow -- wasmtime-wit-bindgen + // generates code that consumes the `Resource` for + // `call_debug()` below even though the WIT type is a + // `borrow`. + let borrowed = wasmtime::component::Resource::new_borrow(debuggee.rep()); + let args = self.compute_argv()?; + command + .bytecodealliance_wasmtime_debugger() + .call_debug(&mut *store, borrowed, &args) + .await?; + } + let mut debuggee = store.data_mut().ctx().table.delete(debuggee)?; + debuggee.finish().await?; + Ok(()) + } + #[cfg(feature = "component-model")] fn search_component_funcs( engine: &Engine, @@ -1050,7 +1299,7 @@ impl RunCommand { bail!("Cannot enable wasi-http for core wasm modules"); } CliLinker::Component(linker) => { - wasmtime_wasi_http::add_only_http_to_linker_sync(linker)?; + wasmtime_wasi_http::p2::add_only_http_to_linker_async(linker)?; #[cfg(feature = "component-model-async")] if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) { wasmtime_wasi_http::p3::add_to_linker(linker)?; @@ -1074,16 +1323,14 @@ impl RunCommand { bail!("Cannot enable wasi-tls for core wasm modules"); } CliLinker::Component(linker) => { - let mut opts = wasmtime_wasi_tls::LinkOptions::default(); + let mut opts = wasmtime_wasi_tls::p2::LinkOptions::default(); opts.tls(true); - wasmtime_wasi_tls::add_to_linker(linker, &mut opts, |h| { - let ctx = h.wasip1_ctx.as_mut().expect("wasi is not configured"); - let ctx = Arc::get_mut(ctx).unwrap().get_mut().unwrap(); - WasiTls::new( - Arc::get_mut(h.wasi_tls.as_mut().unwrap()).unwrap(), - ctx.ctx().table, - ) - })?; + wasmtime_wasi_tls::p2::add_to_linker(linker, &opts)?; + + #[cfg(feature = "component-model-async")] + if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) { + wasmtime_wasi_tls::p3::add_to_linker(linker)?; + } let ctx = wasmtime_wasi_tls::WasiTlsCtxBuilder::new().build(); store.data_mut().wasi_tls = Some(Arc::new(ctx)); @@ -1097,7 +1344,17 @@ impl RunCommand { fn set_legacy_p1_ctx(&self, store: &mut Store) -> Result<()> { let mut builder = WasiCtxBuilder::new(); - builder.inherit_stdio().args(&self.compute_argv()?)?; + builder.args(&self.compute_argv()?)?; + + if self.run.common.wasi.inherit_stdin.unwrap_or(true) { + builder.inherit_stdin(); + } + if self.run.common.wasi.inherit_stdout.unwrap_or(true) { + builder.inherit_stdout(); + } + if self.run.common.wasi.inherit_stderr.unwrap_or(true) { + builder.inherit_stderr(); + } if self.run.common.wasi.inherit_env == Some(true) { for (k, v) in std::env::vars() { @@ -1151,11 +1408,24 @@ impl RunCommand { /// `wasmtime-wasi`-vs-`wasi-common` here more than anything else. fn set_wasi_ctx(&self, store: &mut Store) -> Result<()> { let mut builder = wasmtime_wasi::WasiCtxBuilder::new(); - builder.inherit_stdio().args(&self.compute_argv()?); + builder.args(&self.compute_argv()?); + if self.run.common.wasi.inherit_stdin.unwrap_or(true) { + builder.inherit_stdin(); + } + if self.run.common.wasi.inherit_stdout.unwrap_or(true) { + builder.inherit_stdout(); + } + if self.run.common.wasi.inherit_stderr.unwrap_or(true) { + builder.inherit_stderr(); + } self.run.configure_wasip2(&mut builder)?; let mut ctx = builder.build_p1(); if let Some(max) = self.run.common.wasi.max_resources { ctx.ctx().table.set_max_capacity(max); + #[cfg(feature = "component-model-async")] + if let Some(table) = store.concurrent_resource_table() { + table.set_max_capacity(max); + } } if let Some(fuel) = self.run.common.wasi.hostcall_fuel { store.set_hostcall_fuel(fuel); @@ -1197,6 +1467,10 @@ impl RunCommand { /// not compatible with `wasi-threads`. #[derive(Default, Clone)] pub struct Host { + limits: StoreLimits, + #[cfg(feature = "profiling")] + guest_profiler: Option>, + // Legacy wasip1 context using `wasi_common`, not set unless opted-in-to // with the CLI. legacy_p1_ctx: Option, @@ -1220,25 +1494,18 @@ pub struct Host { #[cfg(feature = "wasi-http")] wasi_http: Option>, #[cfg(feature = "wasi-http")] - wasi_http_outgoing_body_buffer_chunks: Option, - #[cfg(feature = "wasi-http")] - wasi_http_outgoing_body_chunk_size: Option, - #[cfg(all(feature = "wasi-http", feature = "component-model-async"))] - p3_http: crate::common::DefaultP3Ctx, - limits: StoreLimits, - #[cfg(feature = "profiling")] - guest_profiler: Option>, + wasi_http_hooks: crate::common::HttpHooks, #[cfg(feature = "wasi-config")] wasi_config: Option>, #[cfg(feature = "wasi-keyvalue")] wasi_keyvalue: Option>, #[cfg(feature = "wasi-tls")] - wasi_tls: Option>, + wasi_tls: Option>, } impl Host { - fn wasip1_ctx(&mut self) -> &mut wasmtime_wasi::p1::WasiP1Ctx { + pub(crate) fn wasip1_ctx(&mut self) -> &mut wasmtime_wasi::p1::WasiP1Ctx { unwrap_singlethread_context(&mut self.wasip1_ctx) } } @@ -1258,33 +1525,37 @@ impl WasiView for Host { } #[cfg(feature = "wasi-http")] -impl wasmtime_wasi_http::types::WasiHttpView for Host { - fn ctx(&mut self) -> &mut WasiHttpCtx { +impl wasmtime_wasi_http::p2::WasiHttpView for Host { + fn http(&mut self) -> wasmtime_wasi_http::p2::WasiHttpCtxView<'_> { let ctx = self.wasi_http.as_mut().unwrap(); - Arc::get_mut(ctx).expect("wasmtime_wasi is not compatible with threads") - } - - fn table(&mut self) -> &mut wasmtime::component::ResourceTable { - WasiView::ctx(self).table - } - - fn outgoing_body_buffer_chunks(&mut self) -> usize { - self.wasi_http_outgoing_body_buffer_chunks - .unwrap_or_else(|| DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS) - } - - fn outgoing_body_chunk_size(&mut self) -> usize { - self.wasi_http_outgoing_body_chunk_size - .unwrap_or_else(|| DEFAULT_OUTGOING_BODY_CHUNK_SIZE) + let ctx = Arc::get_mut(ctx).expect("wasmtime_wasi_http is not compatible with threads"); + wasmtime_wasi_http::p2::WasiHttpCtxView { + table: WasiView::ctx(unwrap_singlethread_context(&mut self.wasip1_ctx)).table, + ctx, + hooks: &mut self.wasi_http_hooks, + } } } #[cfg(all(feature = "wasi-http", feature = "component-model-async"))] impl wasmtime_wasi_http::p3::WasiHttpView for Host { fn http(&mut self) -> wasmtime_wasi_http::p3::WasiHttpCtxView<'_> { + let ctx = self.wasi_http.as_mut().unwrap(); + let ctx = Arc::get_mut(ctx).expect("wasmtime_wasi_http is not compatible with threads"); wasmtime_wasi_http::p3::WasiHttpCtxView { table: WasiView::ctx(unwrap_singlethread_context(&mut self.wasip1_ctx)).table, - ctx: &mut self.p3_http, + ctx, + hooks: &mut self.wasi_http_hooks, + } + } +} + +#[cfg(all(feature = "wasi-tls"))] +impl wasmtime_wasi_tls::WasiTlsView for Host { + fn tls(&mut self) -> wasmtime_wasi_tls::WasiTlsCtxView<'_> { + wasmtime_wasi_tls::WasiTlsCtxView { + table: WasiView::ctx(unwrap_singlethread_context(&mut self.wasip1_ctx)).table, + ctx: Arc::get_mut(self.wasi_tls.as_mut().unwrap()).unwrap(), } } } diff --git a/src/commands/serve.rs b/src/commands/serve.rs index cacf7110faa9..b49be312eceb 100644 --- a/src/commands/serve.rs +++ b/src/commands/serve.rs @@ -1,4 +1,4 @@ -use crate::common::{Profile, RunCommon, RunTarget}; +use crate::common::{HttpHooks, Profile, RunCommon, RunTarget}; use bytes::Bytes; use clap::Parser; use futures::future::FutureExt; @@ -7,6 +7,7 @@ use http_body_util::BodyExt as _; use http_body_util::combinators::UnsyncBoxBody; use hyper::body::{Body, Frame, SizeHint}; use std::convert::Infallible; +use std::ffi::OsString; use std::net::SocketAddr; use std::pin::Pin; use std::task::{Context, Poll}; @@ -20,7 +21,7 @@ use std::{ }; use tokio::io::{self, AsyncWrite}; use tokio::sync::Notify; -use wasmtime::component::{Component, Linker, ResourceTable}; +use wasmtime::component::{Component, Linker}; use wasmtime::{ Engine, Result, Store, StoreContextMut, StoreLimits, UpdateDeadline, bail, error::Context as _, }; @@ -31,10 +32,10 @@ use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView}; use wasmtime_wasi_http::handler::p2::bindings as p2; use wasmtime_wasi_http::handler::{HandlerState, Proxy, ProxyHandler, ProxyPre, StoreBundle}; use wasmtime_wasi_http::io::TokioIo; -use wasmtime_wasi_http::{ - DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS, DEFAULT_OUTGOING_BODY_CHUNK_SIZE, WasiHttpCtx, - WasiHttpView, -}; +use wasmtime_wasi_http::{WasiHttpCtx, p2::WasiHttpView}; + +#[cfg(feature = "debug")] +use crate::commands::run::RunCommand; #[cfg(feature = "wasi-config")] use wasmtime_wasi_config::{WasiConfig, WasiConfigVariables}; @@ -51,11 +52,7 @@ struct Host { table: wasmtime::component::ResourceTable, ctx: WasiCtx, http: WasiHttpCtx, - http_outgoing_body_buffer_chunks: Option, - http_outgoing_body_chunk_size: Option, - - #[cfg(feature = "component-model-async")] - p3_http: crate::common::DefaultP3Ctx, + hooks: HttpHooks, limits: StoreLimits, @@ -81,22 +78,13 @@ impl WasiView for Host { } } -impl WasiHttpView for Host { - fn ctx(&mut self) -> &mut WasiHttpCtx { - &mut self.http - } - fn table(&mut self) -> &mut ResourceTable { - &mut self.table - } - - fn outgoing_body_buffer_chunks(&mut self) -> usize { - self.http_outgoing_body_buffer_chunks - .unwrap_or_else(|| DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS) - } - - fn outgoing_body_chunk_size(&mut self) -> usize { - self.http_outgoing_body_chunk_size - .unwrap_or_else(|| DEFAULT_OUTGOING_BODY_CHUNK_SIZE) +impl wasmtime_wasi_http::p2::WasiHttpView for Host { + fn http(&mut self) -> wasmtime_wasi_http::p2::WasiHttpCtxView<'_> { + wasmtime_wasi_http::p2::WasiHttpCtxView { + ctx: &mut self.http, + table: &mut self.table, + hooks: &mut self.hooks, + } } } @@ -105,7 +93,8 @@ impl wasmtime_wasi_http::p3::WasiHttpView for Host { fn http(&mut self) -> wasmtime_wasi_http::p3::WasiHttpCtxView<'_> { wasmtime_wasi_http::p3::WasiHttpCtxView { table: &mut self.table, - ctx: &mut self.p3_http, + ctx: &mut self.http, + hooks: &mut self.hooks, } } } @@ -209,6 +198,155 @@ impl ServeCommand { Ok(()) } + /// Set up the debugger component side-car, mirroring + /// [`RunCommand::debugger_run`]. + #[cfg(feature = "debug")] + fn debugger_setup(&mut self) -> Result> { + fn set_implicit_option( + place: &str, + name: &str, + setting: &mut Option, + value: bool, + ) -> Result<()> { + if *setting == Some(!value) { + bail!( + "Explicitly-set option on {place} {name}={} is not compatible \ + with debugging-implied setting {value}", + setting.unwrap() + ); + } + *setting = Some(value); + Ok(()) + } + + #[cfg(feature = "gdbstub")] + let override_bytes = if let Some(addr) = self.run.gdbstub.as_deref() { + if self.run.common.debug.debugger.is_some() { + bail!("-g/--gdb cannot be combined with -Ddebugger="); + } + let addr = if addr.parse::().is_ok() { + format!("127.0.0.1:{addr}") + } else { + use std::net::SocketAddr as SA; + addr.parse::() + .with_context(|| format!("invalid gdbstub address: `{addr}`"))?; + addr.to_string() + }; + self.run.common.debug.debugger = Some("".into()); + self.run.common.debug.arg.push(addr); + Some(gdbstub_component_artifact::GDBSTUB_COMPONENT) + } else { + None + }; + #[cfg(not(feature = "gdbstub"))] + let override_bytes = None; + + if let Some(debugger_component_path) = self.run.common.debug.debugger.as_ref() { + set_implicit_option( + "debuggee", + "guest_debug", + &mut self.run.common.debug.guest_debug, + true, + )?; + set_implicit_option( + "debuggee", + "epoch_interruption", + &mut self.run.common.wasm.epoch_interruption, + true, + )?; + + let mut debugger_run = RunCommand::try_parse_from( + ["run".into(), debugger_component_path.into()] + .into_iter() + .chain(self.run.common.debug.arg.iter().map(OsString::from)), + )?; + debugger_run.module_bytes = override_bytes; + + debugger_run.run.common.wasi.tcp.get_or_insert(true); + debugger_run + .run + .common + .wasi + .inherit_network + .get_or_insert(true); + + set_implicit_option( + "debugger", + "inherit_stdin", + &mut debugger_run.run.common.wasi.inherit_stdin, + self.run.common.debug.inherit_stdin.unwrap_or(false), + )?; + set_implicit_option( + "debugger", + "inherit_stdout", + &mut debugger_run.run.common.wasi.inherit_stdout, + self.run.common.debug.inherit_stdout.unwrap_or(false), + )?; + set_implicit_option( + "debugger", + "inherit_stderr", + &mut debugger_run.run.common.wasi.inherit_stderr, + self.run.common.debug.inherit_stderr.unwrap_or(false), + )?; + Ok(Some(debugger_run)) + } else { + Ok(None) + } + } + + /// Run the HTTP server under a debugger component. + /// + /// Uses a single store and instance to handle all requests + /// sequentially, so the debugger can pause and inspect state. + #[cfg(feature = "debug")] + async fn serve_under_debugger( + &self, + mut debug_run: RunCommand, + engine: &Engine, + linker: &Linker, + component: &Component, + ) -> Result<()> { + let instance_pre = linker.instantiate_pre(component)?; + let proxy_pre = wasmtime_wasi_http::p2::bindings::ProxyPre::new(instance_pre)?; + + let mut debuggee_store = self.new_store(engine, None)?; + + // Pre-register component modules so the debugger can see + // them and set breakpoints at the initial stop. + debuggee_store.debug_register_component(component)?; + + let debug_engine = debug_run.new_engine()?; + let debug_main = debug_run.run.load_module( + &debug_engine, + debug_run.module_and_args[0].as_ref(), + debug_run.module_bytes.as_ref().map(|v| &v[..]), + )?; + let (mut debug_store, debug_linker) = + debug_run.new_store_and_linker(&debug_engine, &debug_main)?; + let debug_component = match debug_main { + RunTarget::Core(_) => { + bail!("Debugger component is a core module; only components are supported") + } + RunTarget::Component(c) => c, + }; + let mut debug_linker = match debug_linker { + crate::commands::run::CliLinker::Core(_) => unreachable!(), + crate::commands::run::CliLinker::Component(l) => l, + }; + debug_run.add_debugger_api(&mut debug_linker)?; + + let addr = self.addr; + debug_run + .invoke_debugger( + &mut debug_store, + &debug_component, + &mut debug_linker, + debuggee_store, + move |store| Box::pin(debug_serve_body(store, proxy_pre, addr)), + ) + .await + } + fn new_store(&self, engine: &Engine, req_id: Option) -> Result> { let mut builder = WasiCtxBuilder::new(); self.run.configure_wasip2(&mut builder)?; @@ -240,8 +378,7 @@ impl ServeCommand { table, ctx: builder.build(), http: self.run.wasi_http_ctx()?, - http_outgoing_body_buffer_chunks: self.run.common.wasi.http_outgoing_body_buffer_chunks, - http_outgoing_body_chunk_size: self.run.common.wasi.http_outgoing_body_chunk_size, + hooks: self.run.wasi_http_hooks(), limits: StoreLimits::default(), @@ -253,8 +390,6 @@ impl ServeCommand { wasi_keyvalue: None, #[cfg(feature = "profiling")] guest_profiler: None, - #[cfg(feature = "component-model-async")] - p3_http: crate::common::DefaultP3Ctx, }; if self.run.common.wasi.nn == Some(true) { @@ -337,13 +472,13 @@ impl ServeCommand { // uses. if cli == Some(true) { self.run.add_wasmtime_wasi_to_linker(linker)?; - wasmtime_wasi_http::add_only_http_to_linker_async(linker)?; + wasmtime_wasi_http::p2::add_only_http_to_linker_async(linker)?; #[cfg(feature = "component-model-async")] if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) { wasmtime_wasi_http::p3::add_to_linker(linker)?; } } else { - wasmtime_wasi_http::add_to_linker_async(linker)?; + wasmtime_wasi_http::p2::add_to_linker_async(linker)?; #[cfg(feature = "component-model-async")] if self.run.common.wasi.p3.unwrap_or(crate::common::P3_DEFAULT) { wasmtime_wasi_http::p3::add_to_linker(linker)?; @@ -407,6 +542,9 @@ impl ServeCommand { async fn serve(mut self) -> Result<()> { use hyper::server::conn::http1; + #[cfg(feature = "debug")] + let debug_run = self.debugger_setup()?; + let mut config = self .run .common @@ -432,11 +570,18 @@ impl ServeCommand { self.add_to_linker(&mut linker)?; - let component = match self.run.load_module(&engine, &self.component)? { + let component = match self.run.load_module(&engine, &self.component, None)? { RunTarget::Core(_) => bail!("The serve command currently requires a component"), RunTarget::Component(c) => c, }; + #[cfg(feature = "debug")] + if let Some(debug_run) = debug_run { + return self + .serve_under_debugger(debug_run, &engine, &linker, &component) + .await; + } + let instance = linker.instantiate_pre(&component)?; #[cfg(feature = "component-model-async")] let instance = match wasmtime_wasi_http::p3::bindings::ServicePre::new(instance.clone()) { @@ -831,6 +976,148 @@ fn setup_guest_profiler( Ok(write_profile) } +/// Build a minimal error response with an empty body. +fn error_response(status: StatusCode) -> hyper::Response> { + Response::builder() + .status(status) + .body( + http_body_util::Empty::new() + .map_err(|_| unreachable!()) + .boxed_unsync(), + ) + .unwrap() +} + +/// Debuggee body for `wasmtime serve -g`: instantiate the HTTP component +/// once, then handle requests sequentially on a single store. +#[cfg(feature = "debug")] +async fn debug_serve_body( + store: &mut Store, + proxy_pre: wasmtime_wasi_http::p2::bindings::ProxyPre, + addr: SocketAddr, +) -> Result<()> { + use hyper::server::conn::http1; + use wasmtime_wasi_http::p2::bindings::http::types::Scheme; + use wasmtime_wasi_http::p2::body::HyperOutgoingBody; + + type P2Response = std::result::Result< + hyper::Response, + wasmtime_wasi_http::p2::bindings::http::types::ErrorCode, + >; + + let engine_clone = store.engine().clone(); + let _epoch_thread = std::thread::spawn(move || { + loop { + std::thread::sleep(Duration::from_millis(1)); + engine_clone.increment_epoch(); + } + }); + + store.epoch_deadline_async_yield_and_update(1); + + // Instantiate the HTTP component once. + let proxy = proxy_pre.instantiate_async(&mut *store).await?; + + // Bind the TCP listener. + let socket = match addr { + SocketAddr::V4(_) => tokio::net::TcpSocket::new_v4()?, + SocketAddr::V6(_) => tokio::net::TcpSocket::new_v6()?, + }; + socket.set_reuseaddr(!cfg!(windows))?; + socket.bind(addr)?; + let listener = socket.listen(100)?; + eprintln!("Serving HTTP on http://{}/", listener.local_addr()?); + + // Accept loop: handle one connection at a time, requests sequentially. + loop { + let (stream, _) = listener.accept().await?; + stream.set_nodelay(true)?; + let stream = TokioIo::new(stream); + + // Channel to bridge hyper's service_fn with our sequential + // request processing on the single store. + type RespBody = hyper::Response>; + let (req_tx, mut req_rx) = tokio::sync::mpsc::channel::<( + hyper::Request, + tokio::sync::oneshot::Sender>, + )>(1); + + let serve_conn = http1::Builder::new().keep_alive(true).serve_connection( + stream, + hyper::service::service_fn(move |req| { + let req_tx = req_tx.clone(); + async move { + let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); + if req_tx.send((req, resp_tx)).await.is_err() { + return Ok::<_, Infallible>(error_response( + StatusCode::SERVICE_UNAVAILABLE, + )); + } + resp_rx + .await + .unwrap_or(Ok(error_response(StatusCode::SERVICE_UNAVAILABLE))) + } + }), + ); + + tokio::pin!(serve_conn); + + loop { + tokio::select! { + result = &mut serve_conn => { + if let Err(e) = result { + eprintln!("connection error: {e:?}"); + } + break; + } + msg = req_rx.recv() => { + let Some((req, resp_tx)) = msg else { break }; + + let (p2_tx, p2_rx) = tokio::sync::oneshot::channel::(); + let wasi_req = store + .data_mut() + .http() + .new_incoming_request(Scheme::Http, req); + let wasi_out = wasi_req.and_then(|_req| { + let out = store.data_mut().http().new_response_outparam(p2_tx); + out.map(|out| (_req, out)) + }); + let (wasi_req, wasi_out) = match wasi_out { + Ok(pair) => pair, + Err(e) => { + eprintln!("error creating WASI request: {e:?}"); + let _ = resp_tx.send(Ok(error_response( + StatusCode::INTERNAL_SERVER_ERROR, + ))); + continue; + } + }; + + if let Err(e) = proxy + .wasi_http_incoming_handler() + .call_handle(&mut *store, wasi_req, wasi_out) + .await + { + eprintln!("handler error: {e:?}"); + } + + let resp = match p2_rx.await { + Ok(Ok(resp)) => resp.map(|body| { + body.map_err(|e| e.into()).boxed_unsync() + }), + Ok(Err(e)) => { + eprintln!("component error: {e:?}"); + error_response(StatusCode::INTERNAL_SERVER_ERROR) + } + Err(_) => error_response(StatusCode::INTERNAL_SERVER_ERROR), + }; + let _ = resp_tx.send(Ok(resp)); + } + } + } + } +} + type Request = hyper::Request; async fn handle_request( @@ -854,7 +1141,7 @@ async fn handle_request( // `wasmtime::Error`. type P2Response = Result< - hyper::Response, + hyper::Response, p2::http::types::ErrorCode, >; type P3Response = hyper::Response>; @@ -895,8 +1182,9 @@ async fn handle_request( let (req, out) = store.with(move |mut store| { let req = store .data_mut() + .http() .new_incoming_request(p2::http::types::Scheme::Http, req)?; - let out = store.data_mut().new_response_outparam(tx)?; + let out = store.data_mut().http().new_response_outparam(tx)?; wasmtime::error::Ok((req, out)) })?; diff --git a/src/commands/wizer.rs b/src/commands/wizer.rs index 4fa36eae5489..e221904cd665 100644 --- a/src/commands/wizer.rs +++ b/src/commands/wizer.rs @@ -84,6 +84,7 @@ impl WizerCommand { }), module_and_args: vec![self.input.clone().into()], preloads: self.preloads.clone(), + module_bytes: None, }; let engine = run.new_engine()?; diff --git a/src/common.rs b/src/common.rs index fcaf4c9bfc69..608fe1f2ee28 100644 --- a/src/common.rs +++ b/src/common.rs @@ -19,6 +19,7 @@ use wasmtime::component::Component; /// future. pub const P3_DEFAULT: bool = cfg!(feature = "component-model-async") && false; +#[derive(Clone)] pub enum RunTarget { Core(Module), @@ -99,6 +100,16 @@ pub struct RunCommon { /// cause the environment variable `FOO` to be inherited. #[arg(long = "env", number_of_values = 1, value_name = "NAME[=VAL]", value_parser = parse_env_var)] pub vars: Vec<(String, Option)>, + + /// Attach the built-in gdbstub debugger component, listening on + /// the given TCP address. Accepts a port number (e.g. `1234`) or + /// a full `address:port`. A bare port number will bind on + /// localhost only (`127.0.0.1`). A debugger (e.g. LLDB) can then + /// connect via `process connect --plugin=wasm + /// connect://:`. + #[cfg(feature = "gdbstub")] + #[arg(short = 'g', long = "gdbstub", value_name = "[ADDR:]PORT")] + pub gdbstub: Option, } fn parse_env_var(s: &str) -> Result<(String, Option)> { @@ -161,53 +172,69 @@ impl RunCommon { Ok(()) } - pub fn load_module(&self, engine: &Engine, path: &Path) -> Result { + pub fn load_module( + &self, + engine: &Engine, + path: &Path, + preloaded_bytes: Option<&[u8]>, + ) -> Result { let path = match path.to_str() { #[cfg(unix)] Some("-") => "/dev/stdin".as_ref(), _ => path, }; - let file = - File::open(path).with_context(|| format!("failed to open wasm module {path:?}"))?; - - // First attempt to load the module as an mmap. If this succeeds then - // detection can be done with the contents of the mmap and if a - // precompiled module is detected then `deserialize_file` can be used - // which is a slightly more optimal version than `deserialize` since we - // can leave most of the bytes on disk until they're referenced. - // - // If the mmap fails, for example if stdin is a pipe, then fall back to - // `std::fs::read` to load the contents. At that point precompiled - // modules must go through the `deserialize` functions. - // - // Note that this has the unfortunate side effect for precompiled - // modules on disk that they're opened once to detect what they are and - // then again internally in Wasmtime as part of the `deserialize_file` - // API. Currently there's no way to pass the `MmapVec` here through to - // Wasmtime itself (that'd require making `MmapVec` a public type, both - // which isn't ready to happen at this time). It's hoped though that - // opening a file twice isn't too bad in the grand scheme of things with - // respect to the CLI. - match wasmtime::_internal::MmapVec::from_file(file) { - Ok(map) => self.load_module_contents( + if let Some(bytes) = preloaded_bytes { + self.load_module_contents( engine, path, - &map, - || unsafe { Module::deserialize_file(engine, path) }, + &bytes, + || unsafe { Module::deserialize(engine, &bytes) }, #[cfg(feature = "component-model")] - || unsafe { Component::deserialize_file(engine, path) }, - ), - Err(_) => { - let bytes = std::fs::read(path) - .with_context(|| format!("failed to read file: {}", path.display()))?; - self.load_module_contents( + || unsafe { Component::deserialize(engine, &bytes) }, + ) + } else { + let file = + File::open(path).with_context(|| format!("failed to open wasm module {path:?}"))?; + + // First attempt to load the module as an mmap. If this succeeds then + // detection can be done with the contents of the mmap and if a + // precompiled module is detected then `deserialize_file` can be used + // which is a slightly more optimal version than `deserialize` since we + // can leave most of the bytes on disk until they're referenced. + // + // If the mmap fails, for example if stdin is a pipe, then fall back to + // `std::fs::read` to load the contents. At that point precompiled + // modules must go through the `deserialize` functions. + // + // Note that this has the unfortunate side effect for precompiled + // modules on disk that they're opened once to detect what they are and + // then again internally in Wasmtime as part of the `deserialize_file` + // API. Currently there's no way to pass the `MmapVec` here through to + // Wasmtime itself (that'd require making `MmapVec` a public type, both + // which isn't ready to happen at this time). It's hoped though that + // opening a file twice isn't too bad in the grand scheme of things with + // respect to the CLI. + match wasmtime::_internal::MmapVec::from_file(file) { + Ok(map) => self.load_module_contents( engine, path, - &bytes, - || unsafe { Module::deserialize(engine, &bytes) }, + &map, + || unsafe { Module::deserialize_file(engine, path) }, #[cfg(feature = "component-model")] - || unsafe { Component::deserialize(engine, &bytes) }, - ) + || unsafe { Component::deserialize_file(engine, path) }, + ), + Err(_) => { + let bytes = std::fs::read(path) + .with_context(|| format!("failed to read file: {}", path.display()))?; + self.load_module_contents( + engine, + path, + &bytes, + || unsafe { Module::deserialize(engine, &bytes) }, + #[cfg(feature = "component-model")] + || unsafe { Component::deserialize(engine, &bytes) }, + ) + } } } } @@ -341,6 +368,22 @@ impl RunCommon { Ok(http) } + #[cfg(feature = "wasi-http")] + pub fn wasi_http_hooks(&self) -> HttpHooks { + HttpHooks { + p2_outgoing_body_buffer_chunks: self + .common + .wasi + .http_outgoing_body_buffer_chunks + .unwrap_or_else(|| wasmtime_wasi_http::p2::DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS), + p2_outgoing_body_chunk_size: self + .common + .wasi + .http_outgoing_body_chunk_size + .unwrap_or_else(|| wasmtime_wasi_http::p2::DEFAULT_OUTGOING_BODY_CHUNK_SIZE), + } + } + pub fn compute_preopen_sockets(&self) -> Result> { let mut listeners = vec![]; @@ -444,8 +487,34 @@ impl Profile { } } -#[derive(Default, Clone)] -#[cfg(all(feature = "wasi-http", feature = "component-model-async"))] -pub struct DefaultP3Ctx; -#[cfg(all(feature = "wasi-http", feature = "component-model-async"))] -impl wasmtime_wasi_http::p3::WasiHttpCtx for DefaultP3Ctx {} +#[derive(Copy, Clone, Debug)] +#[cfg(feature = "wasi-http")] +pub struct HttpHooks { + p2_outgoing_body_buffer_chunks: usize, + p2_outgoing_body_chunk_size: usize, +} + +#[cfg(feature = "wasi-http")] +impl Default for HttpHooks { + fn default() -> Self { + Self { + p2_outgoing_body_buffer_chunks: + wasmtime_wasi_http::p2::DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS, + p2_outgoing_body_chunk_size: wasmtime_wasi_http::p2::DEFAULT_OUTGOING_BODY_CHUNK_SIZE, + } + } +} + +#[cfg(feature = "wasi-http")] +impl wasmtime_wasi_http::p2::WasiHttpHooks for HttpHooks { + fn outgoing_body_buffer_chunks(&mut self) -> usize { + self.p2_outgoing_body_buffer_chunks + } + + fn outgoing_body_chunk_size(&mut self) -> usize { + self.p2_outgoing_body_chunk_size + } +} + +#[cfg(feature = "wasi-http")] +impl wasmtime_wasi_http::p3::WasiHttpHooks for HttpHooks {} diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 4229d8e2b6e8..7efb47b5d98a 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -1110,6 +1110,14 @@ start = "2026-01-07" end = "2027-01-08" notes = "The Bytecode Alliance is the author of this crate" +[[wildcard-audits.wasmtime-internal-gdbstub-component-artifact]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +trusted-publisher = "github:bytecodealliance/wasmtime" +start = "2026-04-20" +end = "2027-04-20" +notes = "The Bytecode Alliance is the author of this crate." + [[wildcard-audits.wasmtime-internal-jit-debug]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -2051,6 +2059,12 @@ criteria = "safe-to-deploy" version = "0.1.6" notes = "Contains no unsafe code, no IO, no build.rs." +[[audits.ansi_term]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.12.1" +notes = "Only unsafe code is to access the console on Windows." + [[audits.anyhow]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -2114,6 +2128,12 @@ Well documented invariants, good assertions for those invariants in unsafe code, and tested with MIRI to boot. LGTM. """ +[[audits.async-task]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "4.7.1" +notes = "Fairly significant chunk of unsafe code in the async executor core, but seems isolated to well-understood patterns, e.g. a manual vtable and some manual layout code." + [[audits.atomic-waker]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -2654,6 +2674,11 @@ criteria = "safe-to-deploy" version = "0.4.4" notes = "Most unsafe is hidden by `inout` dependency; only remaining unsafe is raw-splitting a slice and an unreachable hint. Older versions of this regularly reach ~150k daily downloads." +[[audits.clap]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "2.34.0" + [[audits.cobs]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -3105,6 +3130,12 @@ who = "Joel Dice " criteria = "safe-to-deploy" version = "0.3.31" +[[audits.futures-lite]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "1.13.0" +notes = "No unsafe code and fairly straightforward/expected imports for async IO primitives." + [[audits.futures-macro]] who = "Joel Dice " criteria = "safe-to-deploy" @@ -3147,6 +3178,16 @@ who = "Pat Hickey " criteria = "safe-to-deploy" delta = "0.6.0 -> 0.8.1" +[[audits.gdbstub]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.7.10" + +[[audits.gdbstub_arch]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.3.3" + [[audits.gimli]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -3241,12 +3282,22 @@ criteria = "safe-to-deploy" version = "0.4.0" notes = "Contains `forbid_unsafe` and only uses `std::fmt` from the standard library. Otherwise only contains string manipulation." +[[audits.heck]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +delta = "0.4.0 -> 0.3.3" + [[audits.heck]] who = "Alex Crichton " criteria = "safe-to-deploy" delta = "0.4.1 -> 0.5.0" notes = "Minor changes for a `no_std` upgrade but otherwise everything looks as expected." +[[audits.hermit-abi]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.1.19" + [[audits.hermit-abi]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -3377,6 +3428,12 @@ criteria = "safe-to-deploy" version = "0.1.3" notes = "A part of RustCrypto/utils, this crate is designed to handle unsafe buffers and carefully documents the safety concerns throughout. Older versions of this tally up to ~130k daily downloads." +[[audits.instant]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.1.12" +notes = "Small crate wrapping platform primitives for time that uses `unsafe` only for FFI." + [[audits.io-extras]] who = "Dan Gohman " criteria = "safe-to-deploy" @@ -3726,6 +3783,12 @@ criteria = "safe-to-deploy" delta = "0.4.1 -> 0.4.2" notes = "It does unsafe FFI bindings, as expected. I didn't check the FFI bindings against the C headers." +[[audits.managed]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.8.0" +notes = "No unsafe code." + [[audits.matchers]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -3881,7 +3944,7 @@ notes = "one use of unsafe to call windows specific api to get console handle." who = "Alex Crichton " criteria = "safe-to-deploy" delta = "0.46.0 -> 0.50.1" -notes = "Lots of stylistic/rust-related chanegs, plus new features, but nothing out of the ordrinary." +notes = "Lots of stylistic/rust-related changes, plus new features, but nothing out of the ordrinary." [[audits.nu-ansi-term]] who = "Alex Crichton " @@ -4119,6 +4182,18 @@ criteria = "safe-to-deploy" version = "0.1.1" notes = "small crate, only defines macro-rules!, nicely documented as well" +[[audits.parking]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "2.2.1" +notes = "forbid-unsafe crate with straightforward imports." + +[[audits.pastey]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.2.1" +notes = "No unsafe code." + [[audits.peeking_take_while]] who = "Nick Fitzgerald " criteria = "safe-to-deploy" @@ -4193,6 +4268,11 @@ criteria = "safe-to-deploy" delta = "0.4.0 -> 0.5.0" notes = "This is a minor update which bumps the `env_logger` dependency and has other formatting, no major changes." +[[audits.proc-macro-error]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "1.0.4" + [[audits.proc-macro2]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -4579,6 +4659,21 @@ criteria = "safe-to-deploy" version = "1.1.0" notes = "No dependencies and completely a compile-time crate as advertised. Uses `unsafe` in one module as a compile-time check only: `mem::transmute` and `ptr::write` are wrapped in an impossible-to-run closure." +[[audits.strsim]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +delta = "0.10.0 -> 0.8.0" + +[[audits.structopt]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.3.26" + +[[audits.structopt-derive]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.4.18" + [[audits.syn]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -4702,6 +4797,12 @@ criteria = "safe-to-run" delta = "0.2.16 -> 0.2.18" notes = "Standard macro changes, nothing out of place" +[[audits.textwrap]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.11.0" +notes = "No unsafe code." + [[audits.thread_local]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -4952,6 +5053,12 @@ criteria = "safe-to-deploy" version = "0.2.15" notes = "no build.rs, no macros, no unsafe. It reads the filesystem and makes copies of DLLs into OUT_DIR." +[[audits.waker-fn]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "1.2.0" +notes = "Trivial crate with no unsafe code or use of any unsafe or risky APIs." + [[audits.walkdir]] who = "Andrew Brown " criteria = "safe-to-deploy" @@ -6442,6 +6549,18 @@ criteria = "safe-to-deploy" delta = "0.243.0 -> 0.244.0" notes = "The Bytecode Alliance is the author of this crate" +[[audits.wstd]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.6.6" +notes = "The Bytecode Alliance is the author of this crate." + +[[audits.wstd-macro]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.6.6" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.xattr]] who = "Andrew Brown " criteria = "safe-to-deploy" @@ -6481,6 +6600,11 @@ who = "Nick Fitzgerald " criteria = "safe-to-deploy" delta = "0.1.3 -> 0.1.5" +[[audits.zeroize]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "1.8.1 -> 1.8.2" + [[audits.zstd]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -6932,6 +7056,12 @@ user-id = 359 # Sean McArthur (seanmonstar) start = "2019-06-10" end = "2026-10-10" +[[trusted.object]] +criteria = "safe-to-deploy" +user-id = 4415 # Philip Craig (philipc) +start = "2019-04-26" +end = "2027-03-30" + [[trusted.once_cell_polyfill]] criteria = "safe-to-deploy" user-id = 6743 # Ed Page (epage) @@ -7104,7 +7234,7 @@ end = "2026-10-10" criteria = "safe-to-deploy" user-id = 1 # Alex Crichton (alexcrichton) start = "2019-03-04" -end = "2025-02-15" +end = "2027-03-24" [[trusted.target-lexicon]] criteria = "safe-to-deploy" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 16dcf6f3fad9..c1610402c5b0 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -154,6 +154,9 @@ audit-as-crates-io = true [policy.wasmtime-internal-fiber] audit-as-crates-io = true +[policy.wasmtime-internal-gdbstub-component-artifact] +audit-as-crates-io = true + [policy.wasmtime-internal-jit-debug] audit-as-crates-io = true @@ -199,12 +202,6 @@ audit-as-crates-io = true [policy.wasmtime-wasi-tls] audit-as-crates-io = true -[policy.wasmtime-wasi-tls-nativetls] -audit-as-crates-io = true - -[policy.wasmtime-wasi-tls-openssl] -audit-as-crates-io = true - [policy.wasmtime-wast] audit-as-crates-io = true @@ -390,10 +387,6 @@ criteria = "safe-to-deploy" version = "0.4.6" criteria = "safe-to-deploy" -[[exemptions.object]] -version = "0.36.0" -criteria = "safe-to-deploy" - [[exemptions.once_cell]] version = "1.12.0" criteria = "safe-to-deploy" @@ -474,6 +467,10 @@ criteria = "safe-to-deploy" version = "0.22.4" criteria = "safe-to-deploy" +[[exemptions.rustls]] +version = "0.23.37" +criteria = "safe-to-deploy" + [[exemptions.rustls-pki-types]] version = "1.3.1" criteria = "safe-to-deploy" @@ -482,6 +479,10 @@ criteria = "safe-to-deploy" version = "0.102.2" criteria = "safe-to-deploy" +[[exemptions.rustls-webpki]] +version = "0.103.10" +criteria = "safe-to-deploy" + [[exemptions.rusty-fork]] version = "0.3.0" criteria = "safe-to-deploy" @@ -534,6 +535,10 @@ criteria = "safe-to-deploy" version = "0.25.0" criteria = "safe-to-deploy" +[[exemptions.tokio-rustls]] +version = "0.26.4" +criteria = "safe-to-deploy" + [[exemptions.typenum]] version = "1.15.0" criteria = "safe-to-deploy" @@ -570,10 +575,6 @@ criteria = "safe-to-deploy" version = "0.4.0" criteria = "safe-to-deploy" -[[exemptions.zeroize]] -version = "1.7.0" -criteria = "safe-to-deploy" - [[exemptions.zip]] version = "0.6.6" criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 3d028dda99bc..307b1bfa1298 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -9,6 +9,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-assembler-x64]] version = "0.129.0" audited_as = "0.127.1" @@ -17,6 +29,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-assembler-x64]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-assembler-x64]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-assembler-x64]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-assembler-x64-meta]] version = "0.129.0" audited_as = "0.127.1" @@ -25,6 +49,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-assembler-x64-meta]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-assembler-x64-meta]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-assembler-x64-meta]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-bforest]] version = "0.129.0" audited_as = "0.127.1" @@ -33,6 +69,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-bforest]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-bforest]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-bforest]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-bitset]] version = "0.129.0" audited_as = "0.127.1" @@ -41,6 +89,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-bitset]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-bitset]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-bitset]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-codegen]] version = "0.129.0" audited_as = "0.127.1" @@ -49,6 +109,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-codegen]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-codegen]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-codegen]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-codegen-meta]] version = "0.129.0" audited_as = "0.127.1" @@ -57,6 +129,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-codegen-meta]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-codegen-meta]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-codegen-meta]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-codegen-shared]] version = "0.129.0" audited_as = "0.127.1" @@ -65,6 +149,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-codegen-shared]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-codegen-shared]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-codegen-shared]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-control]] version = "0.129.0" audited_as = "0.127.1" @@ -73,6 +169,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-control]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-control]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-control]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-entity]] version = "0.129.0" audited_as = "0.127.1" @@ -81,6 +189,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-entity]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-entity]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-entity]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-frontend]] version = "0.129.0" audited_as = "0.127.1" @@ -89,6 +209,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-frontend]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-frontend]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-frontend]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-interpreter]] version = "0.129.0" audited_as = "0.127.1" @@ -97,6 +229,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-interpreter]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-interpreter]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-interpreter]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-isle]] version = "0.129.0" audited_as = "0.127.1" @@ -105,6 +249,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-isle]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-isle]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-isle]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-jit]] version = "0.129.0" audited_as = "0.127.1" @@ -113,6 +269,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-jit]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-jit]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-jit]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-module]] version = "0.129.0" audited_as = "0.127.1" @@ -121,6 +289,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-module]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-module]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-module]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-native]] version = "0.129.0" audited_as = "0.127.1" @@ -129,6 +309,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-native]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-native]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-native]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-object]] version = "0.129.0" audited_as = "0.127.1" @@ -137,6 +329,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-object]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-object]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-object]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-reader]] version = "0.129.0" audited_as = "0.127.1" @@ -145,6 +349,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-reader]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-reader]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-reader]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-serde]] version = "0.129.0" audited_as = "0.127.1" @@ -153,6 +369,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-serde]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-serde]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-serde]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.cranelift-srcgen]] version = "0.129.0" audited_as = "0.127.1" @@ -161,6 +389,18 @@ audited_as = "0.127.1" version = "0.130.0" audited_as = "0.128.3" +[[unpublished.cranelift-srcgen]] +version = "0.131.0" +audited_as = "0.129.1" + +[[unpublished.cranelift-srcgen]] +version = "0.131.1" +audited_as = "0.131.0" + +[[unpublished.cranelift-srcgen]] +version = "0.131.2" +audited_as = "0.131.1" + [[unpublished.pulley-interpreter]] version = "42.0.0" audited_as = "40.0.1" @@ -169,6 +409,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.pulley-interpreter]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.pulley-interpreter]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.pulley-interpreter]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.pulley-macros]] version = "42.0.0" audited_as = "40.0.1" @@ -177,6 +429,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.pulley-macros]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.pulley-macros]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.pulley-macros]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasi-common]] version = "42.0.0" audited_as = "40.0.1" @@ -185,6 +449,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasi-common]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasi-common]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasi-common]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime]] version = "42.0.0" audited_as = "40.0.1" @@ -193,6 +469,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-cli]] version = "42.0.0" audited_as = "40.0.1" @@ -201,6 +489,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-cli]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-cli]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-cli]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-cli-flags]] version = "42.0.0" audited_as = "40.0.1" @@ -209,6 +509,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-cli-flags]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-cli-flags]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-cli-flags]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-environ]] version = "42.0.0" audited_as = "40.0.1" @@ -217,6 +529,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-environ]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-environ]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-environ]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-c-api-macros]] version = "42.0.0" audited_as = "40.0.1" @@ -225,6 +549,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-c-api-macros]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-c-api-macros]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-c-api-macros]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-cache]] version = "42.0.0" audited_as = "40.0.1" @@ -233,6 +569,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-cache]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-cache]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-cache]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-component-macro]] version = "42.0.0" audited_as = "40.0.1" @@ -241,6 +589,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-component-macro]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-component-macro]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-component-macro]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-component-util]] version = "42.0.0" audited_as = "40.0.1" @@ -249,6 +609,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-component-util]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-component-util]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-component-util]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-core]] version = "42.0.0" audited_as = "0.0.0" @@ -257,6 +629,18 @@ audited_as = "0.0.0" version = "43.0.0" audited_as = "0.0.0" +[[unpublished.wasmtime-internal-core]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-core]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-core]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-cranelift]] version = "42.0.0" audited_as = "40.0.1" @@ -265,6 +649,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-cranelift]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-cranelift]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-cranelift]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-debugger]] version = "42.0.0" audited_as = "41.0.0" @@ -273,6 +669,18 @@ audited_as = "41.0.0" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-debugger]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-debugger]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-debugger]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-error]] version = "42.0.0" audited_as = "0.0.0" @@ -285,6 +693,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-explorer]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-explorer]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-explorer]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-fiber]] version = "42.0.0" audited_as = "40.0.1" @@ -293,6 +713,30 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-fiber]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-fiber]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-fiber]] +version = "44.0.2" +audited_as = "44.0.1" + +[[unpublished.wasmtime-internal-gdbstub-component-artifact]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-gdbstub-component-artifact]] +version = "44.0.2" +audited_as = "44.0.1" + +[[unpublished.wasmtime-internal-gdbstub-component-artifact]] +version = "45.0.0" +audited_as = "44.0.0" + [[unpublished.wasmtime-internal-jit-debug]] version = "42.0.0" audited_as = "40.0.1" @@ -301,6 +745,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-jit-debug]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-jit-debug]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-jit-debug]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-jit-icache-coherence]] version = "42.0.0" audited_as = "40.0.1" @@ -309,6 +765,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-jit-icache-coherence]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-jit-icache-coherence]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-jit-icache-coherence]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-math]] version = "42.0.0" audited_as = "40.0.1" @@ -325,6 +793,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-unwinder]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-unwinder]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-unwinder]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-versioned-export-macros]] version = "42.0.0" audited_as = "40.0.1" @@ -333,6 +813,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-versioned-export-macros]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-versioned-export-macros]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-versioned-export-macros]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-winch]] version = "42.0.0" audited_as = "40.0.1" @@ -341,6 +833,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-winch]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-winch]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-winch]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-wit-bindgen]] version = "42.0.0" audited_as = "40.0.1" @@ -349,6 +853,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-wit-bindgen]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-wit-bindgen]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-wit-bindgen]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-internal-wmemcheck]] version = "42.0.0" audited_as = "40.0.1" @@ -357,6 +873,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-internal-wmemcheck]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-internal-wmemcheck]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-internal-wmemcheck]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wasi]] version = "42.0.0" audited_as = "40.0.1" @@ -365,6 +893,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wasi]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wasi]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wasi]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wasi-config]] version = "42.0.0" audited_as = "40.0.1" @@ -373,6 +913,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wasi-config]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wasi-config]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wasi-config]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wasi-http]] version = "42.0.0" audited_as = "40.0.1" @@ -381,6 +933,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wasi-http]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wasi-http]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wasi-http]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wasi-io]] version = "42.0.0" audited_as = "40.0.1" @@ -389,6 +953,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wasi-io]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wasi-io]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wasi-io]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wasi-keyvalue]] version = "42.0.0" audited_as = "40.0.1" @@ -397,6 +973,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wasi-keyvalue]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wasi-keyvalue]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wasi-keyvalue]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wasi-nn]] version = "42.0.0" audited_as = "40.0.1" @@ -405,6 +993,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wasi-nn]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wasi-nn]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wasi-nn]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wasi-threads]] version = "42.0.0" audited_as = "40.0.1" @@ -413,6 +1013,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wasi-threads]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wasi-threads]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wasi-threads]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wasi-tls]] version = "42.0.0" audited_as = "40.0.1" @@ -421,6 +1033,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wasi-tls]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wasi-tls]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wasi-tls]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wasi-tls-nativetls]] version = "42.0.0" audited_as = "40.0.1" @@ -429,6 +1053,10 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wasi-tls-nativetls]] +version = "44.0.0" +audited_as = "42.0.1" + [[unpublished.wasmtime-wasi-tls-openssl]] version = "42.0.0" audited_as = "0.0.1" @@ -437,6 +1065,10 @@ audited_as = "0.0.1" version = "43.0.0" audited_as = "0.0.1" +[[unpublished.wasmtime-wasi-tls-openssl]] +version = "44.0.0" +audited_as = "42.0.1" + [[unpublished.wasmtime-wast]] version = "42.0.0" audited_as = "40.0.1" @@ -445,6 +1077,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wast]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wast]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wast]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wasmtime-wizer]] version = "42.0.0" audited_as = "40.0.1" @@ -453,6 +1097,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wasmtime-wizer]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wasmtime-wizer]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wasmtime-wizer]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wiggle]] version = "42.0.0" audited_as = "40.0.1" @@ -461,6 +1117,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wiggle]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wiggle]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wiggle]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wiggle-generate]] version = "42.0.0" audited_as = "40.0.1" @@ -469,6 +1137,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wiggle-generate]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wiggle-generate]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wiggle-generate]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wiggle-macro]] version = "42.0.0" audited_as = "40.0.1" @@ -477,6 +1157,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.wiggle-macro]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.wiggle-macro]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.wiggle-macro]] +version = "44.0.2" +audited_as = "44.0.1" + [[unpublished.wiggle-test]] version = "0.0.0" audited_as = "0.1.0" @@ -489,6 +1181,18 @@ audited_as = "40.0.1" version = "43.0.0" audited_as = "41.0.3" +[[unpublished.winch-codegen]] +version = "44.0.0" +audited_as = "42.0.1" + +[[unpublished.winch-codegen]] +version = "44.0.1" +audited_as = "44.0.0" + +[[unpublished.winch-codegen]] +version = "44.0.2" +audited_as = "44.0.1" + [[publisher.aho-corasick]] version = "1.0.2" when = "2023-06-04" @@ -706,103 +1410,103 @@ user-login = "jrmuizel" user-name = "Jeff Muizelaar" [[publisher.cranelift]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-assembler-x64]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-assembler-x64-meta]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-bforest]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-bitset]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-codegen]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-codegen-meta]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-codegen-shared]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-control]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-entity]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-frontend]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-interpreter]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-isle]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-jit]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-module]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-native]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-object]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-reader]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-serde]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.cranelift-srcgen]] -version = "0.128.3" -when = "2026-02-04" +version = "0.131.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.derive_arbitrary]] @@ -1022,8 +1726,8 @@ user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.json-from-wast]] -version = "0.245.0" -when = "2026-02-10" +version = "0.246.1" +when = "2026-03-31" trusted-publisher = "github:bytecodealliance/wasm-tools" [[publisher.libc]] @@ -1066,6 +1770,20 @@ user-id = 359 user-login = "seanmonstar" user-name = "Sean McArthur" +[[publisher.object]] +version = "0.36.5" +when = "2024-10-04" +user-id = 4415 +user-login = "philipc" +user-name = "Philip Craig" + +[[publisher.object]] +version = "0.39.0" +when = "2026-03-29" +user-id = 4415 +user-login = "philipc" +user-name = "Philip Craig" + [[publisher.once_cell_polyfill]] version = "1.70.1" when = "2025-05-22" @@ -1095,13 +1813,13 @@ user-login = "dtolnay" user-name = "David Tolnay" [[publisher.pulley-interpreter]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.pulley-macros]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.quote]] @@ -1266,8 +1984,8 @@ user-login = "sunfishcode" user-name = "Dan Gohman" [[publisher.tar]] -version = "0.4.41" -when = "2024-06-04" +version = "0.4.45" +when = "2026-03-19" user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" @@ -1427,8 +2145,8 @@ user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.wasi-common]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasip1]] @@ -1481,226 +2199,228 @@ user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.wasm-compose]] -version = "0.245.0" -when = "2026-02-10" +version = "0.246.1" +when = "2026-03-31" trusted-publisher = "github:bytecodealliance/wasm-tools" -[[publisher.wasm-encoder]] -version = "0.236.0" -when = "2025-07-28" -user-id = 73222 -user-login = "wasmtime-publish" - [[publisher.wasm-encoder]] version = "0.245.0" when = "2026-02-10" trusted-publisher = "github:bytecodealliance/wasm-tools" -[[publisher.wasm-metadata]] -version = "0.236.0" -when = "2025-07-28" -user-id = 73222 -user-login = "wasmtime-publish" +[[publisher.wasm-encoder]] +version = "0.246.1" +when = "2026-03-31" +trusted-publisher = "github:bytecodealliance/wasm-tools" [[publisher.wasm-metadata]] version = "0.245.0" when = "2026-02-10" trusted-publisher = "github:bytecodealliance/wasm-tools" -[[publisher.wasm-wave]] -version = "0.245.0" -when = "2026-02-10" +[[publisher.wasm-metadata]] +version = "0.246.1" +when = "2026-03-31" trusted-publisher = "github:bytecodealliance/wasm-tools" -[[publisher.wasmparser]] -version = "0.236.0" -when = "2025-07-28" -user-id = 73222 -user-login = "wasmtime-publish" +[[publisher.wasm-wave]] +version = "0.246.1" +when = "2026-03-31" +trusted-publisher = "github:bytecodealliance/wasm-tools" [[publisher.wasmparser]] version = "0.245.0" when = "2026-02-10" trusted-publisher = "github:bytecodealliance/wasm-tools" +[[publisher.wasmparser]] +version = "0.246.1" +when = "2026-03-31" +trusted-publisher = "github:bytecodealliance/wasm-tools" + [[publisher.wasmprinter]] -version = "0.245.0" -when = "2026-02-10" +version = "0.246.1" +when = "2026-03-31" trusted-publisher = "github:bytecodealliance/wasm-tools" [[publisher.wasmtime]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-cli]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-cli-flags]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-environ]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-c-api-macros]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-cache]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-component-macro]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-component-util]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" +trusted-publisher = "github:bytecodealliance/wasmtime" + +[[publisher.wasmtime-internal-core]] +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-cranelift]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-debugger]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-explorer]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-fiber]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" +trusted-publisher = "github:bytecodealliance/wasmtime" + +[[publisher.wasmtime-internal-gdbstub-component-artifact]] +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-jit-debug]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-jit-icache-coherence]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-unwinder]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-versioned-export-macros]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-winch]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-wit-bindgen]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-internal-wmemcheck]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wasi]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wasi-config]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wasi-http]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wasi-io]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wasi-keyvalue]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wasi-nn]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wasi-threads]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wasi-tls]] -version = "41.0.3" -when = "2026-02-04" -trusted-publisher = "github:bytecodealliance/wasmtime" - -[[publisher.wasmtime-wasi-tls-nativetls]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wast]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wasmtime-wizer]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wast]] -version = "245.0.0" -when = "2026-02-10" +version = "246.0.1" +when = "2026-03-31" trusted-publisher = "github:bytecodealliance/wasm-tools" [[publisher.wat]] -version = "1.245.0" -when = "2026-02-10" +version = "1.246.1" +when = "2026-03-31" trusted-publisher = "github:bytecodealliance/wasm-tools" [[publisher.wiggle]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wiggle-generate]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wiggle-macro]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.wiggle-test]] @@ -1718,8 +2438,8 @@ user-login = "BurntSushi" user-name = "Andrew Gallant" [[publisher.winch-codegen]] -version = "41.0.3" -when = "2026-02-04" +version = "44.0.1" +when = "2026-04-30" trusted-publisher = "github:bytecodealliance/wasmtime" [[publisher.windows]] @@ -1943,28 +2663,26 @@ version = "0.53.0" when = "2026-02-12" trusted-publisher = "github:bytecodealliance/wit-bindgen" -[[publisher.wit-component]] -version = "0.236.0" -when = "2025-07-28" -user-id = 73222 -user-login = "wasmtime-publish" - [[publisher.wit-component]] version = "0.245.0" when = "2026-02-10" trusted-publisher = "github:bytecodealliance/wasm-tools" -[[publisher.wit-parser]] -version = "0.236.0" -when = "2025-07-28" -user-id = 73222 -user-login = "wasmtime-publish" +[[publisher.wit-component]] +version = "0.246.1" +when = "2026-03-31" +trusted-publisher = "github:bytecodealliance/wasm-tools" [[publisher.wit-parser]] version = "0.245.0" when = "2026-02-10" trusted-publisher = "github:bytecodealliance/wasm-tools" +[[publisher.wit-parser]] +version = "0.246.1" +when = "2026-03-31" +trusted-publisher = "github:bytecodealliance/wasm-tools" + [[publisher.witx]] version = "0.9.1" when = "2021-06-22" @@ -2020,6 +2738,12 @@ criteria = "safe-to-deploy" version = "0.1.0" notes = "No unsafe usage or ambient capabilities, sane build script" +[[audits.embark-studios.audits.vec_map]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +version = "0.8.2" +notes = "No unsafe usage or ambient capabilities" + [[audits.google.audits.addr2line]] who = "George Burgess IV " criteria = "safe-to-run" @@ -2111,6 +2835,12 @@ delta = "0.2.9 -> 0.2.13" notes = "Audited at https://fxrev.dev/946396" aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.proc-macro-error-attr]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "1.0.4" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.rand]] who = "Lukasz Anforowicz " criteria = "safe-to-deploy" @@ -2377,7 +3107,7 @@ who = "Manish Goregaokar " criteria = "safe-to-deploy" user-id = 1139 # Manish Goregaokar (Manishearth) start = "2019-05-15" -end = "2026-02-01" +end = "2027-04-23" notes = "All code written or reviewed by Manish" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" @@ -2395,7 +3125,7 @@ who = "Manish Goregaokar " criteria = "safe-to-deploy" user-id = 1139 # Manish Goregaokar (Manishearth) start = "2019-07-25" -end = "2026-02-01" +end = "2027-04-23" notes = "All code written or reviewed by Manish" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" @@ -3449,6 +4179,16 @@ criteria = "safe-to-deploy" version = "0.1.3" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.zeroize]] +who = "Benjamin Beurdouche " +criteria = "safe-to-deploy" +version = "1.8.1" +notes = """ +This code DOES contain unsafe code required to internally call volatiles +for deleting data. This is expected and documented behavior. +""" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.zerovec]] who = "Makoto Kato " criteria = "safe-to-deploy" diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs index 122181e2883c..ba761bf024a2 100644 --- a/tests/all/cli_tests.rs +++ b/tests/all/cli_tests.rs @@ -876,12 +876,12 @@ fn memory_growth_failure() -> Result<()> { "tests/all/cli_tests/memory-grow-failure.wat", ]) .output()?; - assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!( stderr.contains("forcing a memory growth failure to be a trap"), "bad stderr: {stderr}" ); + assert!(!output.status.success()); Ok(()) } @@ -2312,44 +2312,102 @@ start a print 1234 #[test] fn p2_cli_http_headers() -> Result<()> { + let td = tempfile::TempDir::new()?; + let cwasm = td.path().join("http_headers.cwasm"); + let cwasm = cwasm.to_str().unwrap(); + run_wasmtime(&["compile", P2_CLI_HTTP_HEADERS_COMPONENT, "-o", cwasm])?; + // p2 traps on too-many-fields/size-too-big, so expect error messages. for test in ["append", "append-empty", "append-same", "append-same-empty"] { let err = run_wasmtime(&[ "run", - "-Shttp", + "-Shttp,p3", "-Smax-http-fields-size=1048576", - P2_CLI_HTTP_HEADERS_COMPONENT, - test, + "--allow-precompiled", + cwasm, + &format!("p2-{test}"), ]) .unwrap_err(); assert!( err.to_string() - .contains("Field size limit 1048576 exceeded") - || err.to_string().contains("max size reached"), + .contains("total size of fields exceeds limit") + || err.to_string().contains("too many fields in the field map"), "bad error message: {err:?}" ); // gated by default too - let err = - run_wasmtime(&["run", "-Shttp", P2_CLI_HTTP_HEADERS_COMPONENT, test]).unwrap_err(); + let err = run_wasmtime(&[ + "run", + "-Shttp,p3", + "--allow-precompiled", + cwasm, + &format!("p2-{test}"), + ]) + .unwrap_err(); assert!( - err.to_string().contains("Field size limit"), + err.to_string() + .contains("total size of fields exceeds limit"), "bad error message: {err:?}" ); } - // With an extremely large limit Wasmtime still shouldn't panic. + // With an extremely large limit Wasmtime still shouldn't panic (p2). let err = run_wasmtime(&[ "run", - "-Shttp", + "-Shttp,p3", &format!("-Smax-http-fields-size={}", 1 << 30), - P2_CLI_HTTP_HEADERS_COMPONENT, - "append", + "--allow-precompiled", + cwasm, + "p2-append", ]) .unwrap_err(); assert!( - err.to_string().contains("max size reached"), + err.to_string().contains("too many fields in the field map"), "bad error message: {err:?}" ); + + // p3 returns an error code instead of trapping, so the guest + // program exits successfully after receiving the error. + for test in ["append", "append-empty", "append-same", "append-same-empty"] { + let output = run_wasmtime(&[ + "run", + "-Shttp,p3", + "-Smax-http-fields-size=1048576", + "--allow-precompiled", + cwasm, + &format!("p3-{test}"), + ])?; + assert!( + output.contains("error received"), + "p3-{test}: guest should have received an error" + ); + + // gated by default too + let output = run_wasmtime(&[ + "run", + "-Shttp,p3", + "--allow-precompiled", + cwasm, + &format!("p3-{test}"), + ])?; + assert!( + output.contains("error received"), + "p3-{test} (default limit): guest should have received an error" + ); + } + + // With an extremely large limit Wasmtime still shouldn't panic (p3). + let output = run_wasmtime(&[ + "run", + "-Shttp,p3", + &format!("-Smax-http-fields-size={}", 1 << 30), + "--allow-precompiled", + cwasm, + "p3-append", + ])?; + assert!( + output.contains("error received"), + "p3-append (large limit): guest should have received an error" + ); Ok(()) } @@ -2668,7 +2726,29 @@ start a print 1234 #[test] fn p2_cli_many_resources() -> Result<()> { - let err = run_wasmtime(&["run", P2_CLI_MANY_RESOURCES_COMPONENT]).unwrap_err(); + let err = run_wasmtime(&[ + "run", + "-Smax-resources=100", + P2_CLI_MANY_RESOURCES_COMPONENT, + ]) + .unwrap_err(); + assert!( + err.to_string().contains("resource table has no free keys"), + "bad error message: {err}" + ); + Ok(()) + } + + #[test] + fn p3_cli_many_tasks() -> Result<()> { + let err = run_wasmtime(&[ + "run", + "-Smax-resources=100", + "-Sp3", + "-Wcomponent-model-async", + dbg!(P3_CLI_MANY_TASKS_COMPONENT), + ]) + .unwrap_err(); assert!( err.to_string().contains("resource table has no free keys"), "bad error message: {err}" @@ -2757,6 +2837,47 @@ start a print 1234 } Ok(()) } + + #[test] + fn p3_cli_random_limits() -> Result<()> { + let c = P3_CLI_RANDOM_LIMITS_COMPONENT; + + for rand in ["random", "insecure"] { + run_wasmtime(&["run", "-Sp3", "-Wcomponent-model-async", c, rand, "256"])?; + run_wasmtime(&[ + "run", + "-Sp3", + "-Wcomponent-model-async", + "-Smax-random-size=255", + c, + rand, + "256", + ])?; + } + + Ok(()) + } + + #[test] + fn p3_cli_read_stdin() -> Result<()> { + let mut cmd = get_wasmtime_command()?; + let mut child = cmd + .arg("-Sp3") + .arg("-Wcomponent-model-async") + .arg(P3_CLI_READ_STDIN_COMPONENT) + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .unwrap(); + child.stdin.take().unwrap().write_all(b"hello!").unwrap(); + let output = child.wait_with_output()?; + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + assert!(output.status.success()); + + Ok(()) + } } #[test] diff --git a/tests/all/component_model.rs b/tests/all/component_model.rs index 785bf398fff6..b7e46fcc1377 100644 --- a/tests/all/component_model.rs +++ b/tests/all/component_model.rs @@ -5,7 +5,7 @@ use wasmtime::component::{ }; use wasmtime::{Config, Result, Store}; use wasmtime_component_util::REALLOC_AND_FREE; -use wasmtime_test_util::component::{async_engine, config, engine}; +use wasmtime_test_util::component::{async_engine, config, engine, map_engine}; mod aot; mod r#async; diff --git a/tests/all/component_model/async.rs b/tests/all/component_model/async.rs index 0c31867080fe..3ee76779e631 100644 --- a/tests/all/component_model/async.rs +++ b/tests/all/component_model/async.rs @@ -1177,3 +1177,150 @@ async fn stream_cancel_read_async_does_not_corrupt_state() -> Result<()> { } } } + +/// Regression test: multiple threads may concurrently make a synchronous +/// call into the same async host function without corrupting state. +/// +/// Bug: waitable sets for host calls used to be shared across all threads, so if two threads +/// called a sync-lowered async host function concurrently, the waitable set state got overwritten. +#[tokio::test] +#[cfg_attr(miri, ignore)] +async fn concurrent_sync_calls_to_async_host() -> Result<()> { + _ = env_logger::try_init(); + + let mut config = Config::new(); + config.wasm_component_model_async(true); + config.wasm_component_model_async_builtins(true); + config.wasm_component_model_async_stackful(true); + config.wasm_component_model_threading(true); + let engine = Engine::new(&config)?; + let mut store = Store::new(&engine, 0); + + let component = Component::new( + &engine, + r#"(component + (import "await-three-calls" (func $await-three-calls async)) + + (core module $libc + (table (export "__indirect_function_table") 1 funcref)) + + (core module $m + (import "" "await-three-calls" (func $await-three-calls)) + (import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32))) + (import "" "thread.unsuspend" (func $thread-unsuspend (param i32))) + (import "libc" "__indirect_function_table" (table $indirect-function-table 1 funcref)) + + (func (export "run") + (call $thread-new-indirect (i32.const 0) (i32.const 0)) + (call $thread-unsuspend) + (call $thread-new-indirect (i32.const 0) (i32.const 0)) + (call $thread-unsuspend) + (call $await-three-calls) + ) + (func $thread-entry (param i32) + (call $await-three-calls) + ) + (elem (table $indirect-function-table) (i32.const 0) func $thread-entry) + ) + ;; Instantiate the libc module to get the table + (core instance $libc (instantiate $libc)) + ;; Get access to `thread.new-indirect` that uses the table from libc + (core type $start-func-ty (func (param i32))) + (alias core export $libc "__indirect_function_table" (core table $indirect-function-table)) + (core func $thread-new-indirect + (canon thread.new-indirect $start-func-ty (table $indirect-function-table))) + (core func $thread-unsuspend (canon thread.unsuspend)) + + (core func $await-three-calls (canon lower (func $await-three-calls) )) + (core instance $i (instantiate $m + (with "" (instance + (export "await-three-calls" (func $await-three-calls)) + (export "thread.new-indirect" (func $thread-new-indirect)) + (export "thread.unsuspend" (func $thread-unsuspend)) + )) + (with "libc" (instance $libc)) + )) + (func (export "run") async + (canon lift (core func $i "run"))) + )"#, + )?; + + let mut linker = Linker::::new(&engine); + linker + .root() + .func_wrap_concurrent("await-three-calls", |accessor, (): ()| { + Box::pin(async move { + accessor.with(|mut s| { + *s.data_mut() += 1; + }); + while accessor.with(|mut s| *s.data_mut()) < 3 { + tokio::task::yield_now().await; + } + Ok(()) + }) + })?; + let instance = linker.instantiate_async(&mut store, &component).await?; + let func = instance.get_typed_func::<(), ()>(&mut store, "run")?; + func.call_async(&mut store, ()).await?; + + store.assert_concurrent_state_empty(); + + Ok(()) +} + +#[tokio::test] +#[cfg_attr(miri, ignore)] +async fn bytes_stream_producer() -> Result<()> { + let mut config = Config::new(); + config.wasm_component_model_async(true); + let engine = Engine::new(&config)?; + + let component = Component::new( + &engine, + r#" + (component + (core module $libc (memory (export "mem") 1)) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "mem" (memory 1)) + (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32))) + + (func (export "read") (param i32 i32) (result i32) + (call $stream.read (local.get 0) (i32.const 0) (local.get 1)) + ) + ) + (type $s (stream u8)) + (core func $stream.read (canon stream.read $s async (memory $libc "mem"))) + (core instance $i (instantiate $m + (with "" (instance + (export "mem" (memory $libc "mem")) + (export "stream.read" (func $stream.read)) + )) + )) + (func (export "read") (param "s" (stream u8)) (param "l" u32) (result u32) + (canon lift (core func $i "read"))) + ) + "#, + )?; + + let linker = Linker::new(&engine); + let mut store = Store::new(&engine, ()); + let instance = linker.instantiate_async(&mut store, &component).await?; + let func = instance.get_typed_func::<(StreamReader, u32), (u32,)>(&mut store, "read")?; + + // read less than the capacity + let reader = StreamReader::new(&mut store, bytes::Bytes::from_static(b"hello"))?; + assert_eq!( + func.call_async(&mut store, (reader, 1)).await?, + ((1 << 4) | 0,), + ); + + // read more than the capacity + let reader = StreamReader::new(&mut store, bytes::Bytes::from_static(b"hello"))?; + assert_eq!( + func.call_async(&mut store, (reader, 100)).await?, + ((5 << 4) | 1,), + ); + + Ok(()) +} diff --git a/tests/all/component_model/async_dynamic.rs b/tests/all/component_model/async_dynamic.rs index 02b55986e149..1f9b0225741b 100644 --- a/tests/all/component_model/async_dynamic.rs +++ b/tests/all/component_model/async_dynamic.rs @@ -178,3 +178,108 @@ fn simple_type_assertions() -> Result<()> { Ok(()) } + +#[tokio::test] +#[cfg_attr(miri, ignore)] +async fn stream_any_smoke() -> Result<()> { + let mut config = Config::new(); + config.wasm_component_model_async(true); + let engine = Engine::new(&config)?; + let mut store = Store::new(&engine, ()); + let component = Component::new( + &engine, + r#" +(component + (type $s (stream u8)) + + (core module $libc (memory (export "mem") 1)) + (core instance $libc (instantiate $libc)) + + (core module $m + (import "" "stream.new" (func $stream.new (result i64))) + (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32))) + (import "" "task.return" (func $task.return)) + (import "" "waitable-set.new" (func $waitable-set.new (result i32))) + (import "" "waitable.join" (func $waitable.join (param i32 i32))) + (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32))) + (import "" "waitable-set.drop" (func $waitable-set.drop (param i32))) + (import "" "mem" (memory 1)) + + (global $w (mut i32) (i32.const 0)) + + (func (export "mk") (result i32) + (local $r i32) (local $tmp i64) + (local.set $tmp (call $stream.new)) + (local.set $r (i32.wrap_i64 (local.get $tmp))) + (global.set $w (i32.wrap_i64 (i64.shr_u (local.get $tmp) (i64.const 32)))) + local.get $r + ) + + (func (export "run") (result i32) + (local $ws i32) + (local.set $ws (call $waitable-set.new)) + (call $waitable.join (global.get $w) (local.get $ws)) + (call $waitable-set.wait (local.get $ws) (i32.const 0)) + i32.const 3 ;; EVENT_STREAM_WRITE + i32.ne + if unreachable end + + (if (i32.ne (i32.load (i32.const 0)) (global.get $w)) + (then unreachable)) + (if (i32.ne (i32.load (i32.const 4)) (i32.const 1)) ;; DROPPED | (0 << 4) + (then unreachable)) + + call $task.return + + i32.const 0 ;; CALLBACK_CODE_EXIT + ) + + (func (export "cb") (param i32 i32 i32) (result i32) unreachable) + ) + (core func $stream.new (canon stream.new $s)) + (core func $stream.write (canon stream.write $s (memory $libc "mem"))) + (core func $task.return (canon task.return)) + (core func $waitable-set.new (canon waitable-set.new)) + (core func $waitable.join (canon waitable.join)) + (core func $waitable-set.wait (canon waitable-set.wait (memory $libc "mem"))) + (core func $waitable-set.drop (canon waitable-set.drop)) + (core instance $i (instantiate $m + (with "" (instance + (export "stream.new" (func $stream.new)) + (export "stream.write" (func $stream.write)) + (export "task.return" (func $task.return)) + (export "waitable-set.new" (func $waitable-set.new)) + (export "waitable.join" (func $waitable.join)) + (export "waitable-set.wait" (func $waitable-set.wait)) + (export "waitable-set.drop" (func $waitable-set.drop)) + (export "mem" (memory $libc "mem")) + )) + )) + (func (export "mk") (result (stream u8)) + (canon lift (core func $i "mk"))) + (func (export "run") async + (canon lift (core func $i "run") async (callback (func $i "cb")))) +) + "#, + )?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let mk = instance.get_typed_func::<(), (StreamAny,)>(&mut store, "mk")?; + let run = instance.get_typed_func::<(), ()>(&mut store, "run")?; + store + .run_concurrent(async |store| { + let (mut stream,) = mk.call_concurrent(store, ()).await?; + tokio::try_join! { + async { + run.call_concurrent(store, ()).await?; + wasmtime::error::Ok(()) + }, + async { + store.with(|store| stream.close(store))?; + wasmtime::error::Ok(()) + } + }?; + wasmtime::error::Ok(()) + }) + .await??; + Ok(()) +} diff --git a/tests/all/component_model/dynamic.rs b/tests/all/component_model/dynamic.rs index 0a67a320f82b..e1a0202728f3 100644 --- a/tests/all/component_model/dynamic.rs +++ b/tests/all/component_model/dynamic.rs @@ -136,6 +136,343 @@ fn lists() -> Result<()> { Ok(()) } +#[test] +fn maps() -> Result<()> { + let engine = super::map_engine(); + let mut store = Store::new(&engine, ()); + + let component = Component::new(&engine, make_echo_component("(map u32 string)", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let input_map = vec![ + (Val::U32(1), Val::String("one".into())), + (Val::U32(2), Val::String("two".into())), + (Val::U32(3), Val::String("three".into())), + ]; + let input = Val::Map(input_map); + + let mut output = [Val::Bool(false)]; + func.call(&mut store, &[input.clone()], &mut output)?; + + // Maps should round-trip correctly + match &output[0] { + Val::Map(output_map) => { + assert_eq!(output_map.len(), 3); + assert!( + output_map + .iter() + .any(|(k, v)| *k == Val::U32(1) && *v == Val::String("one".into())) + ); + assert!( + output_map + .iter() + .any(|(k, v)| *k == Val::U32(2) && *v == Val::String("two".into())) + ); + assert!( + output_map + .iter() + .any(|(k, v)| *k == Val::U32(3) && *v == Val::String("three".into())) + ); + } + _ => panic!("expected map"), + } + + // Sad path: type mismatch (wrong key type) + // Need to create a fresh instance because errors can leave the instance in a bad state + let mut store2 = Store::new(&engine, ()); + let instance2 = Linker::new(&engine).instantiate(&mut store2, &component)?; + let func2 = instance2.get_func(&mut store2, "echo").unwrap(); + + let err_map = vec![(Val::String("key".into()), Val::String("value".into()))]; + let err = Val::Map(err_map); + let err = func2.call(&mut store2, &[err], &mut output).unwrap_err(); + assert!(err.to_string().contains("type mismatch"), "{err}"); + + // Sad path: type mismatch (wrong value type) + let mut store3 = Store::new(&engine, ()); + let instance3 = Linker::new(&engine).instantiate(&mut store3, &component)?; + let func3 = instance3.get_func(&mut store3, "echo").unwrap(); + + let err_map2 = vec![(Val::U32(1), Val::U32(42))]; + let err = Val::Map(err_map2); + let err = func3.call(&mut store3, &[err], &mut output).unwrap_err(); + assert!(err.to_string().contains("type mismatch"), "{err}"); + + // Test empty map + let empty_map = vec![]; + let input = Val::Map(empty_map); + func.call(&mut store, &[input.clone()], &mut output)?; + match &output[0] { + Val::Map(output_map) => assert_eq!(output_map.len(), 0), + _ => panic!("expected map"), + } + + Ok(()) +} + +#[test] +fn maps_complex_types() -> Result<()> { + let engine = super::map_engine(); + let mut store = Store::new(&engine, ()); + + // Test map> + let component = Component::new(&engine, make_echo_component("(map string (list u32))", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let input_map = vec![ + ( + Val::String("first".into()), + Val::List(vec![Val::U32(1), Val::U32(2), Val::U32(3)]), + ), + ( + Val::String("second".into()), + Val::List(vec![Val::U32(4), Val::U32(5)]), + ), + ]; + let input = Val::Map(input_map); + + let mut output = [Val::Bool(false)]; + func.call(&mut store, &[input.clone()], &mut output)?; + + // Verify round-trip + match &output[0] { + Val::Map(output_map) => { + assert_eq!(output_map.len(), 2); + // Check first entry + let first_entry = output_map + .iter() + .find(|(k, _)| *k == Val::String("first".into())); + match first_entry { + Some((_, Val::List(list))) => { + assert_eq!(list.len(), 3); + assert_eq!(list[0], Val::U32(1)); + assert_eq!(list[1], Val::U32(2)); + assert_eq!(list[2], Val::U32(3)); + } + _ => panic!("expected list"), + } + // Check second entry + let second_entry = output_map + .iter() + .find(|(k, _)| *k == Val::String("second".into())); + match second_entry { + Some((_, Val::List(list))) => { + assert_eq!(list.len(), 2); + assert_eq!(list[0], Val::U32(4)); + assert_eq!(list[1], Val::U32(5)); + } + _ => panic!("expected list"), + } + } + _ => panic!("expected map"), + } + + Ok(()) +} + +#[test] +fn maps_equality() -> Result<()> { + let map1 = vec![ + (Val::U32(1), Val::String("one".into())), + (Val::U32(2), Val::String("two".into())), + ]; + + let map2 = vec![ + (Val::U32(1), Val::String("one".into())), + (Val::U32(2), Val::String("two".into())), + ]; + + // Maps with same content in same order should be equal + assert_eq!(Val::Map(map1.clone()), Val::Map(map2)); + + // Different values should not be equal + let map3 = vec![ + (Val::U32(1), Val::String("different".into())), + (Val::U32(2), Val::String("two".into())), + ]; + assert_ne!(Val::Map(map1.clone()), Val::Map(map3)); + + // Different keys should not be equal + let map4 = vec![ + (Val::U32(3), Val::String("one".into())), + (Val::U32(2), Val::String("two".into())), + ]; + assert_ne!(Val::Map(map1), Val::Map(map4)); + + // Empty maps should be equal + let empty1: Vec<(Val, Val)> = vec![]; + let empty2: Vec<(Val, Val)> = vec![]; + assert_eq!(Val::Map(empty1), Val::Map(empty2)); + + Ok(()) +} + +#[test] +fn maps_duplicate_keys() -> Result<()> { + let engine = super::map_engine(); + let mut store = Store::new(&engine, ()); + + let component = Component::new(&engine, make_echo_component("(map u32 string)", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + // Create a map with duplicate keys - Vec preserves all entries + let input_map = vec![ + (Val::U32(1), Val::String("first".into())), + (Val::U32(1), Val::String("last".into())), // Duplicate key + (Val::U32(2), Val::String("two".into())), + ]; + let input = Val::Map(input_map); + + let mut output = [Val::Bool(false)]; + func.call(&mut store, &[input.clone()], &mut output)?; + + // Verify all entries are preserved (Vec doesn't deduplicate) + match &output[0] { + Val::Map(output_map) => { + // Should have 3 entries (Vec preserves duplicates) + assert_eq!(output_map.len(), 3); + } + _ => panic!("expected map"), + } + + Ok(()) +} + +#[test] +fn maps_all_primitive_types() -> Result<()> { + let engine = super::map_engine(); + let mut store = Store::new(&engine, ()); + + // Test map + let component = Component::new(&engine, make_echo_component("(map u32 u32)", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let input_map = vec![(Val::U32(1), Val::U32(100)), (Val::U32(2), Val::U32(200))]; + let input = Val::Map(input_map); + + let mut output = [Val::Bool(false)]; + func.call(&mut store, &[input.clone()], &mut output)?; + assert_eq!(input, output[0]); + + // Test map + let component = Component::new(&engine, make_echo_component("(map string u32)", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let input_map = vec![ + (Val::String("one".into()), Val::U32(1)), + (Val::String("two".into()), Val::U32(2)), + ]; + let input = Val::Map(input_map); + + func.call(&mut store, &[input.clone()], &mut output)?; + assert_eq!(input, output[0]); + + Ok(()) +} + +#[test] +fn maps_alignment() -> Result<()> { + let engine = super::map_engine(); + let mut store = Store::new(&engine, ()); + + // Test map - key_size=1, value_align=8 + // This would fail with the alignment bug because value would be at offset 1 instead of 8 + let component = Component::new(&engine, make_echo_component("(map u8 u64)", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let input_map = vec![ + (Val::U8(1), Val::U64(100)), + (Val::U8(2), Val::U64(200)), + (Val::U8(3), Val::U64(300)), + ]; + let input = Val::Map(input_map); + + let mut output = [Val::Bool(false)]; + func.call(&mut store, &[input.clone()], &mut output)?; + assert_eq!(input, output[0]); + + // Test map - key_size=4, value_align=8 + // This would fail with the alignment bug because value would be at offset 4 instead of 8 + let component = Component::new(&engine, make_echo_component("(map u32 u64)", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let input_map = vec![(Val::U32(1), Val::U64(1000)), (Val::U32(2), Val::U64(2000))]; + let input = Val::Map(input_map); + + func.call(&mut store, &[input.clone()], &mut output)?; + assert_eq!(input, output[0]); + + // Test map - key_size=1, value_align=4 + // This would fail with the alignment bug because value would be at offset 1 instead of 4 + let component = Component::new(&engine, make_echo_component("(map u8 u32)", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let input_map = vec![(Val::U8(10), Val::U32(100)), (Val::U8(20), Val::U32(200))]; + let input = Val::Map(input_map); + + func.call(&mut store, &[input.clone()], &mut output)?; + assert_eq!(input, output[0]); + + // Test map - key_size=2, value_align=8 + // This would fail with the alignment bug because value would be at offset 2 instead of 8 + let component = Component::new(&engine, make_echo_component("(map u16 u64)", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let input_map = vec![ + (Val::U16(1), Val::U64(10000)), + (Val::U16(2), Val::U64(20000)), + ]; + let input = Val::Map(input_map); + + func.call(&mut store, &[input.clone()], &mut output)?; + assert_eq!(input, output[0]); + + Ok(()) +} + +#[test] +fn maps_large() -> Result<()> { + let engine = super::map_engine(); + let mut store = Store::new(&engine, ()); + + let component = Component::new(&engine, make_echo_component("(map u32 string)", 8))?; + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + // Create a map with many entries + let input_map: Vec<(Val, Val)> = (0..100) + .map(|i| (Val::U32(i), Val::String(format!("value_{i}")))) + .collect(); + let input = Val::Map(input_map); + + let mut output = [Val::Bool(false)]; + func.call(&mut store, &[input.clone()], &mut output)?; + + // Verify all entries are present + match &output[0] { + Val::Map(output_map) => { + assert_eq!(output_map.len(), 100); + for i in 0..100 { + assert!(output_map.iter().any(|(k, v)| { + *k == Val::U32(i) && *v == Val::String(format!("value_{i}")) + })); + } + } + _ => panic!("expected map"), + } + + Ok(()) +} + #[test] fn records() -> Result<()> { let engine = super::engine(); @@ -900,3 +1237,72 @@ fn introspection() -> Result<()> { ); Ok(()) } + +#[test] +fn flags_beyond_end() -> Result<()> { + let engine = super::engine(); + + let component = Component::new( + &engine, + r#" + (component + (type $f' (flags "a" "b")) + (export $f "f" (type $f')) + + (core module $m + (func (export "r") (param i32) (result i32) local.get 0) + ) + (core instance $i (instantiate $m)) + (func (export "run") (param "x" u32) (result $f) + (canon lift (core func $i "r"))) + ) + "#, + )?; + let mut store = Store::new(&engine, ()); + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let run = instance.get_func(&mut store, "run").unwrap(); + + let mut output = [Val::Bool(false)]; + run.call(&mut store, &[Val::U32(0)], &mut output)?; + assert_eq!(output[0], Val::Flags(vec![])); + + let mut output = [Val::Bool(false)]; + run.call(&mut store, &[Val::U32(1)], &mut output)?; + assert_eq!(output[0], Val::Flags(vec!["a".to_string()])); + + let mut output = [Val::Bool(false)]; + run.call(&mut store, &[Val::U32(2)], &mut output)?; + assert_eq!(output[0], Val::Flags(vec!["b".to_string()])); + + let mut output = [Val::Bool(false)]; + run.call(&mut store, &[Val::U32(3)], &mut output)?; + assert_eq!( + output[0], + Val::Flags(vec!["a".to_string(), "b".to_string()]) + ); + let mut output = [Val::Bool(false)]; + + run.call(&mut store, &[Val::U32(4)], &mut output)?; + assert_eq!(output[0], Val::Flags(vec![])); + + run.call(&mut store, &[Val::U32(5)], &mut output)?; + assert_eq!(output[0], Val::Flags(vec!["a".to_string()])); + + wasmtime::component::flags! { + F { + #[component(name = "a")] + const A; + #[component(name = "b")] + const B; + } + } + + let run = instance.get_typed_func::<(u32,), (F,)>(&mut store, "run")?; + assert_eq!(run.call(&mut store, (0,))?, (F::empty(),)); + assert_eq!(run.call(&mut store, (1,))?, (F::A,)); + assert_eq!(run.call(&mut store, (2,))?, (F::B,)); + assert_eq!(run.call(&mut store, (3,))?, (F::A | F::B,)); + assert_eq!(run.call(&mut store, (4,))?, (F::empty(),)); + assert_eq!(run.call(&mut store, (5,))?, (F::A,)); + Ok(()) +} diff --git a/tests/all/component_model/func.rs b/tests/all/component_model/func.rs index b74b1712b11c..e9dcfc3a589a 100644 --- a/tests/all/component_model/func.rs +++ b/tests/all/component_model/func.rs @@ -3846,3 +3846,803 @@ fn host_call_with_concurrency_disabled() -> Result<()> { Ok(()) } + +/// Tests map types with misaligned key/value combinations through the adapter +/// trampoline (component-to-component translation). +/// +/// This specifically tests the alignment bug where the value offset was +/// calculated as `key_size` instead of `align(key_size, value_align)`. +/// For map, the value should be at offset 8 (not 1). +/// +#[test] +fn map_trampoline_alignment() -> Result<()> { + // Test map - key_size=1, value_align=8 + // With the alignment bug, value would be read/written at offset 1 instead of 8 + let component = format!( + r#" +(component + (import "host" (func $host (param "m" (map u8 u64)) (result (map u8 u64)))) + + ;; Component A: the "destination" that receives and echoes back + (component $dst + (import "echo" (func $echo (param "m" (map u8 u64)) (result (map u8 u64)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u8 u64)) (result (map u8 u64)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + ;; Component B: the "source" that calls dst + (component $src + (import "echo" (func $echo (param "m" (map u8 u64)) (result (map u8 u64)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u8 u64)) (result (map u8 u64)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + ;; Wire: host -> dst -> src creates adapter trampolines between components + (instance $dst (instantiate $dst (with "echo" (func $host)))) + (instance $src (instantiate $src (with "echo" (func $dst "echo2")))) + (export "echo" (func $src "echo2")) +) +"# + ); + + let mut config = Config::new(); + config.wasm_component_model(true); + config.wasm_component_model_map(true); + let engine = Engine::new(&config)?; + let component = Component::new(&engine, component)?; + + let mut store = Store::new(&engine, ()); + let mut linker = Linker::new(&engine); + + linker.root().func_new("host", |_cx, _ty, args, results| { + results[0] = args[0].clone(); + Ok(()) + })?; + + let instance = linker.instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let test_data = vec![ + (Val::U8(1), Val::U64(0x0102030405060708)), + (Val::U8(2), Val::U64(0x1112131415161718)), + (Val::U8(255), Val::U64(0xFFFFFFFFFFFFFFFF)), + ]; + let input = Val::Map(test_data.clone()); + + let mut results = [Val::Bool(false)]; + func.call(&mut store, &[input], &mut results)?; + + match &results[0] { + Val::Map(output) => { + assert_eq!(output.len(), 3); + for (key, value) in &test_data { + assert!( + output.iter().any(|(k, v)| k == key && v == value), + "Missing or corrupted entry" + ); + } + } + _ => panic!("expected map"), + } + + Ok(()) +} + +/// Tests map alignment through trampoline +#[test] +fn map_trampoline_alignment_u32_u64() -> Result<()> { + // Test map - key_size=4, value_align=8 + // With the alignment bug, value would be read/written at offset 4 instead of 8 + let component = format!( + r#" +(component + (import "host" (func $host (param "m" (map u32 u64)) (result (map u32 u64)))) + + (component $dst + (import "echo" (func $echo (param "m" (map u32 u64)) (result (map u32 u64)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u32 u64)) (result (map u32 u64)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (component $src + (import "echo" (func $echo (param "m" (map u32 u64)) (result (map u32 u64)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u32 u64)) (result (map u32 u64)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (instance $dst (instantiate $dst (with "echo" (func $host)))) + (instance $src (instantiate $src (with "echo" (func $dst "echo2")))) + (export "echo" (func $src "echo2")) +) +"# + ); + + let mut config = Config::new(); + config.wasm_component_model(true); + config.wasm_component_model_map(true); + let engine = Engine::new(&config)?; + let component = Component::new(&engine, component)?; + + let mut store = Store::new(&engine, ()); + let mut linker = Linker::new(&engine); + + linker.root().func_new("host", |_cx, _ty, args, results| { + results[0] = args[0].clone(); + Ok(()) + })?; + + let instance = linker.instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let test_data = vec![ + (Val::U32(1), Val::U64(0x0102030405060708)), + (Val::U32(2), Val::U64(0x1112131415161718)), + ]; + let input = Val::Map(test_data.clone()); + + let mut results = [Val::Bool(false)]; + func.call(&mut store, &[input], &mut results)?; + + match &results[0] { + Val::Map(output) => { + assert_eq!(output.len(), 2); + for (key, value) in &test_data { + assert!( + output.iter().any(|(k, v)| k == key && v == value), + "Missing or corrupted entry" + ); + } + } + _ => panic!("expected map"), + } + + Ok(()) +} + +/// Tests map alignment through trampoline +#[test] +fn map_trampoline_alignment_u8_u32() -> Result<()> { + let component = format!( + r#" +(component + (import "host" (func $host (param "m" (map u8 u32)) (result (map u8 u32)))) + + (component $dst + (import "echo" (func $echo (param "m" (map u8 u32)) (result (map u8 u32)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u8 u32)) (result (map u8 u32)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (component $src + (import "echo" (func $echo (param "m" (map u8 u32)) (result (map u8 u32)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u8 u32)) (result (map u8 u32)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (instance $dst (instantiate $dst (with "echo" (func $host)))) + (instance $src (instantiate $src (with "echo" (func $dst "echo2")))) + (export "echo" (func $src "echo2")) +) +"# + ); + + let mut config = Config::new(); + config.wasm_component_model(true); + config.wasm_component_model_map(true); + let engine = Engine::new(&config)?; + let component = Component::new(&engine, component)?; + + let mut store = Store::new(&engine, ()); + let mut linker = Linker::new(&engine); + + linker.root().func_new("host", |_cx, _ty, args, results| { + results[0] = args[0].clone(); + Ok(()) + })?; + + let instance = linker.instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let test_data = vec![ + (Val::U8(1), Val::U32(0x01020304)), + (Val::U8(2), Val::U32(0x11121314)), + ]; + let input = Val::Map(test_data.clone()); + + let mut results = [Val::Bool(false)]; + func.call(&mut store, &[input], &mut results)?; + + match &results[0] { + Val::Map(output) => { + assert_eq!(output.len(), 2); + for (key, value) in &test_data { + assert!( + output.iter().any(|(k, v)| k == key && v == value), + "Missing or corrupted entry" + ); + } + } + _ => panic!("expected map"), + } + + Ok(()) +} + +/// Tests map alignment through trampoline +#[test] +fn map_trampoline_alignment_u16_u64() -> Result<()> { + let component = format!( + r#" +(component + (import "host" (func $host (param "m" (map u16 u64)) (result (map u16 u64)))) + + (component $dst + (import "echo" (func $echo (param "m" (map u16 u64)) (result (map u16 u64)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u16 u64)) (result (map u16 u64)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (component $src + (import "echo" (func $echo (param "m" (map u16 u64)) (result (map u16 u64)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u16 u64)) (result (map u16 u64)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (instance $dst (instantiate $dst (with "echo" (func $host)))) + (instance $src (instantiate $src (with "echo" (func $dst "echo2")))) + (export "echo" (func $src "echo2")) +) +"# + ); + + let mut config = Config::new(); + config.wasm_component_model(true); + config.wasm_component_model_map(true); + let engine = Engine::new(&config)?; + let component = Component::new(&engine, component)?; + + let mut store = Store::new(&engine, ()); + let mut linker = Linker::new(&engine); + + linker.root().func_new("host", |_cx, _ty, args, results| { + results[0] = args[0].clone(); + Ok(()) + })?; + + let instance = linker.instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let test_data = vec![ + (Val::U16(1), Val::U64(0x0102030405060708)), + (Val::U16(2), Val::U64(0x1112131415161718)), + ]; + let input = Val::Map(test_data.clone()); + + let mut results = [Val::Bool(false)]; + func.call(&mut store, &[input], &mut results)?; + + match &results[0] { + Val::Map(output) => { + assert_eq!(output.len(), 2); + for (key, value) in &test_data { + assert!( + output.iter().any(|(k, v)| k == key && v == value), + "Missing or corrupted entry" + ); + } + } + _ => panic!("expected map"), + } + + Ok(()) +} + +/// Tests map alignment through trampoline +#[test] +fn map_trampoline_alignment_u8_u16() -> Result<()> { + let component = format!( + r#" +(component + (import "host" (func $host (param "m" (map u8 u16)) (result (map u8 u16)))) + + (component $dst + (import "echo" (func $echo (param "m" (map u8 u16)) (result (map u8 u16)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u8 u16)) (result (map u8 u16)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (component $src + (import "echo" (func $echo (param "m" (map u8 u16)) (result (map u8 u16)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u8 u16)) (result (map u8 u16)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (instance $dst (instantiate $dst (with "echo" (func $host)))) + (instance $src (instantiate $src (with "echo" (func $dst "echo2")))) + (export "echo" (func $src "echo2")) +) +"# + ); + + let mut config = Config::new(); + config.wasm_component_model(true); + config.wasm_component_model_map(true); + let engine = Engine::new(&config)?; + let component = Component::new(&engine, component)?; + + let mut store = Store::new(&engine, ()); + let mut linker = Linker::new(&engine); + + linker.root().func_new("host", |_cx, _ty, args, results| { + results[0] = args[0].clone(); + Ok(()) + })?; + + let instance = linker.instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let test_data = vec![ + (Val::U8(1), Val::U16(0x0102)), + (Val::U8(2), Val::U16(0x1112)), + ]; + let input = Val::Map(test_data.clone()); + + let mut results = [Val::Bool(false)]; + func.call(&mut store, &[input], &mut results)?; + + match &results[0] { + Val::Map(output) => { + assert_eq!(output.len(), 2); + for (key, value) in &test_data { + assert!( + output.iter().any(|(k, v)| k == key && v == value), + "Missing or corrupted entry" + ); + } + } + _ => panic!("expected map"), + } + + Ok(()) +} + +/// Tests map alignment through trampoline (reverse case: key larger than value) +#[test] +fn map_trampoline_alignment_u64_u8() -> Result<()> { + let component = format!( + r#" +(component + (import "host" (func $host (param "m" (map u64 u8)) (result (map u64 u8)))) + + (component $dst + (import "echo" (func $echo (param "m" (map u64 u8)) (result (map u64 u8)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u64 u8)) (result (map u64 u8)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (component $src + (import "echo" (func $echo (param "m" (map u64 u8)) (result (map u64 u8)))) + (core module $libc + (memory (export "memory") 1) + {REALLOC_AND_FREE} + ) + (core module $echo_mod + (import "" "echo" (func $echo (param i32 i32 i32))) + (import "libc" "memory" (memory 0)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + + (func (export "echo") (param i32 i32) (result i32) + (local $retptr i32) + (local.set $retptr + (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $echo (local.get 0) (local.get 1) (local.get $retptr)) + local.get $retptr + ) + ) + (core instance $libc (instantiate $libc)) + (core func $echo_lower (canon lower (func $echo) + (memory $libc "memory") + (realloc (func $libc "realloc")) + )) + (core instance $echo_inst (instantiate $echo_mod + (with "libc" (instance $libc)) + (with "" (instance (export "echo" (func $echo_lower)))) + )) + (func (export "echo2") (param "m" (map u64 u8)) (result (map u64 u8)) + (canon lift + (core func $echo_inst "echo") + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + ) + + (instance $dst (instantiate $dst (with "echo" (func $host)))) + (instance $src (instantiate $src (with "echo" (func $dst "echo2")))) + (export "echo" (func $src "echo2")) +) +"# + ); + + let mut config = Config::new(); + config.wasm_component_model(true); + config.wasm_component_model_map(true); + let engine = Engine::new(&config)?; + let component = Component::new(&engine, component)?; + + let mut store = Store::new(&engine, ()); + let mut linker = Linker::new(&engine); + + linker.root().func_new("host", |_cx, _ty, args, results| { + results[0] = args[0].clone(); + Ok(()) + })?; + + let instance = linker.instantiate(&mut store, &component)?; + let func = instance.get_func(&mut store, "echo").unwrap(); + + let test_data = vec![ + (Val::U64(0x0102030405060708), Val::U8(42)), + (Val::U64(0x1112131415161718), Val::U8(99)), + ]; + let input = Val::Map(test_data.clone()); + + let mut results = [Val::Bool(false)]; + func.call(&mut store, &[input], &mut results)?; + + match &results[0] { + Val::Map(output) => { + assert_eq!(output.len(), 2); + for (key, value) in &test_data { + assert!( + output.iter().any(|(k, v)| k == key && v == value), + "Missing or corrupted entry" + ); + } + } + _ => panic!("expected map"), + } + + Ok(()) +} diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs index 9b99e0d766d3..6f1fed7895ba 100644 --- a/tests/all/component_model/resources.rs +++ b/tests/all/component_model/resources.rs @@ -187,7 +187,27 @@ fn resource_any() -> Result<()> { // reuse of `t` should fail, despite it pointing to a valid resource assert_eq!( t_dtor.call(&mut store, (t,)).unwrap_err().to_string(), - "host-owned resource is being used with the wrong type" + "host-owned resource was already de-allocated" + ); + } + + { + let mut store = Store::new(&engine, ()); + let i = linker.instantiate(&mut store, &c)?; + let t_ctor = i.get_typed_func::<(u32,), (ResourceAny,)>(&mut store, "[constructor]t")?; + let t_dtor = i.get_typed_func::<(ResourceAny,), ()>(&mut store, "drop-t")?; + + // `t0` is placed at host index 0 + let (t0,) = t_ctor.call(&mut store, (100,))?; + t_dtor.call(&mut store, (t0,))?; + + // `t1` is also placed at host index 0 since `t0` was deallocated + let (_t1,) = t_ctor.call(&mut store, (100,))?; + + // reuse of `t0` should fail, despite it pointing to a valid resource + assert_eq!( + t_dtor.call(&mut store, (t0,)).unwrap_err().to_string(), + "host-owned resource was already de-allocated" ); } diff --git a/tests/all/component_model/strings.rs b/tests/all/component_model/strings.rs index d341ef36a184..173f829e7bab 100644 --- a/tests/all/component_model/strings.rs +++ b/tests/all/component_model/strings.rs @@ -3,7 +3,7 @@ use super::REALLOC_AND_FREE; use wasmtime::Result; use wasmtime::component::{Component, Linker}; -use wasmtime::{Engine, Store, StoreContextMut, Trap}; +use wasmtime::{Config, Engine, PoolingAllocationConfig, Store, StoreContextMut, Trap}; const UTF16_TAG: u32 = 1 << 31; @@ -576,3 +576,73 @@ fn test_raw_when_encoded( Err(e) => Ok(Some(e)), } } + +#[test] +fn pass_string_on_component_boundary() -> Result<()> { + // Configure an engine such that linear memories are allocated right next + // to each other and are 1 wasm page large. This'll ensure that the string + // at the beginning of the second memory is at the end of the first memory. + let mut pooling_config = PoolingAllocationConfig::new(); + pooling_config.total_component_instances(3); + pooling_config.total_memories(2); + pooling_config.total_tables(0); + pooling_config.total_stacks(0); + pooling_config.max_memory_size(65536); + let mut config = Config::new(); + config.memory_guard_size(0); + config.memory_reservation(65536); + config.allocation_strategy(pooling_config); + let engine = Engine::new(&config)?; + + let component = r#" +(component + ;; This component is instantiated first so its allocation function returns a + ;; pointer at the end of memory which will be right up against the next + ;; linear memory. + (component $c + (core module $m + (func (export "") (param i32 i32)) + (func (export "realloc") (param i32 i32 i32 i32) (result i32) i32.const 65520) + (memory (export "memory") 1) + ) + (core instance $m (instantiate $m)) + (func (export "a") (param "a" string) + (canon lift (core func $m "") (realloc (func $m "realloc")) (memory $m "memory")) + ) + ) + + ;; This component is instantiated second meaning its memory is after the + ;; one above, so the string is placed first thing in linear memory. + (component $c2 + (import "a" (func $f (param "a" string))) + (core module $libc + (memory (export "memory") 1) + (data (memory 0) (i32.const 0) "0123456789abcdef") + ) + (core instance $libc (instantiate $libc)) + (core func $f (canon lower (func $f) (memory $libc "memory"))) + (core module $m + (import "" "" (func $f (param i32 i32))) + (func (export "f") + (call $f + (i32.const 0) ;; ptr + (i32.const 16)) ;; len + ) + ) + (core instance $m (instantiate $m (with "" (instance (export "" (func $f)))))) + (func (export "f") (canon lift (core func $m "f"))) + ) + + (instance $c (instantiate $c)) + (instance $c2 (instantiate $c2 (with "a" (func $c "a")))) + (export "f" (func $c2 "f")) +) +"#; + + let component = Component::new(&engine, &component)?; + let mut store = Store::new(&engine, ()); + let instance = Linker::new(&engine).instantiate(&mut store, &component)?; + let func = instance.get_typed_func::<(), ()>(&mut store, "f")?; + func.call(&mut store, ())?; + Ok(()) +} diff --git a/tests/all/debug.rs b/tests/all/debug.rs index e8f5cacdcedb..7f8b4b2062af 100644 --- a/tests/all/debug.rs +++ b/tests/all/debug.rs @@ -4,7 +4,8 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use wasmtime::{ AsContextMut, Caller, Config, DebugEvent, DebugHandler, Engine, Extern, FrameHandle, Func, - Global, GlobalType, Instance, Module, Mutability, Store, StoreContextMut, Val, ValType, + Global, GlobalType, Instance, Module, ModulePC, Mutability, Store, StoreContextMut, Val, + ValType, }; use crate::async_functions::PollOnce; @@ -108,8 +109,12 @@ fn stack_values_two_frames() -> wasmtime::Result<()> { 1 ); assert_eq!( - stack.wasm_function_index_and_pc(&mut caller)?.unwrap().1, - 65 + stack + .wasm_function_index_and_pc(&mut caller)? + .unwrap() + .1 + .raw(), + 67 ); assert_eq!(stack.num_locals(&mut caller)?, 2); @@ -129,8 +134,12 @@ fn stack_values_two_frames() -> wasmtime::Result<()> { 0 ); assert_eq!( - stack.wasm_function_index_and_pc(&mut caller)?.unwrap().1, - 55 + stack + .wasm_function_index_and_pc(&mut caller)? + .unwrap() + .1 + .raw(), + 57 ); let stack = stack.parent(&mut caller)?; @@ -289,8 +298,12 @@ fn stack_values_two_activations() -> wasmtime::Result<()> { 0 ); assert_eq!( - stack.wasm_function_index_and_pc(&mut caller)?.unwrap().1, - 56 + stack + .wasm_function_index_and_pc(&mut caller)? + .unwrap() + .1 + .raw(), + 58 ); assert!(Module::same( stack.module(&mut caller)?.unwrap(), @@ -317,8 +330,12 @@ fn stack_values_two_activations() -> wasmtime::Result<()> { 0 ); assert_eq!( - stack.wasm_function_index_and_pc(&mut caller)?.unwrap().1, - 56 + stack + .wasm_function_index_and_pc(&mut caller)? + .unwrap() + .1 + .raw(), + 58 ); assert!(Module::same( stack.module(&mut caller)?.unwrap(), @@ -694,18 +711,23 @@ async fn hostcall_trap_events() -> wasmtime::Result<()> { }, r#" (module - (func (export "main") + (func (export "main") (result i32) i32.const 0 i32.const 0 i32.div_u - drop)) + drop + i32.const 42)) "#, )?; debug_event_checker!( D, store, { 0 ; - wasmtime::DebugEvent::Trap(wasmtime_environ::Trap::IntegerDivisionByZero) => {} + wasmtime::DebugEvent::Trap(wasmtime_environ::Trap::IntegerDivisionByZero) => { + let frame = store.debug_exit_frames().next().unwrap(); + let (_func, pc) = frame.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); + assert_eq!(pc.raw(), 0x26); + } } ); @@ -714,7 +736,7 @@ async fn hostcall_trap_events() -> wasmtime::Result<()> { let instance = Instance::new_async(&mut store, &module, &[]).await?; let func = instance.get_func(&mut store, "main").unwrap(); - let mut results = []; + let mut results = [Val::I32(0)]; let result = func.call_async(&mut store, &[], &mut results).await; assert!(result.is_err()); // Uncaught trap. assert_eq!(counter.load(Ordering::Relaxed), 1); @@ -794,7 +816,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { assert_eq!(stack.local(&mut store, 1).unwrap().unwrap_i32(), 2); let (func, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); assert_eq!(func.as_u32(), 0); - assert_eq!(pc, 0x28); + assert_eq!(pc.raw(), 0x28); let stack = stack.parent(&mut store).unwrap(); assert!(stack.is_none()); } @@ -806,7 +828,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { store .edit_breakpoints() .unwrap() - .add_breakpoint(&module, 0x28)?; + .add_breakpoint(&module, ModulePC::new(0x28))?; let instance = Instance::new_async(&mut store, &module, &[]).await?; let func = instance.get_func(&mut store, "main").unwrap(); @@ -819,12 +841,12 @@ async fn breakpoint_events() -> wasmtime::Result<()> { let breakpoints = store.breakpoints().unwrap().collect::>(); assert_eq!(breakpoints.len(), 1); assert!(Module::same(&breakpoints[0].module, &module)); - assert_eq!(breakpoints[0].pc, 0x28); + assert_eq!(breakpoints[0].pc, ModulePC::new(0x28)); store .edit_breakpoints() .unwrap() - .remove_breakpoint(&module, 0x28)?; + .remove_breakpoint(&module, ModulePC::new(0x28))?; func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) .await?; assert_eq!(counter.load(Ordering::Relaxed), 1); // Should not have incremented from above. @@ -841,7 +863,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { wasmtime::DebugEvent::Breakpoint => { let stack = store.debug_exit_frames().next().unwrap(); let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); - assert_eq!(pc, 0x24); + assert_eq!(pc.raw(), 0x24); } }, { @@ -849,7 +871,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { wasmtime::DebugEvent::Breakpoint => { let stack = store.debug_exit_frames().next().unwrap(); let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); - assert_eq!(pc, 0x26); + assert_eq!(pc.raw(), 0x26); } }, { @@ -857,7 +879,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { wasmtime::DebugEvent::Breakpoint => { let stack = store.debug_exit_frames().next().unwrap(); let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); - assert_eq!(pc, 0x28); + assert_eq!(pc.raw(), 0x28); } }, { @@ -865,7 +887,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { wasmtime::DebugEvent::Breakpoint => { let stack = store.debug_exit_frames().next().unwrap(); let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); - assert_eq!(pc, 0x29); + assert_eq!(pc.raw(), 0x29); } } ); @@ -881,7 +903,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { store .edit_breakpoints() .unwrap() - .add_breakpoint(&module, 0x28) + .add_breakpoint(&module, ModulePC::new(0x28)) .unwrap(); // Now disable single-stepping. The single breakpoint set above @@ -944,7 +966,7 @@ async fn breakpoints_in_inlined_code() -> wasmtime::Result<()> { store .edit_breakpoints() .unwrap() - .add_breakpoint(&module, 0x2d)?; // `i32.add` in `$f`. + .add_breakpoint(&module, ModulePC::new(0x2d))?; // `i32.add` in `$f`. let instance = Instance::new_async(&mut store, &module, &[]).await?; let func_main = instance.get_func(&mut store, "main").unwrap(); @@ -1420,3 +1442,186 @@ async fn early_epoch_yield_still_has_vmctx() -> wasmtime::Result<()> { Ok(()) } + +#[tokio::test] +#[cfg_attr(miri, ignore)] +async fn breakpoint_slips_to_first_opcode() -> wasmtime::Result<()> { + let _ = env_logger::try_init(); + + // Breakpoints set at the function body start (which includes the + // local declarations and precedes the first opcode) should be + // "slipped" forward to the first opcode. This matches how LLDB + // sets breakpoints using DWARF `DW_AT_low_pc`. + // + // For the WAT below, `wasm-objdump -d` shows: + // + // ``` + // 000023 func[0]
: + // 000024: 20 00 | local.get 0 + // 000026: 20 01 | local.get 1 + // 000028: 6a | i32.add + // 000029: 0b | end + // ``` + // + // 0x23 is the function body start (locals count byte), while 0x24 + // is the first opcode. Setting a breakpoint at 0x23 should slip + // to 0x24. + let (module, mut store) = get_module_and_store( + |_config| {}, + r#" + (module + (func (export "main") (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.add)) + "#, + )?; + + debug_event_checker!( + D, store, + { 0 ; + wasmtime::DebugEvent::Breakpoint => { + let stack = store.debug_exit_frames().next().unwrap(); + let (func, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); + assert_eq!(func.as_u32(), 0); + // The breakpoint should fire at the first opcode + // (0x24), not at the function body start (0x23). + assert_eq!(pc.raw(), 0x24); + } + } + ); + + let (handler, counter) = D::new_and_counter(); + store.set_debug_handler(handler); + + store + .edit_breakpoints() + .unwrap() + .add_breakpoint(&module, ModulePC::new(0x23))?; + + let instance = Instance::new_async(&mut store, &module, &[]).await?; + let func = instance.get_func(&mut store, "main").unwrap(); + let mut results = [Val::I32(0)]; + func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) + .await?; + assert_eq!(counter.load(Ordering::Relaxed), 1); + assert_eq!(results[0].unwrap_i32(), 3); + + // The actual breakpoint stored should be at the slipped PC. + let breakpoints = store.breakpoints().unwrap().collect::>(); + assert_eq!(breakpoints.len(), 1); + assert_eq!(breakpoints[0].pc, ModulePC::new(0x24)); + + // Removing with the originally requested PC should work. + store + .edit_breakpoints() + .unwrap() + .remove_breakpoint(&module, ModulePC::new(0x23))?; + func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) + .await?; + // Counter should not have incremented now that we removed the + // breakpoint. + assert_eq!(counter.load(Ordering::Relaxed), 1); + + Ok(()) +} + +#[tokio::test] +#[cfg_attr(miri, ignore)] +async fn component_module_relative_breakpoint_pcs() -> wasmtime::Result<()> { + use wasmtime::component::{Component, Linker}; + + let _ = env_logger::try_init(); + + let m1_body = r#"(func (export "f1") (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.add)"#; + let m2_body = r#"(func (export "f2") (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.mul)"#; + + let _m1_wasm = wat::parse_str(&format!("(module {m1_body})"))?; + let _m2_wasm = wat::parse_str(&format!("(module {m2_body})"))?; + + let component_wat = format!( + r#"(component + (core module $m1 {m1_body}) + (core instance $i1 (instantiate $m1)) + (core module $m2 {m2_body}) + (core instance $i2 (instantiate $m2)) + (func (export "f1") (param "a" s32) (param "b" s32) (result s32) + (canon lift (core func $i1 "f1"))) + (func (export "f2") (param "a" s32) (param "b" s32) (result s32) + (canon lift (core func $i2 "f2"))))"#, + ); + + let mut config = Config::default(); + config.guest_debug(true); + let engine = Engine::new(&config)?; + + let component = Component::new(&engine, &component_wat)?; + let linker: Linker<()> = Linker::new(&engine); + let mut store = Store::new(&engine, ()); + + let instance = linker.instantiate_async(&mut store, &component).await?; + + let modules = store.debug_all_modules(); + assert_eq!(modules.len(), 2); + + // The i32.add / i32.mul instruction is at module-relative offset + // 0x26 in both modules. + let breakpoint_pc = ModulePC::new(0x26); + + // Record breakpoint PCs seen in each event. + let observed_pcs = Arc::new(Mutex::new(Vec::<(u32, u32)>::new())); + let observed_pcs_clone = observed_pcs.clone(); + + #[derive(Clone)] + struct D(Arc>>); + impl DebugHandler for D { + type Data = (); + fn handle( + &self, + mut store: StoreContextMut<'_, ()>, + _event: DebugEvent<'_>, + ) -> impl std::future::Future + Send { + let frame = store.debug_exit_frames().next().unwrap(); + let (func, pc) = frame + .wasm_function_index_and_pc(&mut store) + .unwrap() + .unwrap(); + self.0.lock().unwrap().push((func.as_u32(), pc.raw())); + async {} + } + } + store.set_debug_handler(D(observed_pcs_clone)); + + // Set breakpoints at the same module-relative PC (0x26) in both + // modules. + store + .edit_breakpoints() + .unwrap() + .add_breakpoint(&modules[0], breakpoint_pc)?; + store + .edit_breakpoints() + .unwrap() + .add_breakpoint(&modules[1], breakpoint_pc)?; + + let f1 = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "f1")?; + let (result,) = f1.call_async(&mut store, (3, 5)).await?; + assert_eq!(result, 8); + + let f2 = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "f2")?; + let (result,) = f2.call_async(&mut store, (3, 5)).await?; + assert_eq!(result, 15); + + // Both breakpoint PCs should be 0x26 (module-relative). + let pcs = observed_pcs.lock().unwrap(); + assert_eq!(pcs.len(), 2); + assert_eq!(pcs[0], (0, 0x26)); + assert_eq!(pcs[1], (0, 0x26)); + + Ok(()) +} diff --git a/tests/all/debug_component.rs b/tests/all/debug_component.rs new file mode 100644 index 000000000000..6dc7ead5d625 --- /dev/null +++ b/tests/all/debug_component.rs @@ -0,0 +1,64 @@ +#![cfg(not(miri))] + +use crate::cli_tests::get_wasmtime_command; +use test_programs_artifacts::*; +use wasmtime::Result; + +fn run_debugger_test(debugger_component: &str, debuggee: &str, test_mode: &str) -> Result<()> { + let mut cmd = get_wasmtime_command()?; + cmd.args(&[ + "run", + "-Ccache=n", + &format!("-Ddebugger={debugger_component}"), + &format!("-Darg={test_mode}"), + "-Dinherit-stderr=y", + debuggee, + ]); + let output = cmd.output()?; + let stderr = String::from_utf8_lossy(&output.stderr); + if !output.status.success() { + wasmtime::bail!( + "wasmtime failed with status {}\nstderr:\n{stderr}", + output.status, + ); + } + assert!( + stderr.contains("OK"), + "expected 'OK' in stderr, got:\n{stderr}" + ); + Ok(()) +} + +#[test] +fn debugger_debuggee_simple() -> Result<()> { + run_debugger_test( + DEBUGGER_COMPONENT_COMPONENT, + DEBUGGER_DEBUGGEE_SIMPLE_COMPONENT, + "simple", + ) +} + +#[test] +fn debugger_debuggee_loop() -> Result<()> { + run_debugger_test( + DEBUGGER_COMPONENT_COMPONENT, + DEBUGGER_DEBUGGEE_LOOP_COMPONENT, + "loop", + ) +} + +#[test] +fn debugger_component() -> Result<()> { + // This is present so that `assert_test_exists` can assert presence of unit-tests for all + // components. The debugger component itself exists in this list alongside all the debuggees; + // we only test the debuggees (with the debugger implicitly used for each). + Ok(()) +} + +macro_rules! assert_test_exists { + ($name:ident) => { + #[expect(unused_imports, reason = "here to assert the test exists")] + use self::$name as _; + }; +} +foreach_debugger!(assert_test_exists); diff --git a/tests/all/fuel.rs b/tests/all/fuel.rs index 064366512f78..d6a77cf886db 100644 --- a/tests/all/fuel.rs +++ b/tests/all/fuel.rs @@ -57,7 +57,7 @@ fn fuel_consumed(config: &Config, wasm: &[u8]) -> Result { Ok(u64::MAX - store.get_fuel()?) } -#[wasmtime_test(wasm_features(gc, function_references))] +#[wasmtime_test(wasm_features(gc, function_references, bulk_memory))] #[cfg_attr(miri, ignore)] fn iloop(config: &mut Config) -> Result<()> { config.consume_fuel(true); diff --git a/tests/all/func.rs b/tests/all/func.rs index 7b068b80908b..cf789053499d 100644 --- a/tests/all/func.rs +++ b/tests/all/func.rs @@ -1425,7 +1425,7 @@ fn wrap_multiple_results(config: &mut Config) -> wasmtime::Result<()> { } } -#[wasmtime_test(wasm_features(reference_types, gc_types))] +#[wasmtime_test(wasm_features(reference_types, bulk_memory))] #[cfg_attr(miri, ignore)] fn trampoline_for_declared_elem(config: &mut Config) -> wasmtime::Result<()> { let engine = Engine::new(&config)?; @@ -1675,7 +1675,7 @@ fn calls_with_funcref_and_externref(config: &mut Config) -> wasmtime::Result<()> Ok(()) } -#[wasmtime_test(wasm_features(function_references))] +#[wasmtime_test(wasm_features(function_references, bulk_memory))] #[cfg_attr(miri, ignore)] fn typed_concrete_param(config: &mut Config) -> wasmtime::Result<()> { let engine = Engine::new(&config)?; @@ -1748,7 +1748,7 @@ fn typed_concrete_param(config: &mut Config) -> wasmtime::Result<()> { Ok(()) } -#[wasmtime_test(wasm_features(function_references))] +#[wasmtime_test(wasm_features(function_references, bulk_memory))] #[cfg_attr(miri, ignore)] fn typed_concrete_result(config: &mut Config) -> wasmtime::Result<()> { let engine = Engine::new(&config)?; @@ -1924,7 +1924,7 @@ fn call_wasm_passing_subtype_func_param(config: &mut Config) -> wasmtime::Result Ok(()) } -#[wasmtime_test(wasm_features(gc, function_references))] +#[wasmtime_test(wasm_features(gc, function_references, bulk_memory))] #[cfg_attr(miri, ignore)] fn call_wasm_getting_subtype_func_return(config: &mut Config) -> wasmtime::Result<()> { let engine = Engine::new(&config)?; diff --git a/tests/all/gc.rs b/tests/all/gc.rs index 55b6e9e8b5b6..a49ada513380 100644 --- a/tests/all/gc.rs +++ b/tests/all/gc.rs @@ -1800,3 +1800,80 @@ fn array_init_elem_oom() -> Result<()> { Ok(()) } + +// The result of a `select` instruction with a GC reference type should be +// declared as needing a stack map. +#[test] +#[cfg_attr(miri, ignore)] +fn select_gc_ref_stack_map() -> Result<()> { + let _ = env_logger::try_init(); + + let mut config = Config::new(); + config.wasm_function_references(true); + config.wasm_gc(true); + // Use pooling allocator with a tiny GC heap so that GC is triggered + // frequently and freed memory is reused quickly. + let mut pool = crate::small_pool_config(); + pool.max_memory_size(1 << 16); // 64 KiB + config.allocation_strategy(pool); + + let engine = Engine::new(&config)?; + let module = Module::new( + &engine, + r#" + (module + (type $pair (struct (field (mut i32)))) + (type $arr (array (mut i8))) + + ;; Allocate many objects to fill the GC heap and trigger + ;; collection. After GC frees everything, subsequent + ;; allocations reuse the freed memory. + (func $force_gc + (local i32) + (local.set 0 (i32.const 64)) + (loop $l + (drop (array.new $arr (i32.const 0) (i32.const 1024))) + (local.set 0 (i32.sub (local.get 0) (i32.const 1))) + (br_if $l (local.get 0)) + ) + ) + + (func (export "test") (param $cond i32) (result i32) + ;; The select result stays on the Wasm operand stack (never + ;; stored in a local variable). If the new SSA value created + ;; by select is not declared for stack maps, the GC will + ;; free it. + (select (result (ref null $pair)) + (struct.new $pair (i32.const 111)) + (ref.null $pair) + (local.get $cond) + ) + + ;; This call is a safepoint. The select result is live + ;; across it. The called function triggers GC which will + ;; free the struct if it is incorrectly omitted from the + ;; safepoint's stack map, and the new allocations would + ;; overwrite the freed memory. + (call $force_gc) + + ;; Use the select result. If it was incorrectly freed, then + ;; this will have the wrong value. + (struct.get $pair 0) + ) + ) + "#, + )?; + + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let test = instance.get_typed_func::<(i32,), i32>(&mut store, "test")?; + + // Run multiple times to increase chance of triggering GC at the right + // moment. + for _ in 0..30 { + let result = test.call(&mut store, (1,))?; + assert_eq!(result, 111); + } + + Ok(()) +} diff --git a/tests/all/guest_debug/mod.rs b/tests/all/guest_debug/mod.rs new file mode 100644 index 000000000000..2a1342e597ad --- /dev/null +++ b/tests/all/guest_debug/mod.rs @@ -0,0 +1,398 @@ +//! Integration tests for guest-debug support (gdbstub + LLDB). +//! +//! These tests launch `wasmtime run` with `-g `, connect LLDB via +//! the wasm remote protocol, execute debug scripts (set breakpoints, +//! continue, step, inspect variables), and validate output. +//! +//! Requirements: +//! - LLDB with Wasm plugin support (`LLDB` env var or `/opt/wasi-sdk/bin/lldb` by default) +//! - `WASI_SDK_PATH` env var set (for C test programs) +//! - Built with `--features gdbstub` + +use filecheck::{CheckerBuilder, NO_VARIABLES}; +use std::io::{BufRead, BufReader, Write}; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::process::{Child, Command, Stdio}; +use std::time::Duration; +use test_programs_artifacts::*; +use wasmtime::{Result, bail, format_err}; + +/// Find the wasmtime binary built alongside the test binary. +fn wasmtime_binary() -> std::path::PathBuf { + let mut me = std::env::current_exe().expect("current_exe specified"); + me.pop(); // chop off file name + me.pop(); // chop off `deps` + if cfg!(target_os = "windows") { + me.push("wasmtime.exe"); + } else { + me.push("wasmtime"); + } + me +} + +/// Find an available TCP port by binding to port 0. +fn free_port() -> u16 { + TcpListener::bind("127.0.0.1:0") + .unwrap() + .local_addr() + .unwrap() + .port() +} + +/// Path to the wasm-aware LLDB. +fn lldb_path() -> String { + std::env::var("LLDB").unwrap_or("/opt/wasi-sdk/bin/lldb".to_string()) +} + +/// The readiness marker printed by the gdbstub to stderr. +const GDBSTUB_READY_MARKER: &str = "Debugger listening on"; + +/// A running wasmtime process with a gdbstub endpoint. +struct WasmtimeWithGdbstub { + child: Child, + /// Keeps the stderr pipe alive to avoid SIGPIPE on the child. + /// Also used by serve tests to read the HTTP address. + stderr_reader: BufReader, +} + +impl WasmtimeWithGdbstub { + /// Spawn wasmtime and wait for stderr to contain the gdbstub + /// readiness marker. + fn spawn( + subcmd: &str, + gdbstub_port: u16, + extra_args: &[&str], + timeout: Duration, + ) -> Result { + let mut cmd = Command::new(wasmtime_binary()); + cmd.arg(subcmd) + .arg(format!("-g{gdbstub_port}")) + .args(extra_args) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::piped()); + eprintln!("spawning: {cmd:?}"); + let mut child = cmd.spawn()?; + + let stderr = child.stderr.take().unwrap(); + let mut reader = BufReader::new(stderr); + let deadline = std::time::Instant::now() + timeout; + let mut line = String::new(); + loop { + if std::time::Instant::now() > deadline { + let _ = child.kill(); + bail!("timed out waiting for gdbstub readiness"); + } + line.clear(); + reader.read_line(&mut line)?; + eprintln!("wasmtime stderr: {}", line.trim_end()); + if line.contains(GDBSTUB_READY_MARKER) { + return Ok(Self { + child, + stderr_reader: reader, + }); + } + if line.is_empty() { + let _ = child.kill(); + let status = child.wait()?; + bail!("wasmtime exited ({status}) without readiness marker"); + } + } + } + + /// Read stderr lines until one contains `marker`, returning that line. + fn wait_for_stderr(&mut self, marker: &str, timeout: Duration) -> Result { + let deadline = std::time::Instant::now() + timeout; + let mut line = String::new(); + loop { + if std::time::Instant::now() > deadline { + bail!("timed out waiting for '{marker}' on stderr"); + } + line.clear(); + self.stderr_reader.read_line(&mut line)?; + eprintln!("wasmtime stderr: {}", line.trim_end()); + if line.contains(marker) { + return Ok(line); + } + if line.is_empty() { + bail!("wasmtime stderr closed before finding '{marker}'"); + } + } + } +} + +/// Run an LLDB debug script against a gdbstub endpoint. +/// +/// Connects LLDB to `127.0.0.1:` using the Wasm plugin, +/// executes the given script commands, and returns LLDB's stdout. +fn lldb_with_gdbstub_script(port: u16, script: &str) -> Result { + let _ = env_logger::try_init(); + + let mut cmd = Command::new(lldb_path()); + cmd.arg("--batch"); + cmd.arg("-o").arg(format!( + "process connect --plugin wasm connect://127.0.0.1:{port}" + )); + for line in script.lines() { + let line = line.trim(); + if !line.is_empty() { + cmd.arg("-o").arg(line); + } + } + + eprintln!("Running LLDB: {cmd:?}"); + let output = cmd.output()?; + + let stdout = String::from_utf8(output.stdout)?; + let stderr = String::from_utf8(output.stderr)?; + eprintln!("--- LLDB stdout ---\n{stdout}"); + eprintln!("--- LLDB stderr ---\n{stderr}"); + + Ok(stdout) +} + +/// Validate output against FileCheck-style directives. +fn check_output(output: &str, directives: &str) -> Result<()> { + let mut builder = CheckerBuilder::new(); + builder + .text(directives) + .map_err(|e| format_err!("unable to build checker: {e:?}"))?; + let checker = builder.finish(); + let check = checker + .explain(output, NO_VARIABLES) + .map_err(|e| format_err!("{e:?}"))?; + assert!(check.0, "didn't pass check {}", check.1); + Ok(()) +} + +/// Test that breakpoints can be set at the initial stop (before any +/// continue), then hit when the program runs. +#[test] +#[ignore] +fn guest_debug_cli_fib_breakpoint() -> Result<()> { + let port = free_port(); + let mut wt = WasmtimeWithGdbstub::spawn( + "run", + port, + &["-Ccache=n", GUEST_DEBUG_FIB], + Duration::from_secs(30), + )?; + + // Set breakpoint at the initial stop, *before* continuing. + // This tests that modules are visible immediately. + let output = lldb_with_gdbstub_script( + port, + r#" +b fib +c +fr v +c +"#, + )?; + wt.child.kill().ok(); + wt.child.wait()?; + + check_output( + &output, + r#" +check: stop reason +check: fib +check: n = +"#, + )?; + Ok(()) +} + +/// Test single-stepping within fib. +#[test] +#[ignore] +fn guest_debug_cli_fib_step() -> Result<()> { + let port = free_port(); + let mut wt = WasmtimeWithGdbstub::spawn( + "run", + port, + &["-Ccache=n", GUEST_DEBUG_FIB], + Duration::from_secs(30), + )?; + + let output = lldb_with_gdbstub_script( + port, + r#" +b fib +c +n +n +n +fr v +c +"#, + )?; + wt.child.kill().ok(); + wt.child.wait()?; + + check_output( + &output, + r#" +check: stop reason +check: fib +"#, + )?; + Ok(()) +} + +/// Helper: send an HTTP/1.0 request and return the full response. +fn http_request(addr: SocketAddr, path: &str) -> Result { + let mut tcp = TcpStream::connect_timeout(&addr, Duration::from_secs(5))?; + tcp.set_read_timeout(Some(Duration::from_secs(5)))?; + write!(tcp, "GET {path} HTTP/1.0\r\nHost: localhost\r\n\r\n")?; + let mut response = String::new(); + let _ = std::io::Read::read_to_string(&mut tcp, &mut response); + Ok(response) +} + +/// Parse an HTTP serve address from a "Serving HTTP on http://addr/" line. +fn parse_http_addr(line: &str) -> Result { + line.find("127.0.0.1") + .and_then(|start| { + let addr = &line[start..]; + let end = addr.find('/')?; + addr[..end].parse().ok() + }) + .ok_or_else(|| format_err!("failed to parse HTTP address from: {line}")) +} + +/// Start serve under debugger, continue, and send multiple HTTP requests +/// to verify instance reuse works correctly under the debugger. +#[test] +#[ignore] +fn guest_debug_serve_requests() -> Result<()> { + let gdb_port = free_port(); + + let mut wt = WasmtimeWithGdbstub::spawn( + "serve", + gdb_port, + &[ + "-Ccache=n", + "--addr=127.0.0.1:0", + "-Scli", + P2_CLI_SERVE_HELLO_WORLD_COMPONENT, + ], + Duration::from_secs(30), + )?; + + // Connect LLDB in background: just continue to start the HTTP server. + let lldb_handle = std::thread::spawn(move || lldb_with_gdbstub_script(gdb_port, "c\n")); + + // Wait for the HTTP server to start. + let line = wt.wait_for_stderr("Serving HTTP", Duration::from_secs(15))?; + let http_addr = parse_http_addr(&line)?; + eprintln!("HTTP address: {http_addr}"); + + // Send 3 requests to the same instance, verifying instance reuse. + for i in 1..=3 { + let resp = http_request(http_addr, "/")?; + eprintln!("Response {i}: {}", resp.lines().last().unwrap_or("")); + assert!( + resp.contains("Hello, WASI!"), + "request {i}: expected 'Hello, WASI!' in response, got:\n{resp}" + ); + } + + // Kill wasmtime to unblock LLDB (which is waiting for the process). + wt.child.kill().ok(); + wt.child.wait()?; + + // Collect LLDB output (it exits once the process is killed). + let lldb_output = lldb_handle.join().unwrap()?; + + // Verify LLDB connected and the process was running. + check_output( + &lldb_output, + r#" +check: stop reason +check: resuming +"#, + )?; + + Ok(()) +} + +/// Start serve under debugger, set a breakpoint on the HTTP handler, +/// send requests, verify breakpoints fire and responses are correct. +/// Tests instance reuse across multiple requests. +#[test] +#[ignore] +fn guest_debug_serve_breakpoint() -> Result<()> { + let gdb_port = free_port(); + + let mut wt = WasmtimeWithGdbstub::spawn( + "serve", + gdb_port, + &[ + "-Ccache=n", + "--addr=127.0.0.1:0", + "-Scli", + P2_CLI_SERVE_HELLO_WORLD_COMPONENT, + ], + Duration::from_secs(30), + )?; + + // LLDB script: set a breakpoint on the incoming-handler Guest::handle, + // continue to start the server, then for each request: print backtrace + // at breakpoint and continue. We do this for 3 requests. + let lldb_handle = std::thread::spawn(move || { + lldb_with_gdbstub_script( + gdb_port, + r#" +rbreak Guest.*handle +c +bt +c +bt +c +bt +c +"#, + ) + }); + + // Wait for the HTTP server to start. + let line = wt.wait_for_stderr("Serving HTTP", Duration::from_secs(15))?; + let http_addr = parse_http_addr(&line)?; + eprintln!("HTTP address: {http_addr}"); + + // Send 3 requests. Each one will hit the breakpoint, LLDB prints + // the backtrace, then continues to let the response through. + for i in 1..=3 { + let resp = http_request(http_addr, "/")?; + eprintln!("Response {i}: {}", resp.lines().last().unwrap_or("")); + assert!( + resp.contains("Hello, WASI!"), + "request {i}: expected 'Hello, WASI!' in response, got:\n{resp}" + ); + } + + // Kill wasmtime to unblock LLDB. + wt.child.kill().ok(); + wt.child.wait()?; + + let lldb_output = lldb_handle.join().unwrap()?; + + // Verify LLDB stopped at the breakpoint with the correct function + // in the backtrace, and that it happened multiple times. + check_output( + &lldb_output, + r#" +check: Guest +check: handle +check: stop reason +check: Guest +check: handle +check: stop reason +check: Guest +check: handle +"#, + )?; + + Ok(()) +} diff --git a/tests/all/iloop.rs b/tests/all/iloop.rs index 4e6e8771e6e8..4cb9747c8f03 100644 --- a/tests/all/iloop.rs +++ b/tests/all/iloop.rs @@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; use wasmtime::*; -fn interruptable_store() -> Store<()> { +fn interruptible_store() -> Store<()> { let engine = Engine::new(Config::new().epoch_interruption(true)).unwrap(); let mut store = Store::new(&engine, ()); store.set_epoch_deadline(1); @@ -27,8 +27,8 @@ fn hugely_recursive_module(engine: &Engine) -> wasmtime::Result { } #[test] -fn loops_interruptable() -> wasmtime::Result<()> { - let mut store = interruptable_store(); +fn loops_interruptible() -> wasmtime::Result<()> { + let mut store = interruptible_store(); let module = Module::new(store.engine(), r#"(func (export "loop") (loop br 0))"#)?; let instance = Instance::new(&mut store, &module, &[])?; let iloop = instance.get_typed_func::<(), ()>(&mut store, "loop")?; @@ -39,8 +39,8 @@ fn loops_interruptable() -> wasmtime::Result<()> { } #[test] -fn functions_interruptable() -> wasmtime::Result<()> { - let mut store = interruptable_store(); +fn functions_interruptible() -> wasmtime::Result<()> { + let mut store = interruptible_store(); let module = hugely_recursive_module(store.engine())?; let func = Func::wrap(&mut store, || {}); let instance = Instance::new(&mut store, &module, &[func.into()])?; @@ -60,7 +60,7 @@ fn loop_interrupt_from_afar() -> wasmtime::Result<()> { // far. static HITS: AtomicUsize = AtomicUsize::new(0); static STOP: AtomicBool = AtomicBool::new(false); - let mut store = interruptable_store(); + let mut store = interruptible_store(); let module = Module::new( store.engine(), r#" @@ -108,7 +108,7 @@ fn function_interrupt_from_afar() -> wasmtime::Result<()> { static HITS: AtomicUsize = AtomicUsize::new(0); static STOP: AtomicBool = AtomicBool::new(false); - let mut store = interruptable_store(); + let mut store = interruptible_store(); let module = hugely_recursive_module(store.engine())?; let func = Func::wrap(&mut store, || { HITS.fetch_add(1, SeqCst); diff --git a/tests/all/linker.rs b/tests/all/linker.rs index f737ae3546fc..de6614ea6044 100644 --- a/tests/all/linker.rs +++ b/tests/all/linker.rs @@ -332,8 +332,8 @@ fn alias_one() -> Result<()> { assert!(linker.alias("a", "b", "c", "d").is_err()); linker.func_wrap("a", "b", || {})?; assert!(linker.alias("a", "b", "c", "d").is_ok()); - assert!(linker.get(&mut store, "a", "b").is_some()); - assert!(linker.get(&mut store, "c", "d").is_some()); + assert!(linker.get(&mut store, "a", "b").is_ok()); + assert!(linker.get(&mut store, "c", "d").is_ok()); Ok(()) } @@ -774,3 +774,37 @@ fn linker_defines_table_subtype_err() -> Result<()> { Ok(()) } + +// Regression test for a soundness bug in StringPool::try_clone() introduced +// in wasmtime 43.0.0: cloning a StringPool (and thus a Linker) copied the +// map's &'static str keys that pointed into the *original* pool's string +// storage. After the original was dropped those keys became dangling, causing +// get_atom() on the clone to silently fail to find registered imports. +#[test] +#[cfg_attr(miri, ignore)] +fn linker_clone_drop_original_then_instantiate() -> Result<()> { + let engine = Engine::default(); + let wat = r#"(module + (import "env" "answer" (func (result i32))) + (func (export "run") (result i32) + call 0 + ) + )"#; + let module = Module::new(&engine, wat)?; + + let original = { + let mut l: Linker<()> = Linker::new(&engine); + l.func_wrap("env", "answer", || -> i32 { 42 })?; + l + }; + + let clone = original.clone(); + // Dropping the original must not invalidate the clone's string pool. + drop(original); + + let mut store = Store::new(&engine, ()); + let instance = clone.instantiate(&mut store, &module)?; + let f = instance.get_typed_func::<(), i32>(&mut store, "run")?; + assert_eq!(f.call(&mut store, ())?, 42); + Ok(()) +} diff --git a/tests/all/main.rs b/tests/all/main.rs index abbf9be0a6c8..ec61178b3478 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -11,6 +11,7 @@ mod component_model; mod coredump; mod custom_code_memory; mod debug; +mod debug_component; mod defaults; mod engine; mod epoch_interruption; @@ -23,6 +24,8 @@ mod func; mod funcref; mod gc; mod globals; +#[cfg(feature = "gdbstub")] +mod guest_debug; mod host_funcs; mod i31ref; mod iloop; diff --git a/tests/all/pooling_allocator.rs b/tests/all/pooling_allocator.rs index a02e4b0d2822..c14a6dbbc587 100644 --- a/tests/all/pooling_allocator.rs +++ b/tests/all/pooling_allocator.rs @@ -670,103 +670,111 @@ fn instance_too_large() -> Result<()> { #[cfg_attr(miri, ignore)] fn dynamic_memory_pooling_allocator() -> Result<()> { for guard_size in [0, 1 << 16] { - let max_size = 128 << 20; - let mut pool = crate::small_pool_config(); - pool.max_memory_size(max_size as usize); - let mut config = Config::new(); - config.memory_reservation(max_size); - config.memory_guard_size(guard_size); - config.allocation_strategy(pool); - - let engine = Engine::new(&config)?; - - let module = Module::new( - &engine, - r#" - (module - (memory (export "memory") 1) - - (func (export "grow") (param i32) (result i32) - local.get 0 - memory.grow) - - (func (export "size") (result i32) - memory.size) + for signals_based_traps in [false, true] { + let max_size = 128 << 20; + let mut pool = crate::small_pool_config(); + pool.max_memory_size(max_size as usize); + let mut config = Config::new(); + config.memory_reservation(max_size); + config.memory_guard_size(guard_size); + config.allocation_strategy(pool); + config.signals_based_traps(signals_based_traps); + + let engine = Engine::new(&config)?; + + let Ok(module) = Module::new( + &engine, + r#" + (module + (memory (export "memory") 1) + + (func (export "grow") (param i32) (result i32) + local.get 0 + memory.grow) + + (func (export "size") (result i32) + memory.size) + + (func (export "i32.load") (param i32) (result i32) + local.get 0 + i32.load) + + (func (export "i32.store") (param i32 i32) + local.get 0 + local.get 1 + i32.store) + + (data (i32.const 100) "x") + ) + "#, + ) else { + // Ignore invalid configurations on 32-bit which can't run with + // signals-based-traps. + assert!(cfg!(target_pointer_width = "32") && signals_based_traps); + continue; + }; - (func (export "i32.load") (param i32) (result i32) - local.get 0 - i32.load) - - (func (export "i32.store") (param i32 i32) - local.get 0 - local.get 1 - i32.store) - - (data (i32.const 100) "x") - ) - "#, - )?; - - let mut store = Store::new(&engine, ()); - let instance = Instance::new(&mut store, &module, &[])?; - - let grow = instance.get_typed_func::(&mut store, "grow")?; - let size = instance.get_typed_func::<(), u32>(&mut store, "size")?; - let i32_load = instance.get_typed_func::(&mut store, "i32.load")?; - let i32_store = instance.get_typed_func::<(u32, i32), ()>(&mut store, "i32.store")?; - let memory = instance.get_memory(&mut store, "memory").unwrap(); - - // basic length 1 tests - // assert_eq!(memory.grow(&mut store, 1)?, 0); - assert_eq!(memory.size(&store), 1); - assert_eq!(size.call(&mut store, ())?, 1); - assert_eq!(i32_load.call(&mut store, 0)?, 0); - assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'x')); - i32_store.call(&mut store, (0, 0))?; - i32_store.call(&mut store, (100, i32::from(b'y')))?; - assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'y')); - - // basic length 2 tests - let page = 64 * 1024; - assert_eq!(grow.call(&mut store, 1)?, 1); - assert_eq!(memory.size(&store), 2); - assert_eq!(size.call(&mut store, ())?, 2); - i32_store.call(&mut store, (page, 200))?; - assert_eq!(i32_load.call(&mut store, page)?, 200); - - // test writes are visible - i32_store.call(&mut store, (2, 100))?; - assert_eq!(i32_load.call(&mut store, 2)?, 100); - - // test growth can't exceed maximum - let too_many = max_size / (64 * 1024); - assert_eq!(grow.call(&mut store, too_many as u32)?, -1); - assert!(memory.grow(&mut store, too_many).is_err()); - - assert_eq!(memory.data(&store)[page as usize], 200); - - // Re-instantiate in another store. - store = Store::new(&engine, ()); - let instance = Instance::new(&mut store, &module, &[])?; - let i32_load = instance.get_typed_func::(&mut store, "i32.load")?; - let memory = instance.get_memory(&mut store, "memory").unwrap(); - - // This is out of bounds... - assert!(i32_load.call(&mut store, page).is_err()); - assert_eq!(memory.data_size(&store), page as usize); - - // ... but implementation-wise it should still be mapped memory from - // before if we don't have any guard pages. - // - // Note though that prior writes should all appear as zeros and we can't see - // data from the prior instance. - // - // Note that this part is only implemented on Linux which has - // `MADV_DONTNEED`. - if cfg!(target_os = "linux") && guard_size == 0 { - unsafe { - let ptr = memory.data_ptr(&store); - assert_eq!(*ptr.offset(page as isize), 0); + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + + let grow = instance.get_typed_func::(&mut store, "grow")?; + let size = instance.get_typed_func::<(), u32>(&mut store, "size")?; + let i32_load = instance.get_typed_func::(&mut store, "i32.load")?; + let i32_store = instance.get_typed_func::<(u32, i32), ()>(&mut store, "i32.store")?; + let memory = instance.get_memory(&mut store, "memory").unwrap(); + + // basic length 1 tests + // assert_eq!(memory.grow(&mut store, 1)?, 0); + assert_eq!(memory.size(&store), 1); + assert_eq!(size.call(&mut store, ())?, 1); + assert_eq!(i32_load.call(&mut store, 0)?, 0); + assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'x')); + i32_store.call(&mut store, (0, 0))?; + i32_store.call(&mut store, (100, i32::from(b'y')))?; + assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'y')); + + // basic length 2 tests + let page = 64 * 1024; + assert_eq!(grow.call(&mut store, 1)?, 1); + assert_eq!(memory.size(&store), 2); + assert_eq!(size.call(&mut store, ())?, 2); + i32_store.call(&mut store, (page, 200))?; + assert_eq!(i32_load.call(&mut store, page)?, 200); + + // test writes are visible + i32_store.call(&mut store, (2, 100))?; + assert_eq!(i32_load.call(&mut store, 2)?, 100); + + // test growth can't exceed maximum + let too_many = max_size / (64 * 1024); + assert_eq!(grow.call(&mut store, too_many as u32)?, -1); + assert!(memory.grow(&mut store, too_many).is_err()); + + assert_eq!(memory.data(&store)[page as usize], 200); + + // Re-instantiate in another store. + store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + let i32_load = instance.get_typed_func::(&mut store, "i32.load")?; + let memory = instance.get_memory(&mut store, "memory").unwrap(); + + // This is out of bounds... + assert!(i32_load.call(&mut store, page).is_err()); + assert_eq!(memory.data_size(&store), page as usize); + + // ... but implementation-wise it should still be mapped memory from + // before if we don't have any guard pages. + // + // Note though that prior writes should all appear as zeros and we can't see + // data from the prior instance. + // + // Note that this part is only implemented on Linux which has + // `MADV_DONTNEED`. + if cfg!(target_os = "linux") && guard_size == 0 && !signals_based_traps { + unsafe { + let ptr = memory.data_ptr(&store); + assert_eq!(*ptr.offset(page as isize), 0); + } } } } @@ -858,10 +866,11 @@ fn component_instance_size_limit() -> Result<()> { match wasmtime::component::Component::new(&engine, "(component)") { Ok(_) => panic!("should have hit limit"), - Err(e) => e.assert_contains( - "instance allocation for this component requires 64 bytes of \ - `VMComponentContext` space which exceeds the configured maximum of 1 bytes", - ), + Err(e) => { + e.assert_contains("instance allocation for this component requires 64 bytes"); + e.assert_contains("which exceeds the configured maximum of 1 bytes"); + e.assert_contains("`VMComponentContext` used 64 bytes"); + } } Ok(()) @@ -1120,6 +1129,51 @@ fn component_tables_limit() -> Result<()> { Ok(()) } +#[test] +#[cfg(feature = "component-model")] +#[cfg_attr(miri, ignore)] +fn component_core_instances_aggregate_size() -> Result<()> { + let mut pool = crate::small_pool_config(); + pool.max_core_instances_per_component(100) + // x86_64 requires 23824 bytes; we exceed this by a fair bit as there will + // be differences by arch. + .max_component_instance_size(1024); + + let mut config = Config::new(); + config.wasm_component_model(true); + config.allocation_strategy(pool); + let engine = Engine::new(&config)?; + + let core_instances = (1..100) + .map(|i| format!("(core instance $i{i} (instantiate $m))")) + .collect::>() + .join("\n"); + + match wasmtime::component::Component::new( + &engine, + format!( + " + (component + (core module $m + (func (export \"f\") (result i32) + i32.const 42 + ) + ) + {core_instances} + ) + " + ), + ) { + Ok(_) => panic!("should have hit aggregate size limit"), + Err(e) => { + e.assert_contains("instance allocation for this component requires"); + e.assert_contains("exceeds the configured maximum"); + } + } + + Ok(()) +} + #[test] #[cfg_attr(miri, ignore)] fn total_memories_limit() -> Result<()> { @@ -1354,6 +1408,49 @@ fn pagemap_scan_enabled_or_disabled() -> Result<()> { Ok(()) } +#[test] +#[cfg_attr(miri, ignore)] +fn pooling_reuse_resets() -> Result<()> { + let mut config = Config::new(); + let mut cfg = crate::small_pool_config(); + cfg.total_memories(1); + cfg.max_memory_size(0x2000000); + config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg)); + config.memory_guard_size(0); + config.memory_reservation(0x2000000); + let engine = Engine::new(&config)?; + + let a = Module::new(&engine, r#"(module (memory (export "m") 10 100))"#)?; + let b = Module::new( + &engine, + r#" +(module $B + (memory 5 100) + (func (export "read_oob") (result i32) + (i32.load (i32.const 983040)) + ) +) + "#, + )?; + + { + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &a, &[])?; + let memory = instance.get_memory(&mut store, "m").unwrap(); + memory.grow(&mut store, 10)?; + } + + { + let mut store = Store::new(&engine, ()); + let instance = Instance::new(&mut store, &b, &[])?; + let read_oob = instance.get_typed_func::<(), i32>(&mut store, "read_oob")?; + let trap: Trap = read_oob.call(&mut store, ()).unwrap_err().downcast()?; + assert_eq!(trap, Trap::MemoryOutOfBounds); + } + + Ok(()) +} + // This test instantiates a memory with an image into a slot in the pooling // allocator in a way that maps the image into the allocator but fails // instantiation. Failure here is injected with `ResourceLimiter`. Afterwards @@ -1410,3 +1507,41 @@ fn memory_reset_if_instantiation_fails() -> Result<()> { Ok(()) } + +#[test] +fn purge_module_with_mpk() -> Result<()> { + if !wasmtime::PoolingAllocationConfig::are_memory_protection_keys_available() { + println!("skipping test; mpk is not supported"); + return Ok(()); + } + + let mut pool = crate::small_pool_config(); + pool.total_memories(2) + .memory_protection_keys(Enabled::Yes) + .max_memory_protection_keys(2); + let mut config = Config::new(); + config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); + let engine = Engine::new(&config)?; + + // Create a module and instantiate it in stripe 0. + let m1 = Module::new(&engine, "(module (memory 1))")?; + let mut store = Store::new(&engine, ()); + Instance::new(&mut store, &m1, &[])?; + + // Create and instantiate a module in stripe 1. Note that the store is + // immediately destroyed here after instantiation. That should leave the + // linear memory slot available for re-instantiation. + { + let m2 = Module::new(&engine, "(module (memory 1))")?; + Instance::new(&mut Store::new(&engine, ()), &m2, &[])?; + + // ... drop `m2` here which will purge it and should remove the warm + // slot left for the module. Nothing should get corrupted... + } + + // Drop the outer store here which will clean up the rest of the + // instance/etc. + drop(store); + + Ok(()) +} diff --git a/tests/all/pulley.rs b/tests/all/pulley.rs index 151cebb24cb0..8845044111bc 100644 --- a/tests/all/pulley.rs +++ b/tests/all/pulley.rs @@ -1,5 +1,4 @@ use std::ptr::NonNull; -use std::thread; use wasmtime::Result; use wasmtime::component::{self, Component}; use wasmtime::{ @@ -147,7 +146,7 @@ fn pulley_provenance_test() -> Result<()> { .unwrap() .unwrap(); assert_eq!(caller_func.as_u32(), 3); - assert_eq!(pc, 416); + assert_eq!(pc.raw(), 418); let parent_frame = caller_frame.parent(&mut caller).unwrap(); assert!(parent_frame.is_none()); @@ -415,15 +414,6 @@ fn pulley_provenance_test_components() -> Result<()> { Ok(()) } -async fn sleep(duration: std::time::Duration) { - let (tx, rx) = tokio::sync::oneshot::channel(); - thread::spawn(move || { - thread::sleep(duration); - tx.send(()).unwrap(); - }); - rx.await.unwrap() -} - #[tokio::test] #[cfg_attr(miri, ignore)] async fn pulley_provenance_test_async_components() -> Result<()> { @@ -445,12 +435,16 @@ async fn pulley_provenance_test_async_components() -> Result<()> { { let mut store = Store::new(&engine, ()); let mut linker = component::Linker::new(&engine); - linker.root().func_wrap_concurrent("sleep", |_, ()| { - Box::pin(async { - sleep(std::time::Duration::from_millis(10)).await; - Ok(()) - }) - })?; + linker + .root() + .func_wrap_concurrent("return-slowly", |_, ()| { + Box::pin(async { + for _ in 0..5 { + tokio::task::yield_now().await; + } + Ok(()) + }) + })?; let instance = linker.instantiate_async(&mut store, &component).await?; @@ -481,6 +475,13 @@ async fn pulley_provenance_test_async_components() -> Result<()> { wasmtime::error::Ok(run.call_concurrent(accessor, ()).await?) }) .await??; + + let run = instance.get_typed_func::<(), ()>(&mut store, "intra-component-stream")?; + store + .run_concurrent(async move |accessor| { + wasmtime::error::Ok(run.call_concurrent(accessor, ()).await?) + }) + .await??; } Ok(()) diff --git a/tests/all/pulley_provenance_test_async_component.wat b/tests/all/pulley_provenance_test_async_component.wat index b5f04c7ec7bb..8eae46e80daf 100644 --- a/tests/all/pulley_provenance_test_async_component.wat +++ b/tests/all/pulley_provenance_test_async_component.wat @@ -1,5 +1,5 @@ (component - (import "sleep" (func $sleep async)) + (import "return-slowly" (func $return-slowly async)) (component $A (import "run-stackless" (func $run_stackless async)) @@ -48,7 +48,7 @@ (func (export "run-stackful") (local $ret i32) (local $set i32) - (local $status i32) + (local $status i32) (local $call i32) (local $event0 i32) (local $event1 i32) @@ -79,7 +79,8 @@ (if (i32.ne (i32.const 1 (; SUBTASK ;)) (local.get $event0)) (then unreachable)) (call $task_return) - (i32.const 0 (; EXIT ;)))) + (i32.const 0 (; EXIT ;))) + ) (core instance $i (instantiate $m (with "" (instance @@ -92,15 +93,111 @@ (export "run-stackful" (func $run_stackful)))))) (func (export "run-stackless") async (canon lift (core func $i "run-stackless") async (callback (func $i "cb")))) - (func (export "run-stackful") async (canon lift (core func $i "run-stackful") async))) + (func (export "run-stackful") async (canon lift (core func $i "run-stackful") async)) + ) (instance $a (instantiate $A - (with "run-stackless" (func $sleep)) - (with "run-stackful" (func $sleep)))) + (with "run-stackless" (func $return-slowly)) + (with "run-stackful" (func $return-slowly)))) (instance $b (instantiate $A (with "run-stackless" (func $a "run-stackless")) (with "run-stackful" (func $a "run-stackful")))) (func (export "run-stackless") (alias export $a "run-stackless")) (func (export "run-stackful") (alias export $a "run-stackful")) (func (export "run-stackless-stackless") (alias export $b "run-stackless")) - (func (export "run-stackful-stackful") (alias export $b "run-stackful"))) + (func (export "run-stackful-stackful") (alias export $b "run-stackful")) + + + ;; Test which exercises an intra-component stream read/write to ensure that + ;; there's no Miri violations while doing this. + (core module $libc (memory (export "mem") 1)) + (core instance $libc (instantiate $libc)) + (core module $ics + (import "" "stream.new" (func $stream.new (result i64))) + (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32))) + (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32))) + (import "" "stream.drop-readable" (func $stream.drop-readable (param i32))) + (import "" "stream.drop-writable" (func $stream.drop-writable (param i32))) + (import "" "waitable-set.new" (func $waitable-set.new (result i32))) + (import "" "waitable.join" (func $waitable.join (param i32 i32))) + (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32))) + (import "" "waitable-set.drop" (func $waitable-set.drop (param i32))) + (import "" "mem" (memory 1)) + + (global $r (mut i32) (i32.const 0)) + (global $w (mut i32) (i32.const 0)) + + (func (export "run") + (local $t64 i64) + + (local.set $t64 (call $stream.new)) + (global.set $r (i32.wrap_i64 (local.get $t64))) + (global.set $w (i32.wrap_i64 (i64.shr_u (local.get $t64) (i64.const 32)))) + + (call $transfer (i32.const 100) (i32.const 200) (i32.const 100)) + (call $transfer (i32.const 200) (i32.const 100) (i32.const 100)) + (call $transfer (i32.const 150) (i32.const 151) (i32.const 100)) + (call $transfer (i32.const 151) (i32.const 150) (i32.const 100)) + + (call $stream.drop-readable (global.get $r)) + (call $stream.drop-writable (global.get $w)) + ) + + (func $transfer (param $src i32) (param $dst i32) (param $len i32) + (local $ws i32) + + (call $stream.read + (global.get $r) + (local.get $dst) + (local.get $len)) + i32.const -1 ;; BLOCKED + i32.ne + if unreachable end + + (call $stream.write + (global.get $w) + (local.get $src) + (local.get $len)) + (i32.shl (local.get $len) (i32.const 4)) ;; (len << 4) | COMPLETED + ;; where COMPLETED==0 + i32.ne + if unreachable end + + ;; Reap the readable status on the `$r` handle now. + (local.set $ws (call $waitable-set.new)) + (call $waitable.join (global.get $r) (local.get $ws)) + (call $waitable-set.wait (local.get $ws) (i32.const 0)) + i32.const 2 ;; EVENT_STREAM_READ + i32.ne + if unreachable end + (call $waitable.join (global.get $r) (i32.const 0)) + (call $waitable-set.drop (local.get $ws)) + ) + ) + (type $s (stream u8)) + (core func $stream.new (canon stream.new $s)) + (core func $stream.read (canon stream.read $s async (memory $libc "mem"))) + (core func $stream.write (canon stream.write $s async (memory $libc "mem"))) + (core func $stream.drop-readable (canon stream.drop-readable $s)) + (core func $stream.drop-writable (canon stream.drop-writable $s)) + (core func $waitable-set.new (canon waitable-set.new)) + (core func $waitable.join (canon waitable.join)) + (core func $waitable-set.wait (canon waitable-set.wait (memory $libc "mem"))) + (core func $waitable-set.drop (canon waitable-set.drop)) + (core instance $ics (instantiate $ics + (with "" (instance + (export "stream.new" (func $stream.new)) + (export "stream.read" (func $stream.read)) + (export "stream.write" (func $stream.write)) + (export "stream.drop-readable" (func $stream.drop-readable)) + (export "stream.drop-writable" (func $stream.drop-writable)) + (export "mem" (memory $libc "mem")) + (export "waitable-set.new" (func $waitable-set.new)) + (export "waitable.join" (func $waitable.join)) + (export "waitable-set.wait" (func $waitable-set.wait)) + (export "waitable-set.drop" (func $waitable-set.drop)) + )) + )) + (func (export "intra-component-stream") async + (canon lift (core func $ics "run"))) +) diff --git a/tests/all/stack_overflow.rs b/tests/all/stack_overflow.rs index bd27a04c5034..1e6ececa2aa7 100644 --- a/tests/all/stack_overflow.rs +++ b/tests/all/stack_overflow.rs @@ -162,3 +162,22 @@ fn big_stack_works_ok(config: &mut Config) -> Result<()> { assert_eq!(func.call(&mut store, ())?, 0); Ok(()) } + +#[test] +fn infinite_wasm_stack_does_not_panic() -> Result<()> { + let mut config = Config::new(); + config.max_wasm_stack(usize::MAX); + config.async_stack_size(usize::MAX); + let engine = Engine::new(&config)?; + // If the store can't get allocated that's probably because we're using + // pulley and can't allocate a usize::MAX stack, ignore that failure. + // This'll still run on cranelift-native platforms. + let Ok(mut store) = Store::try_new(&engine, ()) else { + return Ok(()); + }; + let module = Module::new(store.engine(), r#"(module (func (export "f")))"#)?; + let instance = Instance::new(&mut store, &module, &[])?; + let func = instance.get_typed_func::<(), ()>(&mut store, "f")?; + func.call(&mut store, ())?; + Ok(()) +} diff --git a/tests/disas/aarch64-extr.wat b/tests/disas/aarch64-extr.wat index e2a1762e8c2b..eee20e5642a0 100644 --- a/tests/disas/aarch64-extr.wat +++ b/tests/disas/aarch64-extr.wat @@ -26,6 +26,14 @@ i32.const 11 i32.shr_u i32.or) + (func $i32_0 (param i32 i32) (result i32) + local.get 0 + i32.const 32 + i32.shl + local.get 1 + i32.const 0 + i32.shr_u + i32.or) (func $i64_21 (param i64 i64) (result i64) local.get 0 @@ -51,6 +59,14 @@ i64.const 11 i64.shr_u i64.or) + (func $i64_0 (param i64 i64) (result i64) + local.get 0 + i64.const 64 + i64.shl + local.get 1 + i64.const 0 + i64.shr_u + i64.or) ) ;; wasm[0]::function[0]::i32_21: @@ -74,23 +90,37 @@ ;; ldp x29, x30, [sp], #0x10 ;; ret ;; -;; wasm[0]::function[3]::i64_21: +;; wasm[0]::function[3]::i32_0: +;; stp x29, x30, [sp, #-0x10]! +;; mov x29, sp +;; orr w2, w4, w5 +;; ldp x29, x30, [sp], #0x10 +;; ret +;; +;; wasm[0]::function[4]::i64_21: ;; stp x29, x30, [sp, #-0x10]! ;; mov x29, sp ;; extr x2, x4, x5, #0x15 ;; ldp x29, x30, [sp], #0x10 ;; ret ;; -;; wasm[0]::function[4]::i64_21_swapped: +;; wasm[0]::function[5]::i64_21_swapped: ;; stp x29, x30, [sp, #-0x10]! ;; mov x29, sp ;; extr x2, x4, x5, #0x15 ;; ldp x29, x30, [sp], #0x10 ;; ret ;; -;; wasm[0]::function[5]::i64_11: +;; wasm[0]::function[6]::i64_11: ;; stp x29, x30, [sp, #-0x10]! ;; mov x29, sp ;; extr x2, x4, x5, #0xb ;; ldp x29, x30, [sp], #0x10 ;; ret +;; +;; wasm[0]::function[7]::i64_0: +;; stp x29, x30, [sp, #-0x10]! +;; mov x29, sp +;; orr x2, x4, x5 +;; ldp x29, x30, [sp], #0x10 +;; ret diff --git a/tests/disas/basic-wat-test.wat b/tests/disas/basic-wat-test.wat index d61b3f66a6f0..6c87259fd721 100644 --- a/tests/disas/basic-wat-test.wat +++ b/tests/disas/basic-wat-test.wat @@ -15,16 +15,16 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0021 v5 = uextend.i64 v2 -;; @0021 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0021 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0021 v7 = iadd v6, v5 ;; @0021 v8 = load.i32 little heap v7 ;; @0026 v9 = uextend.i64 v3 -;; @0026 v10 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0026 v10 = load.i64 notrap aligned readonly can_move v0+56 ;; @0026 v11 = iadd v10, v9 ;; @0026 v12 = load.i32 little heap v11 ;; @0029 v13 = iadd v8, v12 diff --git a/tests/disas/bounds-check.wat b/tests/disas/bounds-check.wat index 6405f57f26dc..e2b6df9ad78d 100644 --- a/tests/disas/bounds-check.wat +++ b/tests/disas/bounds-check.wat @@ -30,12 +30,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @002a v3 = iconst.i32 0 -;; @002c v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @002c v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @002c v4 = uextend.i64 v2 ;; @002c v6 = iadd v5, v4 ;; @002c istore8 little heap v3, v6 ; v3 = 0 diff --git a/tests/disas/debug-exceptions.wat b/tests/disas/debug-exceptions.wat index b21f539483bc..8343daafcf2f 100644 --- a/tests/disas/debug-exceptions.wat +++ b/tests/disas/debug-exceptions.wat @@ -35,28 +35,28 @@ ;; mov x0, x2 ;; stur x2, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 53, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 53, patch bytes [38, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x35, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x35, patch bytes [38, 1, 0, 148] ;; ldur x0, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 55, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 55, patch bytes [36, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x37, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x37, patch bytes [36, 1, 0, 148] ;; ldur x0, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 61, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 61, patch bytes [34, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x3d, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x3d, patch bytes [34, 1, 0, 148] ;; mov w19, #0x2a ;; stur w19, [sp, #8] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 63, slot at FP-0xc0, locals , stack I32 @ slot+0x8 -;; ╰─╼ breakpoint patch: wasm PC 63, patch bytes [31, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x3f, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x3f, patch bytes [31, 1, 0, 148] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 64, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 64, patch bytes [30, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x40, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x40, patch bytes [30, 1, 0, 148] ;; stur w19, [sp, #8] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 66, slot at FP-0xc0, locals , stack I32 @ slot+0x8 -;; ╰─╼ breakpoint patch: wasm PC 66, patch bytes [28, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x42, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x42, patch bytes [28, 1, 0, 148] ;; ldur x2, [sp, #0x10] ;; bl #0x448 ;; 84: mov x20, x2 @@ -89,6 +89,7 @@ ;; bl #0x3dc ;; ec: ldur x2, [sp, #0x10] ;; bl #0x414 +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x42, slot at FP-0xc0, locals , stack I32 @ slot+0x8 ;; f4: .byte 0x1f, 0xc1, 0x00, 0x00 ;; mov x2, x0 ;; mov w3, w2 @@ -109,21 +110,21 @@ ;; stur w0, [sp, #8] ;; ldur x0, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 72, slot at FP-0xc0, locals , stack I32 @ slot+0x8 -;; ╰─╼ breakpoint patch: wasm PC 72, patch bytes [234, 0, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x48, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x48, patch bytes [234, 0, 0, 148] ;; ldur x1, [sp, #0x10] ;; ldr x0, [x1, #0x30] ;; ldr x2, [x1, #0x40] ;; ldur x3, [sp, #0x10] ;; blr x0 -;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 72, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x4a, slot at FP-0xc0, locals , stack I32 @ slot+0x8 ;; 158: ldur x0, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 74, slot at FP-0xc0, locals , stack I32 @ slot+0x8 -;; ╰─╼ breakpoint patch: wasm PC 74, patch bytes [227, 0, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x4a, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x4a, patch bytes [227, 0, 0, 148] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 75, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 75, patch bytes [226, 0, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x4b, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x4b, patch bytes [226, 0, 0, 148] ;; add sp, sp, #0x30 ;; ldp d8, d9, [sp], #0x10 ;; ldp d10, d11, [sp], #0x10 @@ -141,6 +142,7 @@ ;; 19c: bl #0x3dc ;; 1a0: ldur x2, [sp, #0x10] ;; 1a4: bl #0x414 +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x34, slot at FP-0xc0, locals , stack ;; 1a8: .byte 0x1f, 0xc1, 0x00, 0x00 ;; 1ac: .byte 0x1f, 0xc1, 0x00, 0x00 ;; 1b0: .byte 0x1f, 0xc1, 0x00, 0x00 diff --git a/tests/disas/debug.wat b/tests/disas/debug.wat index 285b5d40e502..2a8663ea0aa8 100644 --- a/tests/disas/debug.wat +++ b/tests/disas/debug.wat @@ -22,21 +22,21 @@ ;; jb 0x62 ;; 29: movq %rdi, (%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 36, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack -;; ╰─╼ breakpoint patch: wasm PC 36, patch bytes [232, 184, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x24, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack +;; ╰─╼ breakpoint patch: wasm PC 0x24, patch bytes [232, 184, 1, 0, 0] ;; movl %edx, 0x10(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 38, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10 -;; ╰─╼ breakpoint patch: wasm PC 38, patch bytes [232, 175, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x26, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10 +;; ╰─╼ breakpoint patch: wasm PC 0x26, patch bytes [232, 175, 1, 0, 0] ;; movl %ecx, 0x14(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 40, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10, I32 @ slot+0x14 -;; ╰─╼ breakpoint patch: wasm PC 40, patch bytes [232, 166, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x28, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10, I32 @ slot+0x14 +;; ╰─╼ breakpoint patch: wasm PC 0x28, patch bytes [232, 166, 1, 0, 0] ;; leal (%rdx, %rcx), %eax ;; movl %eax, 0x10(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 41, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10 -;; ╰─╼ breakpoint patch: wasm PC 41, patch bytes [232, 154, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x29, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10 +;; ╰─╼ breakpoint patch: wasm PC 0x29, patch bytes [232, 154, 1, 0, 0] ;; movl %eax, 0x10(%rsp) ;; movq 0x20(%rsp), %r12 ;; addq $0x30, %rsp @@ -48,6 +48,7 @@ ;; 67: callq 0x18c ;; 6c: movq %r12, %rdi ;; 6f: callq 0x1bd +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x23, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack ;; 74: ud2 ;; ;; wasm[0]::array_to_wasm_trampoline[0]: diff --git a/tests/disas/duplicate-loads-dynamic-memory.wat b/tests/disas/duplicate-loads-dynamic-memory.wat index b4ee0b4c7d49..a886458b8cd0 100644 --- a/tests/disas/duplicate-loads-dynamic-memory.wat +++ b/tests/disas/duplicate-loads-dynamic-memory.wat @@ -28,12 +28,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0057 v6 = load.i64 notrap aligned v0+64 -;; @0057 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0057 v8 = load.i64 notrap aligned can_move v0+56 ;; @0057 v5 = uextend.i64 v2 ;; @0057 v7 = icmp ugt v5, v6 ;; @0057 v10 = iconst.i64 0 @@ -52,12 +52,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0064 v6 = load.i64 notrap aligned v0+64 -;; @0064 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0064 v8 = load.i64 notrap aligned can_move v0+56 ;; @0064 v5 = uextend.i64 v2 ;; @0064 v7 = icmp ugt v5, v6 ;; @0064 v12 = iconst.i64 0 diff --git a/tests/disas/duplicate-loads-static-memory.wat b/tests/disas/duplicate-loads-static-memory.wat index 063f8a98dcde..1933d2f45eaf 100644 --- a/tests/disas/duplicate-loads-static-memory.wat +++ b/tests/disas/duplicate-loads-static-memory.wat @@ -23,11 +23,11 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): -;; @0057 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0057 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0057 v5 = uextend.i64 v2 ;; @0057 v7 = iadd v6, v5 ;; @0057 v8 = load.i32 little heap v7 @@ -43,11 +43,11 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): -;; @0064 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0064 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0064 v5 = uextend.i64 v2 ;; @0064 v7 = iadd v6, v5 ;; @0064 v8 = iconst.i64 1234 diff --git a/tests/disas/dynamic-memory-no-spectre-access-same-index-different-offsets.wat b/tests/disas/dynamic-memory-no-spectre-access-same-index-different-offsets.wat index 3eade3d3ef62..52b5db8c4940 100644 --- a/tests/disas/dynamic-memory-no-spectre-access-same-index-different-offsets.wat +++ b/tests/disas/dynamic-memory-no-spectre-access-same-index-different-offsets.wat @@ -41,7 +41,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -49,7 +49,7 @@ ;; @0047 v6 = uextend.i64 v2 ;; @0047 v8 = icmp ugt v6, v7 ;; @0047 trapnz v8, heap_oob -;; @0047 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0047 v9 = load.i64 notrap aligned can_move v0+56 ;; @0047 v10 = iadd v9, v6 ;; @0047 v11 = load.i32 little heap v10 ;; @004c v17 = iconst.i64 4 @@ -74,7 +74,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32, v4: i32, v5: i32): @@ -82,7 +82,7 @@ ;; @005d v6 = uextend.i64 v2 ;; @005d v8 = icmp ugt v6, v7 ;; @005d trapnz v8, heap_oob -;; @005d v9 = load.i64 notrap aligned can_move checked v0+56 +;; @005d v9 = load.i64 notrap aligned can_move v0+56 ;; @005d v10 = iadd v9, v6 ;; @005d store little heap v3, v10 ;; @0064 v16 = iconst.i64 4 diff --git a/tests/disas/dynamic-memory-yes-spectre-access-same-index-different-offsets.wat b/tests/disas/dynamic-memory-yes-spectre-access-same-index-different-offsets.wat index 8e673587a7ff..be1d1aefb049 100644 --- a/tests/disas/dynamic-memory-yes-spectre-access-same-index-different-offsets.wat +++ b/tests/disas/dynamic-memory-yes-spectre-access-same-index-different-offsets.wat @@ -37,12 +37,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0047 v7 = load.i64 notrap aligned v0+64 -;; @0047 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0047 v9 = load.i64 notrap aligned can_move v0+56 ;; @0047 v6 = uextend.i64 v2 ;; @0047 v8 = icmp ugt v6, v7 ;; @0047 v11 = iconst.i64 0 @@ -72,12 +72,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32, v4: i32, v5: i32): ;; @005d v7 = load.i64 notrap aligned v0+64 -;; @005d v9 = load.i64 notrap aligned can_move checked v0+56 +;; @005d v9 = load.i64 notrap aligned can_move v0+56 ;; @005d v6 = uextend.i64 v2 ;; @005d v8 = icmp ugt v6, v7 ;; @005d v11 = iconst.i64 0 diff --git a/tests/disas/f32-load.wat b/tests/disas/f32-load.wat index 6f96bdd69a78..3ed84126a073 100644 --- a/tests/disas/f32-load.wat +++ b/tests/disas/f32-load.wat @@ -12,12 +12,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @002e v4 = uextend.i64 v2 -;; @002e v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @002e v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @002e v6 = iadd v5, v4 ;; @002e v7 = load.f32 little heap v6 ;; @0031 jump block1 diff --git a/tests/disas/f32-store.wat b/tests/disas/f32-store.wat index f3b643350235..858b3fc30956 100644 --- a/tests/disas/f32-store.wat +++ b/tests/disas/f32-store.wat @@ -15,12 +15,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: f32): ;; @0031 v4 = uextend.i64 v2 -;; @0031 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0031 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0031 v6 = iadd v5, v4 ;; @0031 store little heap v3, v6 ;; @0034 jump block1 diff --git a/tests/disas/f64-load.wat b/tests/disas/f64-load.wat index 0992356689d1..e0930d0663a1 100644 --- a/tests/disas/f64-load.wat +++ b/tests/disas/f64-load.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @002e v4 = uextend.i64 v2 -;; @002e v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @002e v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @002e v6 = iadd v5, v4 ;; @002e v7 = load.f64 little heap v6 ;; @0031 jump block1 diff --git a/tests/disas/f64-store.wat b/tests/disas/f64-store.wat index 4caf5489ef27..682dea575b50 100644 --- a/tests/disas/f64-store.wat +++ b/tests/disas/f64-store.wat @@ -15,12 +15,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: f64): ;; @0031 v4 = uextend.i64 v2 -;; @0031 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0031 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0031 v6 = iadd v5, v4 ;; @0031 store little heap v3, v6 ;; @0034 jump block1 diff --git a/tests/disas/fibonacci.wat b/tests/disas/fibonacci.wat index 78c3214ff63a..04ae1e33b1de 100644 --- a/tests/disas/fibonacci.wat +++ b/tests/disas/fibonacci.wat @@ -29,7 +29,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64): @@ -55,7 +55,7 @@ ;; block2: ;; @0056 v16 = iconst.i32 0 ;; @005a v17 = uextend.i64 v16 ; v16 = 0 -;; @005a v18 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @005a v18 = load.i64 notrap aligned readonly can_move v0+56 ;; @005a v19 = iadd v18, v17 ;; @005a store.i32 little heap v11, v19 ;; @005d jump block1 diff --git a/tests/disas/fixed-size-memory.wat b/tests/disas/fixed-size-memory.wat index 4dc7cac8c45c..c437231b5016 100644 --- a/tests/disas/fixed-size-memory.wat +++ b/tests/disas/fixed-size-memory.wat @@ -26,7 +26,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -34,7 +34,7 @@ ;; @0041 v5 = iconst.i64 0x0001_0000 ;; @0041 v6 = icmp uge v4, v5 ; v5 = 0x0001_0000 ;; @0041 trapnz v6, heap_oob -;; @0041 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0041 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0041 v8 = iadd v7, v4 ;; @0041 istore8 little heap v3, v8 ;; @0044 jump block1 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @0049 v5 = iconst.i64 0x0001_0000 ;; @0049 v6 = icmp uge v4, v5 ; v5 = 0x0001_0000 ;; @0049 trapnz v6, heap_oob -;; @0049 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = uload8.i32 little heap v8 ;; @004c jump block1 diff --git a/tests/disas/gc/issue-11753.wat b/tests/disas/gc/issue-11753.wat new file mode 100644 index 000000000000..0690f3670283 --- /dev/null +++ b/tests/disas/gc/issue-11753.wat @@ -0,0 +1,79 @@ +;;! target = "x86_64" +;;! flags = "-W gc,exceptions" +;;! test = "compile" +;;! objdump = "--stack-maps=true" + +(module + (type $s (struct (field (mut i32)))) + + (import "" "gc" (func $gc)) + + (func (export "run") (result i32) + (struct.new $s (i32.const 42)) + + block $b + try_table (catch_all $b) + ;; This should have both exception handlers and stack maps in the + ;; disassembly below. + call $gc + end + end + + struct.get $s 0 + ) +) +;; wasm[0]::function[1]: +;; pushq %rbp +;; movq %rsp, %rbp +;; movq 8(%rdi), %r10 +;; movq 0x18(%r10), %r10 +;; addq $0x60, %r10 +;; cmpq %rsp, %r10 +;; ja 0xbe +;; 19: subq $0x50, %rsp +;; movq %rbx, 0x20(%rsp) +;; movq %r12, 0x28(%rsp) +;; movq %r13, 0x30(%rsp) +;; movq %r14, 0x38(%rsp) +;; movq %r15, 0x40(%rsp) +;; movq %rdi, 8(%rsp) +;; movl $0xb0000000, %esi +;; xorl %edx, %edx +;; movl $0x20, %ecx +;; movl $8, %r8d +;; movq 8(%rsp), %rbx +;; movq %rbx, %rdi +;; callq 0x215 +;; movl %eax, (%rsp) +;; movq 8(%rbx), %rcx +;; movq 0x20(%rcx), %rcx +;; movl %eax, %eax +;; movl $0x2a, 0x18(%rcx, %rax) +;; movq %rcx, 0x10(%rsp) +;; movq 0x30(%rbx), %rax +;; movq 0x40(%rbx), %rdi +;; movq %rbx, %rsi +;; movq %rbx, 8(%rsp) +;; callq *%rax +;; ├─╼ exception frame offset: SP = FP - 0x50 +;; ╰─╼ exception handler: default handler, context at [SP+0x8], handler=0x86 +;; movl (%rsp), %eax +;; ╰─╼ stack_map: frame_size=80, frame_offsets=[0] +;; testl %eax, %eax +;; je 0xc0 +;; 91: movl %eax, %eax +;; movq 0x10(%rsp), %rcx +;; movl 0x18(%rcx, %rax), %eax +;; movq 0x20(%rsp), %rbx +;; movq 0x28(%rsp), %r12 +;; movq 0x30(%rsp), %r13 +;; movq 0x38(%rsp), %r14 +;; movq 0x40(%rsp), %r15 +;; addq $0x50, %rsp +;; movq %rbp, %rsp +;; popq %rbp +;; retq +;; be: ud2 +;; ╰─╼ trap: StackOverflow +;; c0: ud2 +;; ╰─╼ trap: NullReference diff --git a/tests/disas/gc/typed-select-and-stack-maps.wat b/tests/disas/gc/typed-select-and-stack-maps.wat new file mode 100644 index 000000000000..9eaf0158bf47 --- /dev/null +++ b/tests/disas/gc/typed-select-and-stack-maps.wat @@ -0,0 +1,90 @@ +;;! target = "x86_64" +;;! flags = "-W function-references,gc" +;;! test = "optimize" + +(module + (import "" "observe" (func $observe (param anyref))) + (import "" "safepoint" (func $safepoint)) + + (func (param structref i31ref i32) + ;; Select from two types, one of which requires inclusion in stack maps, + ;; resulting in a value that also requires inclusion in stack maps. + (select (result anyref) + (local.get 0) + (local.get 1) + (local.get 2)) + + ;; Make a call, which is a safepoint and has stack maps. + (call $safepoint) + + ;; Observe the result of the select to keep it alive across the call. + call $observe + ) + + (func (param i31ref i31ref i32) + ;; Select from two types that do not require inclusion in stack maps, + ;; resulting in one that (normally) does. In this case, however, we + ;; shouldn't include the value in a stack map, because we know that the + ;; anyref cannot be an instance of a subtype that actually does require + ;; inclusion in stack maps. + (select (result anyref) + (local.get 0) + (local.get 1) + (local.get 2)) + + ;; Make a call, which is a safepoint and has stack maps. + (call $safepoint) + + ;; Observe the result of the select to keep it alive across the call. + call $observe + ) +) +;; function u0:0(i64 vmctx, i64, i32, i32, i32) tail { +;; ss0 = explicit_slot 4, align = 4 +;; gv0 = vmctx +;; gv1 = load.i64 notrap aligned readonly gv0+8 +;; gv2 = load.i64 notrap aligned gv1+24 +;; gv3 = vmctx +;; sig0 = (i64 vmctx, i64) tail +;; sig1 = (i64 vmctx, i64, i32) tail +;; stack_limit = gv2 +;; +;; block0(v0: i64, v1: i64, v2: i32, v3: i32, v4: i32): +;; @0049 v5 = select v4, v2, v3 +;; v14 = stack_addr.i64 ss0 +;; store notrap v5, v14 +;; @004c v8 = load.i64 notrap aligned readonly can_move v0+72 +;; @004c v7 = load.i64 notrap aligned readonly can_move v0+88 +;; @004c call_indirect sig0, v8(v7, v0), stack_map=[i32 @ ss0+0] +;; v12 = load.i32 notrap v14 +;; @004e v11 = load.i64 notrap aligned readonly can_move v0+48 +;; @004e v10 = load.i64 notrap aligned readonly can_move v0+64 +;; @004e call_indirect sig1, v11(v10, v0, v12) +;; @0050 jump block1 +;; +;; block1: +;; @0050 return +;; } +;; +;; function u0:1(i64 vmctx, i64, i32, i32, i32) tail { +;; gv0 = vmctx +;; gv1 = load.i64 notrap aligned readonly gv0+8 +;; gv2 = load.i64 notrap aligned gv1+24 +;; gv3 = vmctx +;; sig0 = (i64 vmctx, i64) tail +;; sig1 = (i64 vmctx, i64, i32) tail +;; stack_limit = gv2 +;; +;; block0(v0: i64, v1: i64, v2: i32, v3: i32, v4: i32): +;; @005c v8 = load.i64 notrap aligned readonly can_move v0+72 +;; @005c v7 = load.i64 notrap aligned readonly can_move v0+88 +;; @005c call_indirect sig0, v8(v7, v0) +;; @005e v11 = load.i64 notrap aligned readonly can_move v0+48 +;; @005e v10 = load.i64 notrap aligned readonly can_move v0+64 +;; @0059 v5 = select v4, v2, v3 +;; @005e call_indirect sig1, v11(v10, v0, v5) +;; @0060 jump block1 +;; +;; block1: +;; @0060 return +;; } diff --git a/tests/disas/globals.wat b/tests/disas/globals.wat index 447e0d409a59..003c8f51b2bb 100644 --- a/tests/disas/globals.wat +++ b/tests/disas/globals.wat @@ -15,7 +15,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64): @@ -23,7 +23,7 @@ ;; @0029 v3 = iconst.i32 0 ;; @002b v5 = load.i32 notrap aligned table v0+80 ;; @002d v6 = uextend.i64 v3 ; v3 = 0 -;; @002d v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @002d v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @002d v8 = iadd v7, v6 ;; @002d store little heap v5, v8 ;; @0030 jump block1 diff --git a/tests/disas/i32-load.wat b/tests/disas/i32-load.wat index 38aa47760553..77a404f5be15 100644 --- a/tests/disas/i32-load.wat +++ b/tests/disas/i32-load.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @002e v4 = uextend.i64 v2 -;; @002e v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @002e v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @002e v6 = iadd v5, v4 ;; @002e v7 = load.i32 little heap v6 ;; @0031 jump block1 diff --git a/tests/disas/i32-load16-s.wat b/tests/disas/i32-load16-s.wat index f25f29e259f4..9a5eee03bcbc 100644 --- a/tests/disas/i32-load16-s.wat +++ b/tests/disas/i32-load16-s.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0032 v4 = uextend.i64 v2 -;; @0032 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0032 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0032 v6 = iadd v5, v4 ;; @0032 v7 = sload16.i32 little heap v6 ;; @0035 jump block1 diff --git a/tests/disas/i32-load16-u.wat b/tests/disas/i32-load16-u.wat index 740bd34d9178..6f715dd41471 100644 --- a/tests/disas/i32-load16-u.wat +++ b/tests/disas/i32-load16-u.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0032 v4 = uextend.i64 v2 -;; @0032 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0032 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0032 v6 = iadd v5, v4 ;; @0032 v7 = uload16.i32 little heap v6 ;; @0035 jump block1 diff --git a/tests/disas/i32-load8-s.wat b/tests/disas/i32-load8-s.wat index a6430c10d7eb..3d62165bf76e 100644 --- a/tests/disas/i32-load8-s.wat +++ b/tests/disas/i32-load8-s.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0031 v4 = uextend.i64 v2 -;; @0031 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0031 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0031 v6 = iadd v5, v4 ;; @0031 v7 = sload8.i32 little heap v6 ;; @0034 jump block1 diff --git a/tests/disas/i32-load8-u.wat b/tests/disas/i32-load8-u.wat index 959250f53379..67548ca5090c 100644 --- a/tests/disas/i32-load8-u.wat +++ b/tests/disas/i32-load8-u.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0031 v4 = uextend.i64 v2 -;; @0031 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0031 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0031 v6 = iadd v5, v4 ;; @0031 v7 = uload8.i32 little heap v6 ;; @0034 jump block1 diff --git a/tests/disas/i32-store.wat b/tests/disas/i32-store.wat index dcb8f1b736d0..bb44dfc2bf70 100644 --- a/tests/disas/i32-store.wat +++ b/tests/disas/i32-store.wat @@ -15,12 +15,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0031 v4 = uextend.i64 v2 -;; @0031 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0031 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0031 v6 = iadd v5, v4 ;; @0031 store little heap v3, v6 ;; @0034 jump block1 diff --git a/tests/disas/i32-store16.wat b/tests/disas/i32-store16.wat index 8de09bffac0a..20db521ac12c 100644 --- a/tests/disas/i32-store16.wat +++ b/tests/disas/i32-store16.wat @@ -15,12 +15,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0033 v4 = uextend.i64 v2 -;; @0033 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0033 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0033 v6 = iadd v5, v4 ;; @0033 istore16 little heap v3, v6 ;; @0036 jump block1 diff --git a/tests/disas/i32-store8.wat b/tests/disas/i32-store8.wat index 4bbe0f2bc80f..0bd3c4dd182c 100644 --- a/tests/disas/i32-store8.wat +++ b/tests/disas/i32-store8.wat @@ -15,12 +15,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0032 v4 = uextend.i64 v2 -;; @0032 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0032 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0032 v6 = iadd v5, v4 ;; @0032 istore8 little heap v3, v6 ;; @0035 jump block1 diff --git a/tests/disas/i64-load.wat b/tests/disas/i64-load.wat index 564ec9a2b780..d392e76b1521 100644 --- a/tests/disas/i64-load.wat +++ b/tests/disas/i64-load.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @002e v4 = uextend.i64 v2 -;; @002e v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @002e v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @002e v6 = iadd v5, v4 ;; @002e v7 = load.i64 little heap v6 ;; @0031 jump block1 diff --git a/tests/disas/i64-load16-s.wat b/tests/disas/i64-load16-s.wat index f62bcdee2fcb..928209697308 100644 --- a/tests/disas/i64-load16-s.wat +++ b/tests/disas/i64-load16-s.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0032 v4 = uextend.i64 v2 -;; @0032 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0032 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0032 v6 = iadd v5, v4 ;; @0032 v7 = sload16.i64 little heap v6 ;; @0035 jump block1 diff --git a/tests/disas/i64-load16-u.wat b/tests/disas/i64-load16-u.wat index 15565385dcdc..359663affd82 100644 --- a/tests/disas/i64-load16-u.wat +++ b/tests/disas/i64-load16-u.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0032 v4 = uextend.i64 v2 -;; @0032 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0032 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0032 v6 = iadd v5, v4 ;; @0032 v7 = uload16.i64 little heap v6 ;; @0035 jump block1 diff --git a/tests/disas/i64-load8-s.wat b/tests/disas/i64-load8-s.wat index 4258fb7f58b2..8e8f0f402e40 100644 --- a/tests/disas/i64-load8-s.wat +++ b/tests/disas/i64-load8-s.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0031 v4 = uextend.i64 v2 -;; @0031 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0031 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0031 v6 = iadd v5, v4 ;; @0031 v7 = sload8.i64 little heap v6 ;; @0034 jump block1 diff --git a/tests/disas/i64-load8-u.wat b/tests/disas/i64-load8-u.wat index 3aa49760f826..05293f228035 100644 --- a/tests/disas/i64-load8-u.wat +++ b/tests/disas/i64-load8-u.wat @@ -14,12 +14,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0031 v4 = uextend.i64 v2 -;; @0031 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0031 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0031 v6 = iadd v5, v4 ;; @0031 v7 = uload8.i64 little heap v6 ;; @0034 jump block1 diff --git a/tests/disas/i64-store.wat b/tests/disas/i64-store.wat index a55b9b33d0f2..d001e38e191a 100644 --- a/tests/disas/i64-store.wat +++ b/tests/disas/i64-store.wat @@ -15,12 +15,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i64): ;; @0031 v4 = uextend.i64 v2 -;; @0031 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0031 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0031 v6 = iadd v5, v4 ;; @0031 store little heap v3, v6 ;; @0034 jump block1 diff --git a/tests/disas/i64-store16.wat b/tests/disas/i64-store16.wat index fba65aed7de6..cb6b8aca0e0d 100644 --- a/tests/disas/i64-store16.wat +++ b/tests/disas/i64-store16.wat @@ -15,12 +15,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i64): ;; @0033 v4 = uextend.i64 v2 -;; @0033 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0033 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0033 v6 = iadd v5, v4 ;; @0033 istore16 little heap v3, v6 ;; @0036 jump block1 diff --git a/tests/disas/i64-store32.wat b/tests/disas/i64-store32.wat index ac5f28cbfd91..749aabbacaa0 100644 --- a/tests/disas/i64-store32.wat +++ b/tests/disas/i64-store32.wat @@ -15,12 +15,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i64): ;; @0033 v4 = uextend.i64 v2 -;; @0033 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0033 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0033 v6 = iadd v5, v4 ;; @0033 istore32 little heap v3, v6 ;; @0036 jump block1 diff --git a/tests/disas/i64-store8.wat b/tests/disas/i64-store8.wat index 756086216b9e..9e263ae74804 100644 --- a/tests/disas/i64-store8.wat +++ b/tests/disas/i64-store8.wat @@ -15,12 +15,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i64): ;; @0032 v4 = uextend.i64 v2 -;; @0032 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0032 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0032 v6 = iadd v5, v4 ;; @0032 istore8 little heap v3, v6 ;; @0035 jump block1 diff --git a/tests/disas/if-unreachable-else-params-2.wat b/tests/disas/if-unreachable-else-params-2.wat index 9ea6e9468b03..407a59765c99 100644 --- a/tests/disas/if-unreachable-else-params-2.wat +++ b/tests/disas/if-unreachable-else-params-2.wat @@ -25,7 +25,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -35,7 +35,7 @@ ;; ;; block2: ;; @0058 v7 = uextend.i64 v2 -;; @0058 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0058 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0058 v9 = iadd v8, v7 ;; @0058 v10 = sload16.i64 little heap v9 ;; @005c jump block3 diff --git a/tests/disas/if-unreachable-else-params.wat b/tests/disas/if-unreachable-else-params.wat index 454cbf89b8b4..970876e6ea73 100644 --- a/tests/disas/if-unreachable-else-params.wat +++ b/tests/disas/if-unreachable-else-params.wat @@ -48,7 +48,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -60,7 +60,7 @@ ;; ;; block4: ;; @004b v7 = uextend.i64 v3 ; v3 = 35 -;; @004b v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004b v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @004b v9 = iadd v8, v7 ;; @004b v10 = sload16.i64 little heap v9 ;; @004e trap user11 diff --git a/tests/disas/issue-12808.wat b/tests/disas/issue-12808.wat new file mode 100644 index 000000000000..04e3e417a3ee --- /dev/null +++ b/tests/disas/issue-12808.wat @@ -0,0 +1,76 @@ +;;! target = "x86_64" +;;! test = "compile" +;;! flags = ["-Dguest-debug=yes", "-Wthreads=yes"] + +(module + (memory 1 1 shared) + (func (result i32 i32) + i32.const 0 + i32.const 0 + i32.const 0 + i32.const 0 + i32.atomic.rmw.cmpxchg)) +;; wasm[0]::function[0]: +;; pushq %rbp +;; movq %rsp, %rbp +;; subq $0x30, %rsp +;; movq %rbx, 0x20(%rsp) +;; movq 8(%rdi), %rax +;; movq 0x18(%rax), %rax +;; movq %rsp, %rcx +;; cmpq %rax, %rcx +;; jb 0xb4 +;; 21: movq %rdi, (%rsp) +;; nopl (%rax, %rax) +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x1f, slot at FP-0x30, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x1f, patch bytes [232, 4, 2, 0, 0] +;; movl $0, 8(%rsp) +;; nopl (%rax, %rax) +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x21, slot at FP-0x30, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x21, patch bytes [232, 247, 1, 0, 0] +;; movl $0, 0xc(%rsp) +;; nopl (%rax, %rax) +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x23, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc +;; ╰─╼ breakpoint patch: wasm PC 0x23, patch bytes [232, 234, 1, 0, 0] +;; movl $0, 0x10(%rsp) +;; nopl (%rax, %rax) +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x25, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc, I32 @ slot+0x10 +;; ╰─╼ breakpoint patch: wasm PC 0x25, patch bytes [232, 221, 1, 0, 0] +;; movl $0, 0x14(%rsp) +;; nopl (%rax, %rax) +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x27, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc, I32 @ slot+0x10, I32 @ slot+0x14 +;; ╰─╼ breakpoint patch: wasm PC 0x27, patch bytes [232, 208, 1, 0, 0] +;; xorl %eax, %eax +;; testb %al, %al +;; jne 0x9d +;; 68: movq 0x30(%rdi), %rax +;; movq (%rax), %rcx +;; xorl %eax, %eax +;; lock cmpxchgl %eax, (%rcx) +;; movl %eax, 0xc(%rsp) +;; movq %rax, %rcx +;; nopl (%rax, %rax) +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x2b, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc +;; ╰─╼ breakpoint patch: wasm PC 0x2b, patch bytes [232, 173, 1, 0, 0] +;; xorl %eax, %eax +;; movl $0, 8(%rsp) +;; movl %ecx, 0xc(%rsp) +;; movq 0x20(%rsp), %rbx +;; addq $0x30, %rsp +;; movq %rbp, %rsp +;; popq %rbp +;; retq +;; 9d: movq %rdi, %rbx +;; a0: movl $2, %esi +;; a5: callq 0x1d0 +;; aa: movq %rbx, %rdi +;; ad: callq 0x201 +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x27, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc +;; b2: ud2 +;; b4: movq %rdi, %rbx +;; b7: xorl %esi, %esi +;; b9: callq 0x1d0 +;; be: movq %rbx, %rdi +;; c1: callq 0x201 +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x1e, slot at FP-0x30, locals , stack +;; c6: ud2 diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat index 7736a2d45a9b..65bd508f8c08 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -29,7 +29,6 @@ ;; add x10, x10, w4, uxtw ;; cmp x11, x12 ;; csel x11, x13, x10, hi -;; csdb ;; str w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; add x10, x10, w4, uxtw ;; cmp x11, x12 ;; csel x11, x13, x10, hi -;; csdb ;; ldr w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index 6e03ce14ab73..b70e3416d8b0 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -31,7 +31,6 @@ ;; add x13, x13, #1, lsl #12 ;; cmp x12, x14 ;; csel x13, x15, x13, hi -;; csdb ;; str w5, [x13] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -49,7 +48,6 @@ ;; add x13, x13, #1, lsl #12 ;; cmp x12, x14 ;; csel x13, x15, x13, hi -;; csdb ;; ldr w2, [x13] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 24858e5c0bfd..ee47f4d59fdd 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; mov w13, w4 ;; mov w14, #-0xfffc ;; adds x13, x13, x14 -;; b.hs #0x48 +;; b.hs #0x44 ;; 18: ldr x14, [x2, #0x40] ;; ldr x0, [x2, #0x38] ;; mov x15, #0 @@ -33,11 +33,10 @@ ;; add x0, x0, x1 ;; cmp x13, x14 ;; csel x15, x15, x0, hi -;; csdb ;; str w5, [x15] ;; ldp x29, x30, [sp], #0x10 ;; ret -;; 48: .byte 0x1f, 0xc1, 0x00, 0x00 +;; 44: .byte 0x1f, 0xc1, 0x00, 0x00 ;; ;; wasm[0]::function[1]: ;; stp x29, x30, [sp, #-0x10]! @@ -45,7 +44,7 @@ ;; mov w13, w4 ;; mov w14, #-0xfffc ;; adds x13, x13, x14 -;; b.hs #0xa8 +;; b.hs #0xa4 ;; 78: ldr x14, [x2, #0x40] ;; ldr x0, [x2, #0x38] ;; mov x15, #0 @@ -54,8 +53,7 @@ ;; add x0, x0, x1 ;; cmp x13, x14 ;; csel x15, x15, x0, hi -;; csdb ;; ldr w2, [x15] ;; ldp x29, x30, [sp], #0x10 ;; ret -;; a8: .byte 0x1f, 0xc1, 0x00, 0x00 +;; a4: .byte 0x1f, 0xc1, 0x00, 0x00 diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat index b667159bcdbc..17f3f1c07470 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -28,7 +28,6 @@ ;; add x10, x10, w4, uxtw ;; cmp x11, x9 ;; csel x10, x12, x10, hs -;; csdb ;; strb w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; add x10, x10, w4, uxtw ;; cmp x11, x9 ;; csel x10, x12, x10, hs -;; csdb ;; ldrb w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index a57dbb5a06db..f141fa08f4bf 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -31,7 +31,6 @@ ;; add x13, x13, #1, lsl #12 ;; cmp x12, x14 ;; csel x13, x15, x13, hi -;; csdb ;; strb w5, [x13] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -49,7 +48,6 @@ ;; add x13, x13, #1, lsl #12 ;; cmp x12, x14 ;; csel x13, x15, x13, hi -;; csdb ;; ldrb w2, [x13] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index bb8679d71580..314b3796f4d4 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; mov w13, w4 ;; mov w14, #-0xffff ;; adds x13, x13, x14 -;; b.hs #0x48 +;; b.hs #0x44 ;; 18: ldr x14, [x2, #0x40] ;; ldr x0, [x2, #0x38] ;; mov x15, #0 @@ -33,11 +33,10 @@ ;; add x0, x0, x1 ;; cmp x13, x14 ;; csel x15, x15, x0, hi -;; csdb ;; strb w5, [x15] ;; ldp x29, x30, [sp], #0x10 ;; ret -;; 48: .byte 0x1f, 0xc1, 0x00, 0x00 +;; 44: .byte 0x1f, 0xc1, 0x00, 0x00 ;; ;; wasm[0]::function[1]: ;; stp x29, x30, [sp, #-0x10]! @@ -45,7 +44,7 @@ ;; mov w13, w4 ;; mov w14, #-0xffff ;; adds x13, x13, x14 -;; b.hs #0xa8 +;; b.hs #0xa4 ;; 78: ldr x14, [x2, #0x40] ;; ldr x0, [x2, #0x38] ;; mov x15, #0 @@ -54,8 +53,7 @@ ;; add x0, x0, x1 ;; cmp x13, x14 ;; csel x15, x15, x0, hi -;; csdb ;; ldrb w2, [x15] ;; ldp x29, x30, [sp], #0x10 ;; ret -;; a8: .byte 0x1f, 0xc1, 0x00, 0x00 +;; a4: .byte 0x1f, 0xc1, 0x00, 0x00 diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index 56047348c579..0d65b1b28a58 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -28,7 +28,6 @@ ;; add x10, x10, w4, uxtw ;; cmp x11, x9 ;; csel x10, x12, x10, hi -;; csdb ;; str w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; add x10, x10, w4, uxtw ;; cmp x11, x9 ;; csel x10, x12, x10, hi -;; csdb ;; ldr w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index 3c8effba97d5..75b0cb0fd353 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -29,7 +29,6 @@ ;; add x13, x13, #1, lsl #12 ;; cmp x11, x10 ;; csel x11, x12, x13, hi -;; csdb ;; str w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; add x13, x13, #1, lsl #12 ;; cmp x11, x10 ;; csel x11, x12, x13, hi -;; csdb ;; ldr w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 004f6727255c..41da5bd96b0f 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -30,7 +30,6 @@ ;; add x14, x14, x15 ;; cmp x12, x11 ;; csel x12, x13, x14, hi -;; csdb ;; str w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -47,7 +46,6 @@ ;; add x14, x14, x15 ;; cmp x12, x11 ;; csel x12, x13, x14, hi -;; csdb ;; ldr w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index 51c21fb13605..b9546ed02d78 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -28,7 +28,6 @@ ;; add x10, x10, w4, uxtw ;; cmp x11, x9 ;; csel x10, x12, x10, hs -;; csdb ;; strb w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; add x10, x10, w4, uxtw ;; cmp x11, x9 ;; csel x10, x12, x10, hs -;; csdb ;; ldrb w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index 0bef14520399..375341e89818 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -29,7 +29,6 @@ ;; add x13, x13, #1, lsl #12 ;; cmp x11, x10 ;; csel x11, x12, x13, hi -;; csdb ;; strb w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; add x13, x13, #1, lsl #12 ;; cmp x11, x10 ;; csel x11, x12, x13, hi -;; csdb ;; ldrb w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 03997b946f5f..a82d8d3a1ea2 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -30,7 +30,6 @@ ;; add x14, x14, x15 ;; cmp x12, x11 ;; csel x12, x13, x14, hi -;; csdb ;; strb w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -47,7 +46,6 @@ ;; add x14, x14, x15 ;; cmp x12, x11 ;; csel x12, x13, x14, hi -;; csdb ;; ldrb w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat index af827a3c91e6..5773dcb877f2 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -28,7 +28,6 @@ ;; add x9, x9, x4 ;; cmp x4, x10 ;; csel x10, x11, x9, hi -;; csdb ;; str w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; add x9, x9, x4 ;; cmp x4, x10 ;; csel x10, x11, x9, hi -;; csdb ;; ldr w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index b6d0059abf33..4fb641a58e0a 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -30,7 +30,6 @@ ;; add x11, x11, #1, lsl #12 ;; cmp x4, x12 ;; csel x12, x13, x11, hi -;; csdb ;; str w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -47,7 +46,6 @@ ;; add x11, x11, #1, lsl #12 ;; cmp x4, x12 ;; csel x12, x13, x11, hi -;; csdb ;; ldr w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index b68ac42f36c5..2478ba87f3a3 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -23,7 +23,7 @@ ;; mov x29, sp ;; mov w12, #-0xfffc ;; adds x12, x4, x12 -;; b.hs #0x44 +;; b.hs #0x40 ;; 14: ldr x13, [x2, #0x40] ;; ldr x15, [x2, #0x38] ;; mov x14, #0 @@ -32,18 +32,17 @@ ;; add x15, x15, x0 ;; cmp x12, x13 ;; csel x14, x14, x15, hi -;; csdb ;; str w5, [x14] ;; ldp x29, x30, [sp], #0x10 ;; ret -;; 44: .byte 0x1f, 0xc1, 0x00, 0x00 +;; 40: .byte 0x1f, 0xc1, 0x00, 0x00 ;; ;; wasm[0]::function[1]: ;; stp x29, x30, [sp, #-0x10]! ;; mov x29, sp ;; mov w12, #-0xfffc ;; adds x12, x4, x12 -;; b.hs #0xa4 +;; b.hs #0xa0 ;; 74: ldr x13, [x2, #0x40] ;; ldr x15, [x2, #0x38] ;; mov x14, #0 @@ -52,8 +51,7 @@ ;; add x15, x15, x0 ;; cmp x12, x13 ;; csel x14, x14, x15, hi -;; csdb ;; ldr w2, [x14] ;; ldp x29, x30, [sp], #0x10 ;; ret -;; a4: .byte 0x1f, 0xc1, 0x00, 0x00 +;; a0: .byte 0x1f, 0xc1, 0x00, 0x00 diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat index f33da8816488..9495b09c9c0e 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -27,7 +27,6 @@ ;; add x10, x10, x4 ;; cmp x4, x8 ;; csel x9, x9, x10, hs -;; csdb ;; strb w5, [x9] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -41,7 +40,6 @@ ;; add x10, x10, x4 ;; cmp x4, x8 ;; csel x9, x9, x10, hs -;; csdb ;; ldrb w2, [x9] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index c4dd713c920b..58fee0976d96 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -30,7 +30,6 @@ ;; add x11, x11, #1, lsl #12 ;; cmp x4, x12 ;; csel x12, x13, x11, hi -;; csdb ;; strb w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -47,7 +46,6 @@ ;; add x11, x11, #1, lsl #12 ;; cmp x4, x12 ;; csel x12, x13, x11, hi -;; csdb ;; ldrb w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index d930f4c431ad..e95a0dfd4ef0 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -23,7 +23,7 @@ ;; mov x29, sp ;; mov w12, #-0xffff ;; adds x12, x4, x12 -;; b.hs #0x44 +;; b.hs #0x40 ;; 14: ldr x13, [x2, #0x40] ;; ldr x15, [x2, #0x38] ;; mov x14, #0 @@ -32,18 +32,17 @@ ;; add x15, x15, x0 ;; cmp x12, x13 ;; csel x14, x14, x15, hi -;; csdb ;; strb w5, [x14] ;; ldp x29, x30, [sp], #0x10 ;; ret -;; 44: .byte 0x1f, 0xc1, 0x00, 0x00 +;; 40: .byte 0x1f, 0xc1, 0x00, 0x00 ;; ;; wasm[0]::function[1]: ;; stp x29, x30, [sp, #-0x10]! ;; mov x29, sp ;; mov w12, #-0xffff ;; adds x12, x4, x12 -;; b.hs #0xa4 +;; b.hs #0xa0 ;; 74: ldr x13, [x2, #0x40] ;; ldr x15, [x2, #0x38] ;; mov x14, #0 @@ -52,8 +51,7 @@ ;; add x15, x15, x0 ;; cmp x12, x13 ;; csel x14, x14, x15, hi -;; csdb ;; ldrb w2, [x14] ;; ldp x29, x30, [sp], #0x10 ;; ret -;; a4: .byte 0x1f, 0xc1, 0x00, 0x00 +;; a0: .byte 0x1f, 0xc1, 0x00, 0x00 diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index 634de74c09e6..8da282884fde 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -27,7 +27,6 @@ ;; add x10, x10, x4 ;; cmp x4, x8 ;; csel x9, x9, x10, hi -;; csdb ;; str w5, [x9] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -41,7 +40,6 @@ ;; add x10, x10, x4 ;; cmp x4, x8 ;; csel x9, x9, x10, hi -;; csdb ;; ldr w2, [x9] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index 4ffa6a184674..77ed273b9ec0 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -28,7 +28,6 @@ ;; add x11, x11, #1, lsl #12 ;; cmp x4, x9 ;; csel x10, x10, x11, hi -;; csdb ;; str w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; add x11, x11, #1, lsl #12 ;; cmp x4, x9 ;; csel x10, x10, x11, hi -;; csdb ;; ldr w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 0ef43edcb81b..8feb01e6999e 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -29,7 +29,6 @@ ;; add x12, x12, x13 ;; cmp x4, x10 ;; csel x11, x11, x12, hi -;; csdb ;; str w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; add x12, x12, x13 ;; cmp x4, x10 ;; csel x11, x11, x12, hi -;; csdb ;; ldr w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index 7629b826ebd7..03d19995a964 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -27,7 +27,6 @@ ;; add x10, x10, x4 ;; cmp x4, x8 ;; csel x9, x9, x10, hs -;; csdb ;; strb w5, [x9] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -41,7 +40,6 @@ ;; add x10, x10, x4 ;; cmp x4, x8 ;; csel x9, x9, x10, hs -;; csdb ;; ldrb w2, [x9] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index 73b2d55a7234..526cfecde2e8 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -28,7 +28,6 @@ ;; add x11, x11, #1, lsl #12 ;; cmp x4, x9 ;; csel x10, x10, x11, hi -;; csdb ;; strb w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; add x11, x11, #1, lsl #12 ;; cmp x4, x9 ;; csel x10, x10, x11, hi -;; csdb ;; ldrb w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index a1b8aa5ae3e6..1037dc48a078 100644 --- a/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -29,7 +29,6 @@ ;; add x12, x12, x13 ;; cmp x4, x10 ;; csel x11, x11, x12, hi -;; csdb ;; strb w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; add x12, x12, x13 ;; cmp x4, x10 ;; csel x11, x11, x12, hi -;; csdb ;; ldrb w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat index 0446500ca64f..cc9c511d132e 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -28,7 +28,6 @@ ;; orr x8, xzr, #0xfffffffc ;; cmp x9, x8 ;; csel x11, x10, x11, hi -;; csdb ;; str w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; orr x8, xzr, #0xfffffffc ;; cmp x9, x8 ;; csel x11, x10, x11, hi -;; csdb ;; ldr w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index dd4db91be03a..9ca5ac98cfe6 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -29,7 +29,6 @@ ;; mov w9, #-0x1004 ;; cmp x10, x9 ;; csel x12, x11, x12, hi -;; csdb ;; str w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; mov w9, #-0x1004 ;; cmp x10, x9 ;; csel x12, x11, x12, hi -;; csdb ;; ldr w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 9f8198c9e0bf..f04fa8ccfb83 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -30,7 +30,6 @@ ;; mov x10, #0xfffc ;; cmp x11, x10 ;; csel x13, x12, x13, hi -;; csdb ;; str w5, [x13] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -47,7 +46,6 @@ ;; mov x10, #0xfffc ;; cmp x11, x10 ;; csel x13, x12, x13, hi -;; csdb ;; ldr w2, [x13] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 4f9c799d9fe4..b5c70c117e7c 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -29,7 +29,6 @@ ;; mov w9, #-0x1001 ;; cmp x10, x9 ;; csel x12, x11, x12, hi -;; csdb ;; strb w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; mov w9, #-0x1001 ;; cmp x10, x9 ;; csel x12, x11, x12, hi -;; csdb ;; ldrb w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index de1c151c5cb9..d1531625f362 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -30,7 +30,6 @@ ;; mov x10, #0xffff ;; cmp x11, x10 ;; csel x13, x12, x13, hi -;; csdb ;; strb w5, [x13] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -47,7 +46,6 @@ ;; mov x10, #0xffff ;; cmp x11, x10 ;; csel x13, x12, x13, hi -;; csdb ;; ldrb w2, [x13] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat index 34c1118ced71..379d5253ef6e 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -27,7 +27,6 @@ ;; orr x7, xzr, #0xfffffffc ;; cmp x4, x7 ;; csel x10, x8, x9, hi -;; csdb ;; str w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -41,7 +40,6 @@ ;; orr x7, xzr, #0xfffffffc ;; cmp x4, x7 ;; csel x10, x8, x9, hi -;; csdb ;; ldr w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index 48310d04d72a..f41234ca1a63 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -28,7 +28,6 @@ ;; mov w8, #-0x1004 ;; cmp x4, x8 ;; csel x11, x9, x10, hi -;; csdb ;; str w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; mov w8, #-0x1004 ;; cmp x4, x8 ;; csel x11, x9, x10, hi -;; csdb ;; ldr w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 76f563a37109..658fe36dbd6b 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -29,7 +29,6 @@ ;; mov x9, #0xfffc ;; cmp x4, x9 ;; csel x12, x10, x11, hi -;; csdb ;; str w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; mov x9, #0xfffc ;; cmp x4, x9 ;; csel x12, x10, x11, hi -;; csdb ;; ldr w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat index c2aea12a5e7a..3b2ee334d735 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -27,7 +27,6 @@ ;; orr x7, xzr, #0xffffffff ;; cmp x4, x7 ;; csel x10, x8, x9, hi -;; csdb ;; strb w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -41,7 +40,6 @@ ;; orr x7, xzr, #0xffffffff ;; cmp x4, x7 ;; csel x10, x8, x9, hi -;; csdb ;; ldrb w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 0a3fa6bcec98..657709d778bc 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -28,7 +28,6 @@ ;; mov w8, #-0x1001 ;; cmp x4, x8 ;; csel x11, x9, x10, hi -;; csdb ;; strb w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; mov w8, #-0x1001 ;; cmp x4, x8 ;; csel x11, x9, x10, hi -;; csdb ;; ldrb w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index a1d37b4ba7f1..2f54120b8507 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -29,7 +29,6 @@ ;; mov x9, #0xffff ;; cmp x4, x9 ;; csel x12, x10, x11, hi -;; csdb ;; strb w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; mov x9, #0xffff ;; cmp x4, x9 ;; csel x12, x10, x11, hi -;; csdb ;; ldrb w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index 79ebebdd8b85..94f32cd10566 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -27,7 +27,6 @@ ;; orr x7, xzr, #0xfffffffc ;; cmp x4, x7 ;; csel x10, x8, x9, hi -;; csdb ;; str w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -41,7 +40,6 @@ ;; orr x7, xzr, #0xfffffffc ;; cmp x4, x7 ;; csel x10, x8, x9, hi -;; csdb ;; ldr w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index 2a8cf00801f1..38547bbbb814 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -28,7 +28,6 @@ ;; mov w8, #-0x1004 ;; cmp x4, x8 ;; csel x11, x9, x10, hi -;; csdb ;; str w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; mov w8, #-0x1004 ;; cmp x4, x8 ;; csel x11, x9, x10, hi -;; csdb ;; ldr w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 36d33b5bcdaa..498cbce90731 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -29,7 +29,6 @@ ;; mov x9, #0xfffc ;; cmp x4, x9 ;; csel x12, x10, x11, hi -;; csdb ;; str w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; mov x9, #0xfffc ;; cmp x4, x9 ;; csel x12, x10, x11, hi -;; csdb ;; ldr w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index 27bd62dab24e..d64e5ca52eb4 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -27,7 +27,6 @@ ;; orr x7, xzr, #0xffffffff ;; cmp x4, x7 ;; csel x10, x8, x9, hi -;; csdb ;; strb w5, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -41,7 +40,6 @@ ;; orr x7, xzr, #0xffffffff ;; cmp x4, x7 ;; csel x10, x8, x9, hi -;; csdb ;; ldrb w2, [x10] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index a30c28908b29..eef9360b4ea6 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -28,7 +28,6 @@ ;; mov w8, #-0x1001 ;; cmp x4, x8 ;; csel x11, x9, x10, hi -;; csdb ;; strb w5, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -43,7 +42,6 @@ ;; mov w8, #-0x1001 ;; cmp x4, x8 ;; csel x11, x9, x10, hi -;; csdb ;; ldrb w2, [x11] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index a5344f78e3bc..ebbe9aec48e2 100644 --- a/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/aarch64/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -29,7 +29,6 @@ ;; mov x9, #0xffff ;; cmp x4, x9 ;; csel x12, x10, x11, hi -;; csdb ;; strb w5, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret @@ -45,7 +44,6 @@ ;; mov x9, #0xffff ;; cmp x4, x9 ;; csel x12, x10, x11, hi -;; csdb ;; ldrb w2, [x12] ;; ldp x29, x30, [sp], #0x10 ;; ret diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat index 3713784b4207..5b15e795b8b8 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -34,7 +34,7 @@ ;; @0040 v7 = isub v5, v6 ; v6 = 4 ;; @0040 v8 = icmp ugt v4, v7 ;; @0040 trapnz v8, heap_oob -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 store little heap v3, v10 ;; @0043 jump block1 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -59,7 +59,7 @@ ;; @0048 v7 = isub v5, v6 ; v6 = 4 ;; @0048 v8 = icmp ugt v4, v7 ;; @0048 trapnz v8, heap_oob -;; @0048 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v9 = load.i64 notrap aligned can_move v0+56 ;; @0048 v10 = iadd v9, v4 ;; @0048 v11 = load.i32 little heap v10 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index 807ef0c51cba..7b52ebb6f5ce 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -34,7 +34,7 @@ ;; @0040 v7 = isub v5, v6 ; v6 = 4100 ;; @0040 v8 = icmp ugt v4, v7 ;; @0040 trapnz v8, heap_oob -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 v11 = iconst.i64 4096 ;; @0040 v12 = iadd v10, v11 ; v11 = 4096 @@ -51,7 +51,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -61,7 +61,7 @@ ;; @0049 v7 = isub v5, v6 ; v6 = 4100 ;; @0049 v8 = icmp ugt v4, v7 ;; @0049 trapnz v8, heap_oob -;; @0049 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v9 = load.i64 notrap aligned can_move v0+56 ;; @0049 v10 = iadd v9, v4 ;; @0049 v11 = iconst.i64 4096 ;; @0049 v12 = iadd v10, v11 ; v11 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index 0fb86e47dc6c..804a2c20b6ef 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -34,7 +34,7 @@ ;; @0040 v7 = load.i64 notrap aligned v0+64 ;; @0040 v8 = icmp ugt v6, v7 ;; @0040 trapnz v8, heap_oob -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 v11 = iconst.i64 0xffff_0000 ;; @0040 v12 = iadd v10, v11 ; v11 = 0xffff_0000 @@ -51,7 +51,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -61,7 +61,7 @@ ;; @004c v7 = load.i64 notrap aligned v0+64 ;; @004c v8 = icmp ugt v6, v7 ;; @004c trapnz v8, heap_oob -;; @004c v9 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v9 = load.i64 notrap aligned can_move v0+56 ;; @004c v10 = iadd v9, v4 ;; @004c v11 = iconst.i64 0xffff_0000 ;; @004c v12 = iadd v10, v11 ; v11 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat index f2dd2b6e287d..030324ab711e 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp uge v4, v5 ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 istore8 little heap v3, v8 ;; @0043 jump block1 @@ -47,7 +47,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -55,7 +55,7 @@ ;; @0048 v5 = load.i64 notrap aligned v0+64 ;; @0048 v6 = icmp uge v4, v5 ;; @0048 trapnz v6, heap_oob -;; @0048 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v7 = load.i64 notrap aligned can_move v0+56 ;; @0048 v8 = iadd v7, v4 ;; @0048 v9 = uload8.i32 little heap v8 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index 514005211a60..e9b669f45ffe 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -34,7 +34,7 @@ ;; @0040 v7 = isub v5, v6 ; v6 = 4097 ;; @0040 v8 = icmp ugt v4, v7 ;; @0040 trapnz v8, heap_oob -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 v11 = iconst.i64 4096 ;; @0040 v12 = iadd v10, v11 ; v11 = 4096 @@ -51,7 +51,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -61,7 +61,7 @@ ;; @0049 v7 = isub v5, v6 ; v6 = 4097 ;; @0049 v8 = icmp ugt v4, v7 ;; @0049 trapnz v8, heap_oob -;; @0049 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v9 = load.i64 notrap aligned can_move v0+56 ;; @0049 v10 = iadd v9, v4 ;; @0049 v11 = iconst.i64 4096 ;; @0049 v12 = iadd v10, v11 ; v11 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index 2738c93981c0..b64e54555fdf 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -34,7 +34,7 @@ ;; @0040 v7 = load.i64 notrap aligned v0+64 ;; @0040 v8 = icmp ugt v6, v7 ;; @0040 trapnz v8, heap_oob -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 v11 = iconst.i64 0xffff_0000 ;; @0040 v12 = iadd v10, v11 ; v11 = 0xffff_0000 @@ -51,7 +51,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -61,7 +61,7 @@ ;; @004c v7 = load.i64 notrap aligned v0+64 ;; @004c v8 = icmp ugt v6, v7 ;; @004c trapnz v8, heap_oob -;; @004c v9 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v9 = load.i64 notrap aligned can_move v0+56 ;; @004c v10 = iadd v9, v4 ;; @004c v11 = iconst.i64 0xffff_0000 ;; @004c v12 = iadd v10, v11 ; v11 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat index 2fa019b3f6c3..72a9e7245195 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = iconst.i64 4 ;; @0040 v7 = isub v5, v6 ; v6 = 4 ;; @0040 v8 = icmp ugt v4, v7 -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 v11 = iconst.i64 0 ;; @0040 v12 = select_spectre_guard v8, v11, v10 ; v11 = 0 @@ -50,7 +50,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -59,7 +59,7 @@ ;; @0048 v6 = iconst.i64 4 ;; @0048 v7 = isub v5, v6 ; v6 = 4 ;; @0048 v8 = icmp ugt v4, v7 -;; @0048 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v9 = load.i64 notrap aligned can_move v0+56 ;; @0048 v10 = iadd v9, v4 ;; @0048 v11 = iconst.i64 0 ;; @0048 v12 = select_spectre_guard v8, v11, v10 ; v11 = 0 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index fd192faa8baf..aa886558a3b0 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = iconst.i64 4100 ;; @0040 v7 = isub v5, v6 ; v6 = 4100 ;; @0040 v8 = icmp ugt v4, v7 -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 v11 = iconst.i64 4096 ;; @0040 v12 = iadd v10, v11 ; v11 = 4096 @@ -52,7 +52,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -61,7 +61,7 @@ ;; @0049 v6 = iconst.i64 4100 ;; @0049 v7 = isub v5, v6 ; v6 = 4100 ;; @0049 v8 = icmp ugt v4, v7 -;; @0049 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v9 = load.i64 notrap aligned can_move v0+56 ;; @0049 v10 = iadd v9, v4 ;; @0049 v11 = iconst.i64 4096 ;; @0049 v12 = iadd v10, v11 ; v11 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index a37514106377..3e29b11b5275 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = uadd_overflow_trap v4, v5, heap_oob ; v5 = 0xffff_0004 ;; @0040 v7 = load.i64 notrap aligned v0+64 ;; @0040 v8 = icmp ugt v6, v7 -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 v11 = iconst.i64 0xffff_0000 ;; @0040 v12 = iadd v10, v11 ; v11 = 0xffff_0000 @@ -52,7 +52,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -61,7 +61,7 @@ ;; @004c v6 = uadd_overflow_trap v4, v5, heap_oob ; v5 = 0xffff_0004 ;; @004c v7 = load.i64 notrap aligned v0+64 ;; @004c v8 = icmp ugt v6, v7 -;; @004c v9 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v9 = load.i64 notrap aligned can_move v0+56 ;; @004c v10 = iadd v9, v4 ;; @004c v11 = iconst.i64 0xffff_0000 ;; @004c v12 = iadd v10, v11 ; v11 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat index a3d8d4f385ab..2db998084749 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp uge v4, v5 -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0 ;; @0040 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 ;; @0048 v5 = load.i64 notrap aligned v0+64 ;; @0048 v6 = icmp uge v4, v5 -;; @0048 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v7 = load.i64 notrap aligned can_move v0+56 ;; @0048 v8 = iadd v7, v4 ;; @0048 v9 = iconst.i64 0 ;; @0048 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 040bdc2b81e8..60bbf550c4ca 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = iconst.i64 4097 ;; @0040 v7 = isub v5, v6 ; v6 = 4097 ;; @0040 v8 = icmp ugt v4, v7 -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 v11 = iconst.i64 4096 ;; @0040 v12 = iadd v10, v11 ; v11 = 4096 @@ -52,7 +52,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -61,7 +61,7 @@ ;; @0049 v6 = iconst.i64 4097 ;; @0049 v7 = isub v5, v6 ; v6 = 4097 ;; @0049 v8 = icmp ugt v4, v7 -;; @0049 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v9 = load.i64 notrap aligned can_move v0+56 ;; @0049 v10 = iadd v9, v4 ;; @0049 v11 = iconst.i64 4096 ;; @0049 v12 = iadd v10, v11 ; v11 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 727d3707bcdc..d261592bd9a9 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = uadd_overflow_trap v4, v5, heap_oob ; v5 = 0xffff_0001 ;; @0040 v7 = load.i64 notrap aligned v0+64 ;; @0040 v8 = icmp ugt v6, v7 -;; @0040 v9 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v9 = load.i64 notrap aligned can_move v0+56 ;; @0040 v10 = iadd v9, v4 ;; @0040 v11 = iconst.i64 0xffff_0000 ;; @0040 v12 = iadd v10, v11 ; v11 = 0xffff_0000 @@ -52,7 +52,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -61,7 +61,7 @@ ;; @004c v6 = uadd_overflow_trap v4, v5, heap_oob ; v5 = 0xffff_0001 ;; @004c v7 = load.i64 notrap aligned v0+64 ;; @004c v8 = icmp ugt v6, v7 -;; @004c v9 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v9 = load.i64 notrap aligned can_move v0+56 ;; @004c v10 = iadd v9, v4 ;; @004c v11 = iconst.i64 0xffff_0000 ;; @004c v12 = iadd v10, v11 ; v11 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat index 2d732c19dc49..e38632f53e5d 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 store little heap v3, v8 ;; @0043 jump block1 @@ -47,7 +47,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -55,7 +55,7 @@ ;; @0048 v5 = load.i64 notrap aligned v0+64 ;; @0048 v6 = icmp ugt v4, v5 ;; @0048 trapnz v6, heap_oob -;; @0048 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v7 = load.i64 notrap aligned can_move v0+56 ;; @0048 v8 = iadd v7, v4 ;; @0048 v9 = load.i32 little heap v8 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index 071c1cee2210..086f27a4df3d 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 4096 ;; @0040 v10 = iadd v8, v9 ; v9 = 4096 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @0049 v5 = load.i64 notrap aligned v0+64 ;; @0049 v6 = icmp ugt v4, v5 ;; @0049 trapnz v6, heap_oob -;; @0049 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = iconst.i64 4096 ;; @0049 v10 = iadd v8, v9 ; v9 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index 41f7f89e6794..58bdb43441b9 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0xffff_0000 ;; @0040 v10 = iadd v8, v9 ; v9 = 0xffff_0000 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @004c v5 = load.i64 notrap aligned v0+64 ;; @004c v6 = icmp ugt v4, v5 ;; @004c trapnz v6, heap_oob -;; @004c v7 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v7 = load.i64 notrap aligned can_move v0+56 ;; @004c v8 = iadd v7, v4 ;; @004c v9 = iconst.i64 0xffff_0000 ;; @004c v10 = iadd v8, v9 ; v9 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat index 125600739e1d..bc08355b4f8f 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp uge v4, v5 ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 istore8 little heap v3, v8 ;; @0043 jump block1 @@ -47,7 +47,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -55,7 +55,7 @@ ;; @0048 v5 = load.i64 notrap aligned v0+64 ;; @0048 v6 = icmp uge v4, v5 ;; @0048 trapnz v6, heap_oob -;; @0048 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v7 = load.i64 notrap aligned can_move v0+56 ;; @0048 v8 = iadd v7, v4 ;; @0048 v9 = uload8.i32 little heap v8 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index 5d2824ed8bc1..db6fc8becc90 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 4096 ;; @0040 v10 = iadd v8, v9 ; v9 = 4096 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @0049 v5 = load.i64 notrap aligned v0+64 ;; @0049 v6 = icmp ugt v4, v5 ;; @0049 trapnz v6, heap_oob -;; @0049 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = iconst.i64 4096 ;; @0049 v10 = iadd v8, v9 ; v9 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index 96b8d395de55..815c2f1972a0 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0xffff_0000 ;; @0040 v10 = iadd v8, v9 ; v9 = 0xffff_0000 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @004c v5 = load.i64 notrap aligned v0+64 ;; @004c v6 = icmp ugt v4, v5 ;; @004c trapnz v6, heap_oob -;; @004c v7 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v7 = load.i64 notrap aligned can_move v0+56 ;; @004c v8 = iadd v7, v4 ;; @004c v9 = iconst.i64 0xffff_0000 ;; @004c v10 = iadd v8, v9 ; v9 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index a93bb3b2516d..4bc0faf827ee 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0 ;; @0040 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 ;; @0048 v5 = load.i64 notrap aligned v0+64 ;; @0048 v6 = icmp ugt v4, v5 -;; @0048 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v7 = load.i64 notrap aligned can_move v0+56 ;; @0048 v8 = iadd v7, v4 ;; @0048 v9 = iconst.i64 0 ;; @0048 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index ac4833e174e2..a475a4944b98 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 4096 ;; @0040 v10 = iadd v8, v9 ; v9 = 4096 @@ -50,14 +50,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0049 v4 = uextend.i64 v2 ;; @0049 v5 = load.i64 notrap aligned v0+64 ;; @0049 v6 = icmp ugt v4, v5 -;; @0049 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = iconst.i64 4096 ;; @0049 v10 = iadd v8, v9 ; v9 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index a6a4f03bb0c7..2b10a932f2fc 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0xffff_0000 ;; @0040 v10 = iadd v8, v9 ; v9 = 0xffff_0000 @@ -50,14 +50,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @004c v4 = uextend.i64 v2 ;; @004c v5 = load.i64 notrap aligned v0+64 ;; @004c v6 = icmp ugt v4, v5 -;; @004c v7 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v7 = load.i64 notrap aligned can_move v0+56 ;; @004c v8 = iadd v7, v4 ;; @004c v9 = iconst.i64 0xffff_0000 ;; @004c v10 = iadd v8, v9 ; v9 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index a32b008368c0..d64e835481f9 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp uge v4, v5 -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0 ;; @0040 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 ;; @0048 v5 = load.i64 notrap aligned v0+64 ;; @0048 v6 = icmp uge v4, v5 -;; @0048 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v7 = load.i64 notrap aligned can_move v0+56 ;; @0048 v8 = iadd v7, v4 ;; @0048 v9 = iconst.i64 0 ;; @0048 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index 5e07e71267f2..d2e45b8afcfe 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 4096 ;; @0040 v10 = iadd v8, v9 ; v9 = 4096 @@ -50,14 +50,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0049 v4 = uextend.i64 v2 ;; @0049 v5 = load.i64 notrap aligned v0+64 ;; @0049 v6 = icmp ugt v4, v5 -;; @0049 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = iconst.i64 4096 ;; @0049 v10 = iadd v8, v9 ; v9 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 9eb8e9472b46..9e1acfb92a2c 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = load.i64 notrap aligned v0+64 ;; @0040 v6 = icmp ugt v4, v5 -;; @0040 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0xffff_0000 ;; @0040 v10 = iadd v8, v9 ; v9 = 0xffff_0000 @@ -50,14 +50,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @004c v4 = uextend.i64 v2 ;; @004c v5 = load.i64 notrap aligned v0+64 ;; @004c v6 = icmp ugt v4, v5 -;; @004c v7 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v7 = load.i64 notrap aligned can_move v0+56 ;; @004c v8 = iadd v7, v4 ;; @004c v9 = iconst.i64 0xffff_0000 ;; @004c v10 = iadd v8, v9 ; v9 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat index 994d02a9877f..d80c3271f1a1 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = isub v4, v5 ; v5 = 4 ;; @0040 v7 = icmp ugt v2, v6 ;; @0040 trapnz v7, heap_oob -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 store little heap v3, v9 ;; @0043 jump block1 @@ -48,7 +48,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -57,7 +57,7 @@ ;; @0048 v6 = isub v4, v5 ; v5 = 4 ;; @0048 v7 = icmp ugt v2, v6 ;; @0048 trapnz v7, heap_oob -;; @0048 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v8 = load.i64 notrap aligned can_move v0+56 ;; @0048 v9 = iadd v8, v2 ;; @0048 v10 = load.i32 little heap v9 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index 296d8487713a..9b55ba5764dc 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = isub v4, v5 ; v5 = 4100 ;; @0040 v7 = icmp ugt v2, v6 ;; @0040 trapnz v7, heap_oob -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 v10 = iconst.i64 4096 ;; @0040 v11 = iadd v9, v10 ; v10 = 4096 @@ -50,7 +50,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -59,7 +59,7 @@ ;; @0049 v6 = isub v4, v5 ; v5 = 4100 ;; @0049 v7 = icmp ugt v2, v6 ;; @0049 trapnz v7, heap_oob -;; @0049 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v8 = load.i64 notrap aligned can_move v0+56 ;; @0049 v9 = iadd v8, v2 ;; @0049 v10 = iconst.i64 4096 ;; @0049 v11 = iadd v9, v10 ; v10 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index 41755f41364e..e2ca694c51d0 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = load.i64 notrap aligned v0+64 ;; @0040 v7 = icmp ugt v5, v6 ;; @0040 trapnz v7, heap_oob -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 v10 = iconst.i64 0xffff_0000 ;; @0040 v11 = iadd v9, v10 ; v10 = 0xffff_0000 @@ -50,7 +50,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -59,7 +59,7 @@ ;; @004c v6 = load.i64 notrap aligned v0+64 ;; @004c v7 = icmp ugt v5, v6 ;; @004c trapnz v7, heap_oob -;; @004c v8 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v8 = load.i64 notrap aligned can_move v0+56 ;; @004c v9 = iadd v8, v2 ;; @004c v10 = iconst.i64 0xffff_0000 ;; @004c v11 = iadd v9, v10 ; v10 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat index 214e5d90f8cc..ab453c32f6a6 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp uge v2, v4 ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 istore8 little heap v3, v7 ;; @0043 jump block1 @@ -46,14 +46,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = load.i64 notrap aligned v0+64 ;; @0048 v5 = icmp uge v2, v4 ;; @0048 trapnz v5, heap_oob -;; @0048 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = uload8.i32 little heap v7 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index ed0426c8125d..965ea70dbfb3 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = isub v4, v5 ; v5 = 4097 ;; @0040 v7 = icmp ugt v2, v6 ;; @0040 trapnz v7, heap_oob -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 v10 = iconst.i64 4096 ;; @0040 v11 = iadd v9, v10 ; v10 = 4096 @@ -50,7 +50,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -59,7 +59,7 @@ ;; @0049 v6 = isub v4, v5 ; v5 = 4097 ;; @0049 v7 = icmp ugt v2, v6 ;; @0049 trapnz v7, heap_oob -;; @0049 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v8 = load.i64 notrap aligned can_move v0+56 ;; @0049 v9 = iadd v8, v2 ;; @0049 v10 = iconst.i64 4096 ;; @0049 v11 = iadd v9, v10 ; v10 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index 2c064d2e54b0..a01e994ff089 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -33,7 +33,7 @@ ;; @0040 v6 = load.i64 notrap aligned v0+64 ;; @0040 v7 = icmp ugt v5, v6 ;; @0040 trapnz v7, heap_oob -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 v10 = iconst.i64 0xffff_0000 ;; @0040 v11 = iadd v9, v10 ; v10 = 0xffff_0000 @@ -50,7 +50,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -59,7 +59,7 @@ ;; @004c v6 = load.i64 notrap aligned v0+64 ;; @004c v7 = icmp ugt v5, v6 ;; @004c trapnz v7, heap_oob -;; @004c v8 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v8 = load.i64 notrap aligned can_move v0+56 ;; @004c v9 = iadd v8, v2 ;; @004c v10 = iconst.i64 0xffff_0000 ;; @004c v11 = iadd v9, v10 ; v10 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat index 449ad49d14e2..fd93721ab1bf 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = iconst.i64 4 ;; @0040 v6 = isub v4, v5 ; v5 = 4 ;; @0040 v7 = icmp ugt v2, v6 -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 v10 = iconst.i64 0 ;; @0040 v11 = select_spectre_guard v7, v10, v9 ; v10 = 0 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -57,7 +57,7 @@ ;; @0048 v5 = iconst.i64 4 ;; @0048 v6 = isub v4, v5 ; v5 = 4 ;; @0048 v7 = icmp ugt v2, v6 -;; @0048 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v8 = load.i64 notrap aligned can_move v0+56 ;; @0048 v9 = iadd v8, v2 ;; @0048 v10 = iconst.i64 0 ;; @0048 v11 = select_spectre_guard v7, v10, v9 ; v10 = 0 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index f1741d8e1527..8bd1569d114a 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = iconst.i64 4100 ;; @0040 v6 = isub v4, v5 ; v5 = 4100 ;; @0040 v7 = icmp ugt v2, v6 -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 v10 = iconst.i64 4096 ;; @0040 v11 = iadd v9, v10 ; v10 = 4096 @@ -51,7 +51,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -59,7 +59,7 @@ ;; @0049 v5 = iconst.i64 4100 ;; @0049 v6 = isub v4, v5 ; v5 = 4100 ;; @0049 v7 = icmp ugt v2, v6 -;; @0049 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v8 = load.i64 notrap aligned can_move v0+56 ;; @0049 v9 = iadd v8, v2 ;; @0049 v10 = iconst.i64 4096 ;; @0049 v11 = iadd v9, v10 ; v10 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index b50d7a0647b3..40450e3a505d 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = uadd_overflow_trap v2, v4, heap_oob ; v4 = 0xffff_0004 ;; @0040 v6 = load.i64 notrap aligned v0+64 ;; @0040 v7 = icmp ugt v5, v6 -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 v10 = iconst.i64 0xffff_0000 ;; @0040 v11 = iadd v9, v10 ; v10 = 0xffff_0000 @@ -51,7 +51,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -59,7 +59,7 @@ ;; @004c v5 = uadd_overflow_trap v2, v4, heap_oob ; v4 = 0xffff_0004 ;; @004c v6 = load.i64 notrap aligned v0+64 ;; @004c v7 = icmp ugt v5, v6 -;; @004c v8 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v8 = load.i64 notrap aligned can_move v0+56 ;; @004c v9 = iadd v8, v2 ;; @004c v10 = iconst.i64 0xffff_0000 ;; @004c v11 = iadd v9, v10 ; v10 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat index 643e9f858150..43a316fb86b1 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp uge v2, v4 -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0 ;; @0040 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 @@ -47,13 +47,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = load.i64 notrap aligned v0+64 ;; @0048 v5 = icmp uge v2, v4 -;; @0048 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = iconst.i64 0 ;; @0048 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 328161faab16..9dac946ce68f 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = iconst.i64 4097 ;; @0040 v6 = isub v4, v5 ; v5 = 4097 ;; @0040 v7 = icmp ugt v2, v6 -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 v10 = iconst.i64 4096 ;; @0040 v11 = iadd v9, v10 ; v10 = 4096 @@ -51,7 +51,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -59,7 +59,7 @@ ;; @0049 v5 = iconst.i64 4097 ;; @0049 v6 = isub v4, v5 ; v5 = 4097 ;; @0049 v7 = icmp ugt v2, v6 -;; @0049 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v8 = load.i64 notrap aligned can_move v0+56 ;; @0049 v9 = iadd v8, v2 ;; @0049 v10 = iconst.i64 4096 ;; @0049 v11 = iadd v9, v10 ; v10 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index bece38fa8009..4d9824dad65e 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = uadd_overflow_trap v2, v4, heap_oob ; v4 = 0xffff_0001 ;; @0040 v6 = load.i64 notrap aligned v0+64 ;; @0040 v7 = icmp ugt v5, v6 -;; @0040 v8 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v8 = load.i64 notrap aligned can_move v0+56 ;; @0040 v9 = iadd v8, v2 ;; @0040 v10 = iconst.i64 0xffff_0000 ;; @0040 v11 = iadd v9, v10 ; v10 = 0xffff_0000 @@ -51,7 +51,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): @@ -59,7 +59,7 @@ ;; @004c v5 = uadd_overflow_trap v2, v4, heap_oob ; v4 = 0xffff_0001 ;; @004c v6 = load.i64 notrap aligned v0+64 ;; @004c v7 = icmp ugt v5, v6 -;; @004c v8 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v8 = load.i64 notrap aligned can_move v0+56 ;; @004c v9 = iadd v8, v2 ;; @004c v10 = iconst.i64 0xffff_0000 ;; @004c v11 = iadd v9, v10 ; v10 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat index ad335b91a327..6646d81a5296 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 store little heap v3, v7 ;; @0043 jump block1 @@ -46,14 +46,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = load.i64 notrap aligned v0+64 ;; @0048 v5 = icmp ugt v2, v4 ;; @0048 trapnz v5, heap_oob -;; @0048 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = load.i32 little heap v7 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index f0655273d08c..0296e7ad16da 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = load.i64 notrap aligned v0+64 ;; @0049 v5 = icmp ugt v2, v4 ;; @0049 trapnz v5, heap_oob -;; @0049 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index 8d1d78d02cb3..4e2aa821a7af 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = load.i64 notrap aligned v0+64 ;; @004c v5 = icmp ugt v2, v4 ;; @004c trapnz v5, heap_oob -;; @004c v6 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat index 944309fbbe3d..1bba7c0a4785 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp uge v2, v4 ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 istore8 little heap v3, v7 ;; @0043 jump block1 @@ -46,14 +46,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = load.i64 notrap aligned v0+64 ;; @0048 v5 = icmp uge v2, v4 ;; @0048 trapnz v5, heap_oob -;; @0048 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = uload8.i32 little heap v7 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index ae400e92f2fa..6dd5f62f961c 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = load.i64 notrap aligned v0+64 ;; @0049 v5 = icmp ugt v2, v4 ;; @0049 trapnz v5, heap_oob -;; @0049 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index a019b57543f1..6ee354e95c01 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = load.i64 notrap aligned v0+64 ;; @004c v5 = icmp ugt v2, v4 ;; @004c trapnz v5, heap_oob -;; @004c v6 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index 69d93bb9da65..a7144490b99c 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0 ;; @0040 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 @@ -47,13 +47,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = load.i64 notrap aligned v0+64 ;; @0048 v5 = icmp ugt v2, v4 -;; @0048 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = iconst.i64 0 ;; @0048 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index f7f6cfb15036..8e6c25e426f4 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = load.i64 notrap aligned v0+64 ;; @0049 v5 = icmp ugt v2, v4 -;; @0049 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index ab74d87b80ce..a328aca1e82d 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = load.i64 notrap aligned v0+64 ;; @004c v5 = icmp ugt v2, v4 -;; @004c v6 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index c763f92fdc1f..41a9d11ab937 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp uge v2, v4 -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0 ;; @0040 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 @@ -47,13 +47,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = load.i64 notrap aligned v0+64 ;; @0048 v5 = icmp uge v2, v4 -;; @0048 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = iconst.i64 0 ;; @0048 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index 60147188f45a..665aa8d37e57 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = load.i64 notrap aligned v0+64 ;; @0049 v5 = icmp ugt v2, v4 -;; @0049 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index d9f54721c81d..5487aaa59155 100644 --- a/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = load.i64 notrap aligned v0+64 ;; @0040 v5 = icmp ugt v2, v4 -;; @0040 v6 = load.i64 notrap aligned can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = load.i64 notrap aligned v0+64 ;; @004c v5 = icmp ugt v2, v4 -;; @004c v6 = load.i64 notrap aligned can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat index d90cfeca1bee..1a7be680f538 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = iconst.i64 0xffff_fffc ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xffff_fffc ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 store little heap v3, v8 ;; @0043 jump block1 @@ -47,7 +47,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -55,7 +55,7 @@ ;; @0048 v5 = iconst.i64 0xffff_fffc ;; @0048 v6 = icmp ugt v4, v5 ; v5 = 0xffff_fffc ;; @0048 trapnz v6, heap_oob -;; @0048 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v8 = iadd v7, v4 ;; @0048 v9 = load.i32 little heap v8 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index f18cc613ae51..408f19f5c6c2 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = iconst.i64 0xffff_effc ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xffff_effc ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 4096 ;; @0040 v10 = iadd v8, v9 ; v9 = 4096 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @0049 v5 = iconst.i64 0xffff_effc ;; @0049 v6 = icmp ugt v4, v5 ; v5 = 0xffff_effc ;; @0049 trapnz v6, heap_oob -;; @0049 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = iconst.i64 4096 ;; @0049 v10 = iadd v8, v9 ; v9 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index 98f6be8e30f3..3324139c5463 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = iconst.i64 0xfffc ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xfffc ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0xffff_0000 ;; @0040 v10 = iadd v8, v9 ; v9 = 0xffff_0000 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @004c v5 = iconst.i64 0xfffc ;; @004c v6 = icmp ugt v4, v5 ; v5 = 0xfffc ;; @004c trapnz v6, heap_oob -;; @004c v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v8 = iadd v7, v4 ;; @004c v9 = iconst.i64 0xffff_0000 ;; @004c v10 = iadd v8, v9 ; v9 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat index 5c5a3a57fcb3..2635c82311cf 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 istore8 little heap v3, v6 ;; @0043 jump block1 @@ -44,12 +44,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 -;; @0048 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v6 = iadd v5, v4 ;; @0048 v7 = uload8.i32 little heap v6 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index e85f6b96b57c..69e22a68297e 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = iconst.i64 0xffff_efff ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xffff_efff ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 4096 ;; @0040 v10 = iadd v8, v9 ; v9 = 4096 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @0049 v5 = iconst.i64 0xffff_efff ;; @0049 v6 = icmp ugt v4, v5 ; v5 = 0xffff_efff ;; @0049 trapnz v6, heap_oob -;; @0049 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = iconst.i64 4096 ;; @0049 v10 = iadd v8, v9 ; v9 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index a79af6549665..bda2cd0cc15d 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -24,7 +24,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -32,7 +32,7 @@ ;; @0040 v5 = iconst.i64 0xffff ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xffff ;; @0040 trapnz v6, heap_oob -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0xffff_0000 ;; @0040 v10 = iadd v8, v9 ; v9 = 0xffff_0000 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @004c v5 = iconst.i64 0xffff ;; @004c v6 = icmp ugt v4, v5 ; v5 = 0xffff ;; @004c trapnz v6, heap_oob -;; @004c v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v8 = iadd v7, v4 ;; @004c v9 = iconst.i64 0xffff_0000 ;; @004c v10 = iadd v8, v9 ; v9 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat index b60582206327..fe507fb8e91e 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = iconst.i64 0xffff_fffc ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xffff_fffc -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0 ;; @0040 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 ;; @0048 v5 = iconst.i64 0xffff_fffc ;; @0048 v6 = icmp ugt v4, v5 ; v5 = 0xffff_fffc -;; @0048 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v8 = iadd v7, v4 ;; @0048 v9 = iconst.i64 0 ;; @0048 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index 93645c3b0e59..d17f02e9b74a 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = iconst.i64 0xffff_effc ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xffff_effc -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 4096 ;; @0040 v10 = iadd v8, v9 ; v9 = 4096 @@ -50,14 +50,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0049 v4 = uextend.i64 v2 ;; @0049 v5 = iconst.i64 0xffff_effc ;; @0049 v6 = icmp ugt v4, v5 ; v5 = 0xffff_effc -;; @0049 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = iconst.i64 4096 ;; @0049 v10 = iadd v8, v9 ; v9 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 3bf097d7c097..dbd5f90c4b91 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = iconst.i64 0xfffc ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xfffc -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0xffff_0000 ;; @0040 v10 = iadd v8, v9 ; v9 = 0xffff_0000 @@ -50,14 +50,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @004c v4 = uextend.i64 v2 ;; @004c v5 = iconst.i64 0xfffc ;; @004c v6 = icmp ugt v4, v5 ; v5 = 0xfffc -;; @004c v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v8 = iadd v7, v4 ;; @004c v9 = iconst.i64 0xffff_0000 ;; @004c v10 = iadd v8, v9 ; v9 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat index 646bf3579969..761234df82a8 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 istore8 little heap v3, v6 ;; @0043 jump block1 @@ -44,12 +44,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 -;; @0048 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v6 = iadd v5, v4 ;; @0048 v7 = uload8.i32 little heap v6 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index ec1caad015c9..ad65905a8615 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = iconst.i64 0xffff_efff ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xffff_efff -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 4096 ;; @0040 v10 = iadd v8, v9 ; v9 = 4096 @@ -50,14 +50,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0049 v4 = uextend.i64 v2 ;; @0049 v5 = iconst.i64 0xffff_efff ;; @0049 v6 = icmp ugt v4, v5 ; v5 = 0xffff_efff -;; @0049 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = iconst.i64 4096 ;; @0049 v10 = iadd v8, v9 ; v9 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 79403464b8b0..9a47ee58a386 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 ;; @0040 v5 = iconst.i64 0xffff ;; @0040 v6 = icmp ugt v4, v5 ; v5 = 0xffff -;; @0040 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v8 = iadd v7, v4 ;; @0040 v9 = iconst.i64 0xffff_0000 ;; @0040 v10 = iadd v8, v9 ; v9 = 0xffff_0000 @@ -50,14 +50,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @004c v4 = uextend.i64 v2 ;; @004c v5 = iconst.i64 0xffff ;; @004c v6 = icmp ugt v4, v5 ; v5 = 0xffff -;; @004c v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v8 = iadd v7, v4 ;; @004c v9 = iconst.i64 0xffff_0000 ;; @004c v10 = iadd v8, v9 ; v9 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat index b250b56d272c..d892f495e2d7 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 store little heap v3, v6 ;; @0043 jump block1 @@ -44,12 +44,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 -;; @0048 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v6 = iadd v5, v4 ;; @0048 v7 = load.i32 little heap v6 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index 41bd965cb714..d8be7984416c 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 v7 = iconst.i64 4096 ;; @0040 v8 = iadd v6, v7 ; v7 = 4096 @@ -46,12 +46,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0049 v4 = uextend.i64 v2 -;; @0049 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v6 = iadd v5, v4 ;; @0049 v7 = iconst.i64 4096 ;; @0049 v8 = iadd v6, v7 ; v7 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index d444296db24f..c4a02249ba35 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 v7 = iconst.i64 0xffff_0000 ;; @0040 v8 = iadd v6, v7 ; v7 = 0xffff_0000 @@ -46,12 +46,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @004c v4 = uextend.i64 v2 -;; @004c v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v6 = iadd v5, v4 ;; @004c v7 = iconst.i64 0xffff_0000 ;; @004c v8 = iadd v6, v7 ; v7 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat index 95124842c327..34fc6379495a 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 istore8 little heap v3, v6 ;; @0043 jump block1 @@ -44,12 +44,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 -;; @0048 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v6 = iadd v5, v4 ;; @0048 v7 = uload8.i32 little heap v6 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index 53b3e64327a3..02f52b5b5cfc 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 v7 = iconst.i64 4096 ;; @0040 v8 = iadd v6, v7 ; v7 = 4096 @@ -46,12 +46,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0049 v4 = uextend.i64 v2 -;; @0049 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v6 = iadd v5, v4 ;; @0049 v7 = iconst.i64 4096 ;; @0049 v8 = iadd v6, v7 ; v7 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index 42aae9d8d4fe..5e723c5ea2dc 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 v7 = iconst.i64 0xffff_0000 ;; @0040 v8 = iadd v6, v7 ; v7 = 0xffff_0000 @@ -46,12 +46,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @004c v4 = uextend.i64 v2 -;; @004c v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v6 = iadd v5, v4 ;; @004c v7 = iconst.i64 0xffff_0000 ;; @004c v8 = iadd v6, v7 ; v7 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index ee837786e1ef..af0143701b00 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 store little heap v3, v6 ;; @0043 jump block1 @@ -44,12 +44,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 -;; @0048 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v6 = iadd v5, v4 ;; @0048 v7 = load.i32 little heap v6 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index 2c655f91c3e5..cd5d9df4cd5c 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 v7 = iconst.i64 4096 ;; @0040 v8 = iadd v6, v7 ; v7 = 4096 @@ -46,12 +46,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0049 v4 = uextend.i64 v2 -;; @0049 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v6 = iadd v5, v4 ;; @0049 v7 = iconst.i64 4096 ;; @0049 v8 = iadd v6, v7 ; v7 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 07e1743a49b2..1af87da238de 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 v7 = iconst.i64 0xffff_0000 ;; @0040 v8 = iadd v6, v7 ; v7 = 0xffff_0000 @@ -46,12 +46,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @004c v4 = uextend.i64 v2 -;; @004c v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v6 = iadd v5, v4 ;; @004c v7 = iconst.i64 0xffff_0000 ;; @004c v8 = iadd v6, v7 ; v7 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index 4f293f188b0f..be7bd3c3d354 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 istore8 little heap v3, v6 ;; @0043 jump block1 @@ -44,12 +44,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0048 v4 = uextend.i64 v2 -;; @0048 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v6 = iadd v5, v4 ;; @0048 v7 = uload8.i32 little heap v6 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index 566dffed4c76..d9a8a51998c0 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 v7 = iconst.i64 4096 ;; @0040 v8 = iadd v6, v7 ; v7 = 4096 @@ -46,12 +46,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0049 v4 = uextend.i64 v2 -;; @0049 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v6 = iadd v5, v4 ;; @0049 v7 = iconst.i64 4096 ;; @0049 v8 = iadd v6, v7 ; v7 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 6dd973f72324..788524f91a79 100644 --- a/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -24,12 +24,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): ;; @0040 v4 = uextend.i64 v2 -;; @0040 v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v6 = iadd v5, v4 ;; @0040 v7 = iconst.i64 0xffff_0000 ;; @0040 v8 = iadd v6, v7 ; v7 = 0xffff_0000 @@ -46,12 +46,12 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @004c v4 = uextend.i64 v2 -;; @004c v5 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v5 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v6 = iadd v5, v4 ;; @004c v7 = iconst.i64 0xffff_0000 ;; @004c v8 = iadd v6, v7 ; v7 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat index b5ac2290129f..460fef89e887 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_fffc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_fffc ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 store little heap v3, v7 ;; @0043 jump block1 @@ -46,14 +46,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = iconst.i64 0xffff_fffc ;; @0048 v5 = icmp ugt v2, v4 ; v4 = 0xffff_fffc ;; @0048 trapnz v5, heap_oob -;; @0048 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = load.i32 little heap v7 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index 14e6cf687f83..39cb1a77ef6b 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_effc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_effc ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = iconst.i64 0xffff_effc ;; @0049 v5 = icmp ugt v2, v4 ; v4 = 0xffff_effc ;; @0049 trapnz v5, heap_oob -;; @0049 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index 1be73a0a48d9..10a55e3303a8 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xfffc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xfffc ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = iconst.i64 0xfffc ;; @004c v5 = icmp ugt v2, v4 ; v4 = 0xfffc ;; @004c trapnz v5, heap_oob -;; @004c v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat index b5064db26bec..368a60bb5337 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_ffff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_ffff ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 istore8 little heap v3, v7 ;; @0043 jump block1 @@ -46,14 +46,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = iconst.i64 0xffff_ffff ;; @0048 v5 = icmp ugt v2, v4 ; v4 = 0xffff_ffff ;; @0048 trapnz v5, heap_oob -;; @0048 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = uload8.i32 little heap v7 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index e20654fd17b4..f94e1f1f2c3a 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_efff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_efff ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = iconst.i64 0xffff_efff ;; @0049 v5 = icmp ugt v2, v4 ; v4 = 0xffff_efff ;; @0049 trapnz v5, heap_oob -;; @0049 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index 6c73ec808ce1..67f3c93d9f27 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = iconst.i64 0xffff ;; @004c v5 = icmp ugt v2, v4 ; v4 = 0xffff ;; @004c trapnz v5, heap_oob -;; @004c v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat index a384ecc9c3c9..d2d0f0cf17a3 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_fffc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_fffc -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0 ;; @0040 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 @@ -47,13 +47,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = iconst.i64 0xffff_fffc ;; @0048 v5 = icmp ugt v2, v4 ; v4 = 0xffff_fffc -;; @0048 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = iconst.i64 0 ;; @0048 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index 7e9edf0c0bf8..04da373d5f53 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_effc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_effc -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = iconst.i64 0xffff_effc ;; @0049 v5 = icmp ugt v2, v4 ; v4 = 0xffff_effc -;; @0049 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 7c1485b9e6a4..813ae6e37ece 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xfffc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xfffc -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = iconst.i64 0xfffc ;; @004c v5 = icmp ugt v2, v4 ; v4 = 0xfffc -;; @004c v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat index 35462f7349c5..6ad2b7fae68c 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_ffff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_ffff -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0 ;; @0040 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 @@ -47,13 +47,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = iconst.i64 0xffff_ffff ;; @0048 v5 = icmp ugt v2, v4 ; v4 = 0xffff_ffff -;; @0048 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = iconst.i64 0 ;; @0048 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 0cb1cd7f0c75..7c6595979680 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_efff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_efff -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = iconst.i64 0xffff_efff ;; @0049 v5 = icmp ugt v2, v4 ; v4 = 0xffff_efff -;; @0049 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 60ab0570cbdb..3a81bda9621f 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = iconst.i64 0xffff ;; @004c v5 = icmp ugt v2, v4 ; v4 = 0xffff -;; @004c v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat index 6bf2bbe9a16c..e195e8a26103 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_fffc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_fffc ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 store little heap v3, v7 ;; @0043 jump block1 @@ -46,14 +46,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = iconst.i64 0xffff_fffc ;; @0048 v5 = icmp ugt v2, v4 ; v4 = 0xffff_fffc ;; @0048 trapnz v5, heap_oob -;; @0048 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = load.i32 little heap v7 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index 98e530b2b1d2..b506eebdff61 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_effc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_effc ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = iconst.i64 0xffff_effc ;; @0049 v5 = icmp ugt v2, v4 ; v4 = 0xffff_effc ;; @0049 trapnz v5, heap_oob -;; @0049 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index 652f639a575a..7354c34240a5 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xfffc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xfffc ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = iconst.i64 0xfffc ;; @004c v5 = icmp ugt v2, v4 ; v4 = 0xfffc ;; @004c trapnz v5, heap_oob -;; @004c v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat index f32b3e707472..217467d3f726 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_ffff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_ffff ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 istore8 little heap v3, v7 ;; @0043 jump block1 @@ -46,14 +46,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = iconst.i64 0xffff_ffff ;; @0048 v5 = icmp ugt v2, v4 ; v4 = 0xffff_ffff ;; @0048 trapnz v5, heap_oob -;; @0048 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = uload8.i32 little heap v7 ;; @004b jump block1 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index 5a823e91455a..0cd432d10090 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_efff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_efff ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = iconst.i64 0xffff_efff ;; @0049 v5 = icmp ugt v2, v4 ; v4 = 0xffff_efff ;; @0049 trapnz v5, heap_oob -;; @0049 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index 525561563daf..fe094e9f920f 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -24,14 +24,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff ;; @0040 trapnz v5, heap_oob -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -48,14 +48,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = iconst.i64 0xffff ;; @004c v5 = icmp ugt v2, v4 ; v4 = 0xffff ;; @004c trapnz v5, heap_oob -;; @004c v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index 76ce54d057cd..7c3c116c2370 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_fffc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_fffc -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0 ;; @0040 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 @@ -47,13 +47,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = iconst.i64 0xffff_fffc ;; @0048 v5 = icmp ugt v2, v4 ; v4 = 0xffff_fffc -;; @0048 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = iconst.i64 0 ;; @0048 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index 873a01a79b51..486a2b9bf8c7 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_effc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_effc -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = iconst.i64 0xffff_effc ;; @0049 v5 = icmp ugt v2, v4 ; v4 = 0xffff_effc -;; @0049 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 263701d98ac3..39e73fbc4822 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xfffc ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xfffc -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = iconst.i64 0xfffc ;; @004c v5 = icmp ugt v2, v4 ; v4 = 0xfffc -;; @004c v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index 418e373477bd..3b45a00ed9cb 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_ffff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_ffff -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0 ;; @0040 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 @@ -47,13 +47,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0048 v4 = iconst.i64 0xffff_ffff ;; @0048 v5 = icmp ugt v2, v4 ; v4 = 0xffff_ffff -;; @0048 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0048 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0048 v7 = iadd v6, v2 ;; @0048 v8 = iconst.i64 0 ;; @0048 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index 42bac7b62757..ce5ffc31a845 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff_efff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff_efff -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 4096 ;; @0040 v9 = iadd v7, v8 ; v8 = 4096 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0049 v4 = iconst.i64 0xffff_efff ;; @0049 v5 = icmp ugt v2, v4 ; v4 = 0xffff_efff -;; @0049 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0049 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0049 v7 = iadd v6, v2 ;; @0049 v8 = iconst.i64 4096 ;; @0049 v9 = iadd v7, v8 ; v8 = 4096 diff --git a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 8554b9b22f30..d8431f063d98 100644 --- a/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/tests/disas/load-store/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -24,13 +24,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64, v3: i32): ;; @0040 v4 = iconst.i64 0xffff ;; @0040 v5 = icmp ugt v2, v4 ; v4 = 0xffff -;; @0040 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v7 = iadd v6, v2 ;; @0040 v8 = iconst.i64 0xffff_0000 ;; @0040 v9 = iadd v7, v8 ; v8 = 0xffff_0000 @@ -49,13 +49,13 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @004c v4 = iconst.i64 0xffff ;; @004c v5 = icmp ugt v2, v4 ; v4 = 0xffff -;; @004c v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004c v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @004c v7 = iadd v6, v2 ;; @004c v8 = iconst.i64 0xffff_0000 ;; @004c v9 = iadd v7, v8 ; v8 = 0xffff_0000 diff --git a/tests/disas/memory-min-max-same.wat b/tests/disas/memory-min-max-same.wat index fc4e33d929d8..d93d1e9d556c 100644 --- a/tests/disas/memory-min-max-same.wat +++ b/tests/disas/memory-min-max-same.wat @@ -39,7 +39,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; sig0 = (i64 vmctx, i64) tail ;; stack_limit = gv2 ;; @@ -49,7 +49,7 @@ ;; @0030 v6 = load.i64 notrap aligned readonly can_move v0+88 ;; @0039 v13 = iconst.i64 0x0001_0000 ;; @0039 v17 = iconst.i64 0 -;; @0039 v15 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0039 v15 = load.i64 notrap aligned readonly can_move v0+56 ;; @003e v19 = iconst.i32 1 ;; @002e jump block2(v3) ; v3 = 0 ;; diff --git a/tests/disas/memory.wat b/tests/disas/memory.wat index 802200ad2c5e..419adb5a42cb 100644 --- a/tests/disas/memory.wat +++ b/tests/disas/memory.wat @@ -18,7 +18,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64): @@ -26,12 +26,12 @@ ;; @0021 v3 = iconst.i32 0 ;; @0023 v4 = iconst.i32 0 ;; @0025 v5 = uextend.i64 v3 ; v3 = 0 -;; @0025 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0025 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0025 v7 = iadd v6, v5 ;; @0025 store little heap v4, v7 ; v4 = 0 ;; @0028 v8 = iconst.i32 0 ;; @002a v9 = uextend.i64 v8 ; v8 = 0 -;; @002a v10 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @002a v10 = load.i64 notrap aligned readonly can_move v0+56 ;; @002a v11 = iadd v10, v9 ;; @002a v12 = load.i32 little heap v11 ;; @002d brif v12, block2, block4 @@ -40,7 +40,7 @@ ;; @002f v13 = iconst.i32 0 ;; @0031 v14 = iconst.i32 10 ;; @0033 v15 = uextend.i64 v13 ; v13 = 0 -;; @0033 v16 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0033 v16 = load.i64 notrap aligned readonly can_move v0+56 ;; @0033 v17 = iadd v16, v15 ;; @0033 store little heap v14, v17 ; v14 = 10 ;; @0036 jump block3 @@ -49,7 +49,7 @@ ;; @0037 v18 = iconst.i32 0 ;; @0039 v19 = iconst.i32 11 ;; @003b v20 = uextend.i64 v18 ; v18 = 0 -;; @003b v21 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @003b v21 = load.i64 notrap aligned readonly can_move v0+56 ;; @003b v22 = iadd v21, v20 ;; @003b store little heap v19, v22 ; v19 = 11 ;; @003e jump block3 diff --git a/tests/disas/non-fixed-size-memory.wat b/tests/disas/non-fixed-size-memory.wat index a21e2a9539d5..3c15e1b2e5c3 100644 --- a/tests/disas/non-fixed-size-memory.wat +++ b/tests/disas/non-fixed-size-memory.wat @@ -26,7 +26,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32, v3: i32): @@ -34,7 +34,7 @@ ;; @0041 v5 = load.i64 notrap aligned v0+64 ;; @0041 v6 = icmp uge v4, v5 ;; @0041 trapnz v6, heap_oob -;; @0041 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0041 v7 = load.i64 notrap aligned can_move v0+56 ;; @0041 v8 = iadd v7, v4 ;; @0041 istore8 little heap v3, v8 ;; @0044 jump block1 @@ -49,7 +49,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -57,7 +57,7 @@ ;; @0049 v5 = load.i64 notrap aligned v0+64 ;; @0049 v6 = icmp uge v4, v5 ;; @0049 trapnz v6, heap_oob -;; @0049 v7 = load.i64 notrap aligned can_move checked v0+56 +;; @0049 v7 = load.i64 notrap aligned can_move v0+56 ;; @0049 v8 = iadd v7, v4 ;; @0049 v9 = uload8.i32 little heap v8 ;; @004c jump block1 diff --git a/tests/disas/passive-data.wat b/tests/disas/passive-data.wat index 576e6f408d56..da2c54727841 100644 --- a/tests/disas/passive-data.wat +++ b/tests/disas/passive-data.wat @@ -19,7 +19,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; sig0 = (i64 vmctx, i32, i32, i64, i32, i32) -> i8 tail ;; fn0 = colocated u805306368:6 sig0 ;; stack_limit = gv2 diff --git a/tests/disas/pcc-imported-memory.wat b/tests/disas/pcc-imported-memory.wat deleted file mode 100644 index 564422192f62..000000000000 --- a/tests/disas/pcc-imported-memory.wat +++ /dev/null @@ -1,58 +0,0 @@ -;;! target = "x86_64" -;;! test = "compile" -;;! flags = [ "-Oopt-level=2", "-Cpcc=y" ] - -(module - (type (;0;) (func)) - (import "" "" (memory (;0;) 1)) - (func (;0;) (type 0) - (local i32 i32) - memory.size - local.set 0 - block ;; label = @1 - block ;; label = @2 - memory.size - i32.const 65536 - i32.mul - i32.const 4 - local.get 0 - i32.add - i32.le_u - br_if 0 (;@2;) - local.get 0 - i32.const 0 - i32.le_s - br_if 0 (;@2;) - local.get 0 - i32.load align=1 - local.set 1 - br 1 (;@1;) - end - i32.const 0 - local.set 1 - end - local.get 1 - drop)) - -;; wasm[0]::function[0]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movq 0x30(%rdi), %rax -;; movq 8(%rax), %rax -;; shrq $0x10, %rax -;; movq %rax, %rcx -;; shll $0x10, %ecx -;; leal 4(%rax), %edx -;; cmpl %edx, %ecx -;; jbe 0x3a -;; 21: testl %eax, %eax -;; jle 0x3a -;; 29: movq 0x30(%rdi), %rcx -;; movq (%rcx), %rcx -;; movl %eax, %eax -;; movl (%rcx, %rax), %eax -;; jmp 0x3c -;; 3a: xorl %eax, %eax -;; movq %rbp, %rsp -;; popq %rbp -;; retq diff --git a/tests/disas/pcc-insertlane-x64-avx.wat b/tests/disas/pcc-insertlane-x64-avx.wat deleted file mode 100644 index b57ffa38f044..000000000000 --- a/tests/disas/pcc-insertlane-x64-avx.wat +++ /dev/null @@ -1,233 +0,0 @@ -;;! target = "x86_64" -;;! test = "compile" -;;! flags = [ "-Oopt-level=0", "-Cpcc=y", "-Ccranelift-has-sse41=true", "-Ccranelift-has-avx=true" ] - -(module - (memory 1 1) - (func (param i32) (result v128) - local.get 0 - v128.const i32x4 0x29292928 0x206e6928 0x616d286d 0x206f7263 - v128.load8_lane align=1 1) - (func (param i32) (result v128) - local.get 0 - v128.const i32x4 0x29292928 0x206e6928 0x616d286d 0x206f7263 - v128.load16_lane align=1 1) - (func (param i32) (result v128) - local.get 0 - v128.const i32x4 0x29292928 0x206e6928 0x616d286d 0x206f7263 - v128.load32_lane align=1 1) - (func (param i32) (result v128) - local.get 0 - v128.const i32x4 0x29292928 0x206e6928 0x616d286d 0x206f7263 - v128.load64_lane align=1 1) - (func (param v128 i32) (result v128) - local.get 0 - local.get 1 - f32.load - f32x4.replace_lane 0) - (func (param v128 i32) (result v128) - local.get 0 - local.get 1 - f64.load - f64x2.replace_lane 1) - (func (param v128 i32) (result v128) - local.get 0 - local.get 1 - f64.load - f64x2.replace_lane 0) - (func (param v128 i32) - local.get 1 - local.get 0 - f64x2.extract_lane 1 - f64.store) - (func (param v128 i32) - local.get 1 - local.get 0 - f32x4.extract_lane 1 - f32.store) - (func (param v128 i32) - local.get 1 - local.get 0 - i8x16.extract_lane_s 1 - i32.store8) - (func (param v128 i32) - local.get 1 - local.get 0 - i16x8.extract_lane_s 1 - i32.store16) - (func (param v128 i32) - local.get 1 - local.get 0 - i32x4.extract_lane 1 - i32.store) - (func (param v128 i32) - local.get 1 - local.get 0 - i64x2.extract_lane 1 - i64.store)) -;; wasm[0]::function[0]: -;; pushq %rbp -;; movq %rsp, %rbp -;; vmovdqu 0x14(%rip), %xmm6 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; vpinsrb $1, (%rdi, %rsi), %xmm6, %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; 1e: addb %al, (%rax) -;; 20: subb %ch, (%rcx) -;; 22: subl %ebp, (%rcx) -;; 24: subb %ch, 0x6e(%rcx) -;; 27: andb %ch, 0x28(%rbp) -;; 2a: insl %dx, (%rdi) -;; -;; wasm[0]::function[1]: -;; pushq %rbp -;; movq %rsp, %rbp -;; vmovdqu 0x14(%rip), %xmm6 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; vpinsrw $1, (%rdi, %rsi), %xmm6, %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; 5d: addb %al, (%rax) -;; 5f: addb %ch, (%rax) -;; 61: subl %ebp, (%rcx) -;; 63: subl %ebp, (%rax) -;; 65: imull $0x616d286d, 0x20(%rsi), %ebp -;; -;; wasm[0]::function[2]: -;; pushq %rbp -;; movq %rsp, %rbp -;; vmovdqu 0x14(%rip), %xmm6 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; vpinsrd $1, (%rdi, %rsi), %xmm6, %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; 9e: addb %al, (%rax) -;; a0: subb %ch, (%rcx) -;; a2: subl %ebp, (%rcx) -;; a4: subb %ch, 0x6e(%rcx) -;; a7: andb %ch, 0x28(%rbp) -;; aa: insl %dx, (%rdi) -;; -;; wasm[0]::function[3]: -;; pushq %rbp -;; movq %rsp, %rbp -;; vmovdqu 0x14(%rip), %xmm6 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; vpinsrq $1, (%rdi, %rsi), %xmm6, %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; de: addb %al, (%rax) -;; e0: subb %ch, (%rcx) -;; e2: subl %ebp, (%rcx) -;; e4: subb %ch, 0x6e(%rcx) -;; e7: andb %ch, 0x28(%rbp) -;; ea: insl %dx, (%rdi) -;; -;; wasm[0]::function[4]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; vinsertps $0, (%rdi, %rsi), %xmm0, %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[5]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movq %rdi, %r9 -;; movl %edx, %edi -;; movq 0x38(%r9), %r8 -;; vmovsd (%r8, %rdi), %xmm7 -;; vmovlhps %xmm7, %xmm0, %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[6]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movq %rdi, %r9 -;; movl %edx, %edi -;; movq 0x38(%r9), %r8 -;; vmovsd (%r8, %rdi), %xmm7 -;; vmovsd %xmm7, %xmm0, %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[7]: -;; pushq %rbp -;; movq %rsp, %rbp -;; vpshufd $0xee, %xmm0, %xmm6 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; vmovsd %xmm6, (%rdi, %rsi) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[8]: -;; pushq %rbp -;; movq %rsp, %rbp -;; vpshufd $1, %xmm0, %xmm6 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; vmovss %xmm6, (%rdi, %rsi) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[9]: -;; pushq %rbp -;; movq %rsp, %rbp -;; vpextrb $1, %xmm0, %r8d -;; movsbl %r8b, %r8d -;; movl %edx, %r9d -;; movq 0x38(%rdi), %rdi -;; movb %r8b, (%rdi, %r9) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[10]: -;; pushq %rbp -;; movq %rsp, %rbp -;; vpextrw $1, %xmm0, %r8d -;; movswl %r8w, %r8d -;; movl %edx, %r9d -;; movq 0x38(%rdi), %rdi -;; movw %r8w, (%rdi, %r9) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[11]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; vpextrd $1, %xmm0, (%rdi, %rsi) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[12]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; vpextrq $1, %xmm0, (%rdi, %rsi) -;; movq %rbp, %rsp -;; popq %rbp -;; retq diff --git a/tests/disas/pcc-insertlane-x64.wat b/tests/disas/pcc-insertlane-x64.wat deleted file mode 100644 index fb4dc8babc23..000000000000 --- a/tests/disas/pcc-insertlane-x64.wat +++ /dev/null @@ -1,231 +0,0 @@ -;;! target = "x86_64" -;;! test = "compile" -;;! flags = [ "-Oopt-level=0", "-Cpcc=y", "-Ccranelift-has-sse41=true", "-Ccranelift-has-avx=false" ] - -(module - (memory 1 1) - (func (param i32) (result v128) - local.get 0 - v128.const i32x4 0x29292928 0x206e6928 0x616d286d 0x206f7263 - v128.load8_lane align=1 1) - (func (param i32) (result v128) - local.get 0 - v128.const i32x4 0x29292928 0x206e6928 0x616d286d 0x206f7263 - v128.load16_lane align=1 1) - (func (param i32) (result v128) - local.get 0 - v128.const i32x4 0x29292928 0x206e6928 0x616d286d 0x206f7263 - v128.load32_lane align=1 1) - (func (param i32) (result v128) - local.get 0 - v128.const i32x4 0x29292928 0x206e6928 0x616d286d 0x206f7263 - v128.load64_lane align=1 1) - (func (param v128 i32) (result v128) - local.get 0 - local.get 1 - f32.load - f32x4.replace_lane 0) - (func (param v128 i32) (result v128) - local.get 0 - local.get 1 - f64.load - f64x2.replace_lane 1) - (func (param v128 i32) (result v128) - local.get 0 - local.get 1 - f64.load - f64x2.replace_lane 0) - (func (param v128 i32) - local.get 1 - local.get 0 - f64x2.extract_lane 1 - f64.store) - (func (param v128 i32) - local.get 1 - local.get 0 - f32x4.extract_lane 1 - f32.store) - (func (param v128 i32) - local.get 1 - local.get 0 - i8x16.extract_lane_s 1 - i32.store8) - (func (param v128 i32) - local.get 1 - local.get 0 - i16x8.extract_lane_s 1 - i32.store16) - (func (param v128 i32) - local.get 1 - local.get 0 - i32x4.extract_lane 1 - i32.store) - (func (param v128 i32) - local.get 1 - local.get 0 - i64x2.extract_lane 1 - i64.store)) -;; wasm[0]::function[0]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movdqu 0x14(%rip), %xmm0 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; pinsrb $1, (%rdi, %rsi), %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; 1e: addb %al, (%rax) -;; 20: subb %ch, (%rcx) -;; 22: subl %ebp, (%rcx) -;; 24: subb %ch, 0x6e(%rcx) -;; 27: andb %ch, 0x28(%rbp) -;; 2a: insl %dx, (%rdi) -;; -;; wasm[0]::function[1]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movdqu 0x14(%rip), %xmm0 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; pinsrw $1, (%rdi, %rsi), %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; 5d: addb %al, (%rax) -;; 5f: addb %ch, (%rax) -;; 61: subl %ebp, (%rcx) -;; 63: subl %ebp, (%rax) -;; 65: imull $0x616d286d, 0x20(%rsi), %ebp -;; -;; wasm[0]::function[2]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movdqu 0x14(%rip), %xmm0 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; pinsrd $1, (%rdi, %rsi), %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; 9e: addb %al, (%rax) -;; a0: subb %ch, (%rcx) -;; a2: subl %ebp, (%rcx) -;; a4: subb %ch, 0x6e(%rcx) -;; a7: andb %ch, 0x28(%rbp) -;; aa: insl %dx, (%rdi) -;; -;; wasm[0]::function[3]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movdqu 0x14(%rip), %xmm0 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; pinsrq $1, (%rdi, %rsi), %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; df: addb %ch, (%rax) -;; e1: subl %ebp, (%rcx) -;; e3: subl %ebp, (%rax) -;; e5: imull $0x616d286d, 0x20(%rsi), %ebp -;; -;; wasm[0]::function[4]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; insertps $0, (%rdi, %rsi), %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[5]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movq %rdi, %r9 -;; movl %edx, %edi -;; movq 0x38(%r9), %r8 -;; movsd (%r8, %rdi), %xmm7 -;; movlhps %xmm7, %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[6]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movq %rdi, %r9 -;; movl %edx, %edi -;; movq 0x38(%r9), %r8 -;; movsd (%r8, %rdi), %xmm7 -;; movsd %xmm7, %xmm0 -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[7]: -;; pushq %rbp -;; movq %rsp, %rbp -;; pshufd $0xee, %xmm0, %xmm6 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; movsd %xmm6, (%rdi, %rsi) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[8]: -;; pushq %rbp -;; movq %rsp, %rbp -;; pshufd $1, %xmm0, %xmm6 -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; movss %xmm6, (%rdi, %rsi) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[9]: -;; pushq %rbp -;; movq %rsp, %rbp -;; pextrb $1, %xmm0, %r8d -;; movsbl %r8b, %r8d -;; movl %edx, %r9d -;; movq 0x38(%rdi), %rdi -;; movb %r8b, (%rdi, %r9) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[10]: -;; pushq %rbp -;; movq %rsp, %rbp -;; pextrw $1, %xmm0, %r8d -;; movswl %r8w, %r8d -;; movl %edx, %r9d -;; movq 0x38(%rdi), %rdi -;; movw %r8w, (%rdi, %r9) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[11]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; pextrd $1, %xmm0, (%rdi, %rsi) -;; movq %rbp, %rsp -;; popq %rbp -;; retq -;; -;; wasm[0]::function[12]: -;; pushq %rbp -;; movq %rsp, %rbp -;; movl %edx, %esi -;; movq 0x38(%rdi), %rdi -;; pextrq $1, %xmm0, (%rdi, %rsi) -;; movq %rbp, %rsp -;; popq %rbp -;; retq diff --git a/tests/disas/pr2303.wat b/tests/disas/pr2303.wat index 28d43a518096..cdb69eca6565 100644 --- a/tests/disas/pr2303.wat +++ b/tests/disas/pr2303.wat @@ -22,19 +22,19 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): ;; @0036 v3 = iconst.i32 48 ;; @0038 v4 = iconst.i32 0 ;; @003a v5 = uextend.i64 v4 ; v4 = 0 -;; @003a v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @003a v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @003a v7 = iadd v6, v5 ;; @003a v8 = load.i8x16 little heap v7 ;; @003e v9 = iconst.i32 16 ;; @0040 v10 = uextend.i64 v9 ; v9 = 16 -;; @0040 v11 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0040 v11 = load.i64 notrap aligned readonly can_move v0+56 ;; @0040 v12 = iadd v11, v10 ;; @0040 v13 = load.i8x16 little heap v12 ;; @0046 brif v2, block2, block4 @@ -45,7 +45,7 @@ ;; @0048 v18 = iadd v16, v17 ;; @004b v19 = iconst.i32 32 ;; @004d v20 = uextend.i64 v19 ; v19 = 32 -;; @004d v21 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @004d v21 = load.i64 notrap aligned readonly can_move v0+56 ;; @004d v22 = iadd v21, v20 ;; @004d v23 = load.i8x16 little heap v22 ;; @0051 v26 = bitcast.i8x16 little v18 @@ -57,7 +57,7 @@ ;; @0052 v29 = isub v27, v28 ;; @0055 v30 = iconst.i32 0 ;; @0057 v31 = uextend.i64 v30 ; v30 = 0 -;; @0057 v32 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0057 v32 = load.i64 notrap aligned readonly can_move v0+56 ;; @0057 v33 = iadd v32, v31 ;; @0057 v34 = load.i8x16 little heap v33 ;; @005b v35 = bitcast.i8x16 little v29 @@ -68,7 +68,7 @@ ;; @005c v37 = bitcast.i16x8 little v15 ;; @005c v38 = imul v36, v37 ;; @005f v39 = uextend.i64 v3 ; v3 = 48 -;; @005f v40 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @005f v40 = load.i64 notrap aligned readonly can_move v0+56 ;; @005f v41 = iadd v40, v39 ;; @005f store little heap v38, v41 ;; @0063 jump block1 diff --git a/tests/disas/readonly-heap-base-pointer1.wat b/tests/disas/readonly-heap-base-pointer1.wat index a5b928417b91..d6f608c00058 100644 --- a/tests/disas/readonly-heap-base-pointer1.wat +++ b/tests/disas/readonly-heap-base-pointer1.wat @@ -13,7 +13,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -21,7 +21,7 @@ ;; @0020 v5 = iconst.i64 0x0001_fffc ;; @0020 v6 = icmp ugt v4, v5 ; v5 = 0x0001_fffc ;; @0020 v9 = iconst.i64 0 -;; @0020 v7 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0020 v7 = load.i64 notrap aligned readonly can_move v0+56 ;; @0020 v8 = iadd v7, v4 ;; @0020 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 ;; @0020 v11 = load.i32 little heap v10 diff --git a/tests/disas/readonly-heap-base-pointer2.wat b/tests/disas/readonly-heap-base-pointer2.wat index 2c58fd0cb7b8..5ea52e8e66f1 100644 --- a/tests/disas/readonly-heap-base-pointer2.wat +++ b/tests/disas/readonly-heap-base-pointer2.wat @@ -14,7 +14,7 @@ ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned readonly can_move gv3+48 ;; gv5 = load.i64 notrap aligned gv4+8 -;; gv6 = load.i64 notrap aligned readonly can_move checked gv4 +;; gv6 = load.i64 notrap aligned readonly can_move gv4 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): @@ -23,7 +23,7 @@ ;; @0022 v6 = icmp ugt v4, v5 ; v5 = 0x0001_fffc ;; @0022 v9 = iconst.i64 0 ;; @0022 v12 = load.i64 notrap aligned readonly can_move v0+48 -;; @0022 v7 = load.i64 notrap aligned readonly can_move checked v12 +;; @0022 v7 = load.i64 notrap aligned readonly can_move v12 ;; @0022 v8 = iadd v7, v4 ;; @0022 v10 = select_spectre_guard v6, v9, v8 ; v9 = 0 ;; @0022 v11 = load.i32 little heap v10 diff --git a/tests/disas/readonly-heap-base-pointer3.wat b/tests/disas/readonly-heap-base-pointer3.wat index 71f250663dc0..9cd51525f07d 100644 --- a/tests/disas/readonly-heap-base-pointer3.wat +++ b/tests/disas/readonly-heap-base-pointer3.wat @@ -13,14 +13,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i64): ;; @0020 v4 = iconst.i64 0xffff_fffc ;; @0020 v5 = icmp ugt v2, v4 ; v4 = 0xffff_fffc ;; @0020 v8 = iconst.i64 0 -;; @0020 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0020 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0020 v7 = iadd v6, v2 ;; @0020 v9 = select_spectre_guard v5, v8, v7 ; v8 = 0 ;; @0020 v10 = load.i32 little heap v9 diff --git a/tests/disas/simd-store.wat b/tests/disas/simd-store.wat index 14fee31d9f5c..2ffbc46fbf29 100644 --- a/tests/disas/simd-store.wat +++ b/tests/disas/simd-store.wat @@ -90,14 +90,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): ;; @003f v3 = iconst.i32 0 ;; @0045 v4 = icmp eq v2, v2 ;; @0047 v5 = uextend.i64 v3 ; v3 = 0 -;; @0047 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0047 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0047 v7 = iadd v6, v5 ;; @0047 store little heap v4, v7 ;; @004b jump block1 @@ -112,7 +112,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -121,7 +121,7 @@ ;; @00dd v5 = bitcast.i32x4 little v2 ;; @00dd v6 = icmp slt v4, v5 ;; @00df v7 = uextend.i64 v3 ; v3 = 0 -;; @00df v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @00df v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @00df v9 = iadd v8, v7 ;; @00df store little heap v6, v9 ;; @00e3 jump block1 @@ -136,7 +136,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -145,7 +145,7 @@ ;; @00ec v5 = bitcast.i64x2 little v2 ;; @00ec v6 = icmp slt v4, v5 ;; @00ef v7 = uextend.i64 v3 ; v3 = 0 -;; @00ef v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @00ef v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @00ef v9 = iadd v8, v7 ;; @00ef store little heap v6, v9 ;; @00f3 jump block1 @@ -160,14 +160,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): ;; @00f6 v3 = iconst.i32 0 ;; @00fc v4 = icmp ult v2, v2 ;; @00fe v5 = uextend.i64 v3 ; v3 = 0 -;; @00fe v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @00fe v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @00fe v7 = iadd v6, v5 ;; @00fe store little heap v4, v7 ;; @0102 jump block1 @@ -182,7 +182,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -191,7 +191,7 @@ ;; @010b v5 = bitcast.i16x8 little v2 ;; @010b v6 = icmp ult v4, v5 ;; @010d v7 = uextend.i64 v3 ; v3 = 0 -;; @010d v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @010d v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @010d v9 = iadd v8, v7 ;; @010d store little heap v6, v9 ;; @0111 jump block1 @@ -206,7 +206,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -215,7 +215,7 @@ ;; @011a v5 = bitcast.i32x4 little v2 ;; @011a v6 = icmp ult v4, v5 ;; @011c v7 = uextend.i64 v3 ; v3 = 0 -;; @011c v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @011c v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @011c v9 = iadd v8, v7 ;; @011c store little heap v6, v9 ;; @0120 jump block1 @@ -230,14 +230,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): ;; @0123 v3 = iconst.i32 0 ;; @0129 v4 = icmp sgt v2, v2 ;; @012b v5 = uextend.i64 v3 ; v3 = 0 -;; @012b v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @012b v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @012b v7 = iadd v6, v5 ;; @012b store little heap v4, v7 ;; @012f jump block1 @@ -252,7 +252,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -261,7 +261,7 @@ ;; @0138 v5 = bitcast.i16x8 little v2 ;; @0138 v6 = icmp sgt v4, v5 ;; @013a v7 = uextend.i64 v3 ; v3 = 0 -;; @013a v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @013a v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @013a v9 = iadd v8, v7 ;; @013a store little heap v6, v9 ;; @013e jump block1 @@ -276,7 +276,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -285,7 +285,7 @@ ;; @0147 v5 = bitcast.i32x4 little v2 ;; @0147 v6 = icmp sgt v4, v5 ;; @0149 v7 = uextend.i64 v3 ; v3 = 0 -;; @0149 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0149 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0149 v9 = iadd v8, v7 ;; @0149 store little heap v6, v9 ;; @014d jump block1 @@ -300,7 +300,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -309,7 +309,7 @@ ;; @0156 v5 = bitcast.i64x2 little v2 ;; @0156 v6 = icmp sgt v4, v5 ;; @0159 v7 = uextend.i64 v3 ; v3 = 0 -;; @0159 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0159 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0159 v9 = iadd v8, v7 ;; @0159 store little heap v6, v9 ;; @015d jump block1 @@ -324,14 +324,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): ;; @0160 v3 = iconst.i32 0 ;; @0166 v4 = icmp ugt v2, v2 ;; @0168 v5 = uextend.i64 v3 ; v3 = 0 -;; @0168 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0168 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0168 v7 = iadd v6, v5 ;; @0168 store little heap v4, v7 ;; @016c jump block1 @@ -346,7 +346,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -355,7 +355,7 @@ ;; @0054 v5 = bitcast.i16x8 little v2 ;; @0054 v6 = icmp eq v4, v5 ;; @0056 v7 = uextend.i64 v3 ; v3 = 0 -;; @0056 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0056 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0056 v9 = iadd v8, v7 ;; @0056 store little heap v6, v9 ;; @005a jump block1 @@ -370,7 +370,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -379,7 +379,7 @@ ;; @0175 v5 = bitcast.i16x8 little v2 ;; @0175 v6 = icmp ugt v4, v5 ;; @0177 v7 = uextend.i64 v3 ; v3 = 0 -;; @0177 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0177 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0177 v9 = iadd v8, v7 ;; @0177 store little heap v6, v9 ;; @017b jump block1 @@ -394,7 +394,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -403,7 +403,7 @@ ;; @0184 v5 = bitcast.i32x4 little v2 ;; @0184 v6 = icmp ugt v4, v5 ;; @0186 v7 = uextend.i64 v3 ; v3 = 0 -;; @0186 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0186 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0186 v9 = iadd v8, v7 ;; @0186 store little heap v6, v9 ;; @018a jump block1 @@ -418,7 +418,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -427,7 +427,7 @@ ;; @0193 v5 = bitcast.f32x4 little v2 ;; @0193 v6 = fcmp eq v4, v5 ;; @0195 v7 = uextend.i64 v3 ; v3 = 0 -;; @0195 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0195 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0195 v9 = iadd v8, v7 ;; @0195 store little heap v6, v9 ;; @0199 jump block1 @@ -442,7 +442,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -451,7 +451,7 @@ ;; @01a2 v5 = bitcast.f64x2 little v2 ;; @01a2 v6 = fcmp eq v4, v5 ;; @01a4 v7 = uextend.i64 v3 ; v3 = 0 -;; @01a4 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @01a4 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @01a4 v9 = iadd v8, v7 ;; @01a4 store little heap v6, v9 ;; @01a8 jump block1 @@ -466,7 +466,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -475,7 +475,7 @@ ;; @01b1 v5 = bitcast.f32x4 little v2 ;; @01b1 v6 = fcmp ne v4, v5 ;; @01b3 v7 = uextend.i64 v3 ; v3 = 0 -;; @01b3 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @01b3 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @01b3 v9 = iadd v8, v7 ;; @01b3 store little heap v6, v9 ;; @01b7 jump block1 @@ -490,7 +490,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -499,7 +499,7 @@ ;; @01c0 v5 = bitcast.f64x2 little v2 ;; @01c0 v6 = fcmp ne v4, v5 ;; @01c2 v7 = uextend.i64 v3 ; v3 = 0 -;; @01c2 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @01c2 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @01c2 v9 = iadd v8, v7 ;; @01c2 store little heap v6, v9 ;; @01c6 jump block1 @@ -514,7 +514,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -523,7 +523,7 @@ ;; @01cf v5 = bitcast.f32x4 little v2 ;; @01cf v6 = fcmp lt v4, v5 ;; @01d1 v7 = uextend.i64 v3 ; v3 = 0 -;; @01d1 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @01d1 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @01d1 v9 = iadd v8, v7 ;; @01d1 store little heap v6, v9 ;; @01d5 jump block1 @@ -538,7 +538,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -547,7 +547,7 @@ ;; @01de v5 = bitcast.f64x2 little v2 ;; @01de v6 = fcmp lt v4, v5 ;; @01e0 v7 = uextend.i64 v3 ; v3 = 0 -;; @01e0 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @01e0 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @01e0 v9 = iadd v8, v7 ;; @01e0 store little heap v6, v9 ;; @01e4 jump block1 @@ -562,7 +562,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -571,7 +571,7 @@ ;; @01ed v5 = bitcast.f32x4 little v2 ;; @01ed v6 = fcmp le v4, v5 ;; @01ef v7 = uextend.i64 v3 ; v3 = 0 -;; @01ef v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @01ef v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @01ef v9 = iadd v8, v7 ;; @01ef store little heap v6, v9 ;; @01f3 jump block1 @@ -586,7 +586,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -595,7 +595,7 @@ ;; @01fc v5 = bitcast.f64x2 little v2 ;; @01fc v6 = fcmp le v4, v5 ;; @01fe v7 = uextend.i64 v3 ; v3 = 0 -;; @01fe v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @01fe v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @01fe v9 = iadd v8, v7 ;; @01fe store little heap v6, v9 ;; @0202 jump block1 @@ -610,7 +610,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -619,7 +619,7 @@ ;; @0063 v5 = bitcast.i32x4 little v2 ;; @0063 v6 = icmp eq v4, v5 ;; @0065 v7 = uextend.i64 v3 ; v3 = 0 -;; @0065 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0065 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0065 v9 = iadd v8, v7 ;; @0065 store little heap v6, v9 ;; @0069 jump block1 @@ -634,7 +634,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -643,7 +643,7 @@ ;; @020b v5 = bitcast.f32x4 little v2 ;; @020b v6 = fcmp gt v4, v5 ;; @020d v7 = uextend.i64 v3 ; v3 = 0 -;; @020d v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @020d v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @020d v9 = iadd v8, v7 ;; @020d store little heap v6, v9 ;; @0211 jump block1 @@ -658,7 +658,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -667,7 +667,7 @@ ;; @021a v5 = bitcast.f64x2 little v2 ;; @021a v6 = fcmp gt v4, v5 ;; @021c v7 = uextend.i64 v3 ; v3 = 0 -;; @021c v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @021c v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @021c v9 = iadd v8, v7 ;; @021c store little heap v6, v9 ;; @0220 jump block1 @@ -682,7 +682,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -691,7 +691,7 @@ ;; @0229 v5 = bitcast.f32x4 little v2 ;; @0229 v6 = fcmp ge v4, v5 ;; @022b v7 = uextend.i64 v3 ; v3 = 0 -;; @022b v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @022b v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @022b v9 = iadd v8, v7 ;; @022b store little heap v6, v9 ;; @022f jump block1 @@ -706,7 +706,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -715,7 +715,7 @@ ;; @0238 v5 = bitcast.f64x2 little v2 ;; @0238 v6 = fcmp ge v4, v5 ;; @023a v7 = uextend.i64 v3 ; v3 = 0 -;; @023a v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @023a v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @023a v9 = iadd v8, v7 ;; @023a store little heap v6, v9 ;; @023e jump block1 @@ -730,7 +730,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -739,7 +739,7 @@ ;; @0072 v5 = bitcast.i64x2 little v2 ;; @0072 v6 = icmp eq v4, v5 ;; @0075 v7 = uextend.i64 v3 ; v3 = 0 -;; @0075 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0075 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0075 v9 = iadd v8, v7 ;; @0075 store little heap v6, v9 ;; @0079 jump block1 @@ -754,14 +754,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): ;; @007c v3 = iconst.i32 0 ;; @0082 v4 = icmp ne v2, v2 ;; @0084 v5 = uextend.i64 v3 ; v3 = 0 -;; @0084 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0084 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @0084 v7 = iadd v6, v5 ;; @0084 store little heap v4, v7 ;; @0088 jump block1 @@ -776,7 +776,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -785,7 +785,7 @@ ;; @0091 v5 = bitcast.i16x8 little v2 ;; @0091 v6 = icmp ne v4, v5 ;; @0093 v7 = uextend.i64 v3 ; v3 = 0 -;; @0093 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @0093 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @0093 v9 = iadd v8, v7 ;; @0093 store little heap v6, v9 ;; @0097 jump block1 @@ -800,7 +800,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -809,7 +809,7 @@ ;; @00a0 v5 = bitcast.i32x4 little v2 ;; @00a0 v6 = icmp ne v4, v5 ;; @00a2 v7 = uextend.i64 v3 ; v3 = 0 -;; @00a2 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @00a2 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @00a2 v9 = iadd v8, v7 ;; @00a2 store little heap v6, v9 ;; @00a6 jump block1 @@ -824,7 +824,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -833,7 +833,7 @@ ;; @00af v5 = bitcast.i64x2 little v2 ;; @00af v6 = icmp ne v4, v5 ;; @00b2 v7 = uextend.i64 v3 ; v3 = 0 -;; @00b2 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @00b2 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @00b2 v9 = iadd v8, v7 ;; @00b2 store little heap v6, v9 ;; @00b6 jump block1 @@ -848,14 +848,14 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): ;; @00b9 v3 = iconst.i32 0 ;; @00bf v4 = icmp slt v2, v2 ;; @00c1 v5 = uextend.i64 v3 ; v3 = 0 -;; @00c1 v6 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @00c1 v6 = load.i64 notrap aligned readonly can_move v0+56 ;; @00c1 v7 = iadd v6, v5 ;; @00c1 store little heap v4, v7 ;; @00c5 jump block1 @@ -870,7 +870,7 @@ ;; gv2 = load.i64 notrap aligned gv1+24 ;; gv3 = vmctx ;; gv4 = load.i64 notrap aligned gv3+64 -;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+56 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+56 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i8x16): @@ -879,7 +879,7 @@ ;; @00ce v5 = bitcast.i16x8 little v2 ;; @00ce v6 = icmp slt v4, v5 ;; @00d0 v7 = uextend.i64 v3 ; v3 = 0 -;; @00d0 v8 = load.i64 notrap aligned readonly can_move checked v0+56 +;; @00d0 v8 = load.i64 notrap aligned readonly can_move v0+56 ;; @00d0 v9 = iadd v8, v7 ;; @00d0 store little heap v6, v9 ;; @00d4 jump block1 diff --git a/tests/disas/stack-switching/resume-suspend-data-passing.wat b/tests/disas/stack-switching/resume-suspend-data-passing.wat index 4ab1455b7e5b..fa1472c2e465 100644 --- a/tests/disas/stack-switching/resume-suspend-data-passing.wat +++ b/tests/disas/stack-switching/resume-suspend-data-passing.wat @@ -11,7 +11,7 @@ (local $i i32) (local.set $i (i32.const 10)) (loop $loop - ;; suspend and pass countdown to our cosnumer + ;; suspend and pass countdown to our consumer (suspend $t (local.get $i)) ;; decrement i; break if we're at 0 (local.tee $i (i32.sub (local.get $i) (i32.const 1))) diff --git a/tests/disas/winch/aarch64/br_table/large.wat b/tests/disas/winch/aarch64/br_table/large.wat index d6112bd1c569..c83e5a99bbb9 100644 --- a/tests/disas/winch/aarch64/br_table/large.wat +++ b/tests/disas/winch/aarch64/br_table/large.wat @@ -749,7 +749,7 @@ ;; movk x17, #0x18 ;; add x16, x16, x17 ;; cmp sp, x16 -;; b.lo #0x1812c +;; b.lo #0x18128 ;; 2c: mov x9, x0 ;; sub x28, x28, #0x18 ;; mov sp, x28 @@ -759,14 +759,13 @@ ;; ldur w0, [x28, #4] ;; mov x1, #0x6027 ;; cmp w0, w1, uxtx -;; b.hs #0x18110 +;; b.hs #0x1810c ;; 54: csel x1, xzr, x0, hs -;; csdb -;; adr x16, #0x6c +;; adr x16, #0x68 ;; ldrsw x1, [x16, w1, uxtw #2] ;; add x16, x16, x1 ;; br x16 -;; 6c: .byte 0x9c, 0x80, 0x01, 0x00 +;; 68: .byte 0x9c, 0x80, 0x01, 0x00 ;; .byte 0xa4, 0x80, 0x01, 0x00 ;; .byte 0x9c, 0x80, 0x01, 0x00 ;; .byte 0xa4, 0x80, 0x01, 0x00 @@ -25382,12 +25381,12 @@ ;; .byte 0xa4, 0x80, 0x01, 0x00 ;; .byte 0x9c, 0x80, 0x01, 0x00 ;; mov x0, #0 -;; b #0x18114 -;; 18110: mov x0, #1 +;; b #0x18110 +;; 1810c: mov x0, #1 ;; add x28, x28, #0x18 ;; mov sp, x28 ;; mov sp, x28 ;; ldr x28, [sp], #0x10 ;; ldp x29, x30, [sp], #0x10 ;; ret -;; 1812c: .byte 0x1f, 0xc1, 0x00, 0x00 +;; 18128: .byte 0x1f, 0xc1, 0x00, 0x00 diff --git a/tests/disas/winch/aarch64/br_table/nested_br_table_loop_block.wat b/tests/disas/winch/aarch64/br_table/nested_br_table_loop_block.wat index e156b44413ad..97ae340c35c0 100644 --- a/tests/disas/winch/aarch64/br_table/nested_br_table_loop_block.wat +++ b/tests/disas/winch/aarch64/br_table/nested_br_table_loop_block.wat @@ -29,7 +29,7 @@ ;; movk x17, #0x18 ;; add x16, x16, x17 ;; cmp sp, x16 -;; b.lo #0xcc +;; b.lo #0xc4 ;; 2c: mov x9, x0 ;; sub x28, x28, #0x18 ;; mov sp, x28 @@ -39,30 +39,28 @@ ;; ldur w0, [x28, #4] ;; mov x1, #2 ;; cmp w0, w1, uxtx -;; b.hs #0x78 +;; b.hs #0x74 ;; 54: csel x1, xzr, x0, hs -;; csdb -;; adr x16, #0x6c +;; adr x16, #0x68 ;; ldrsw x1, [x16, w1, uxtw #2] ;; add x16, x16, x1 ;; br x16 -;; 6c: .byte 0xd8, 0xff, 0xff, 0xff +;; 68: .byte 0xdc, 0xff, 0xff, 0xff ;; .byte 0x0c, 0x00, 0x00, 0x00 ;; b #0x44 -;; 78: mov x0, #0 +;; 74: mov x0, #0 ;; stur w0, [x28, #4] ;; ldur w0, [x28, #4] ;; mov x1, #2 ;; cmp w0, w1, uxtx -;; b.hs #0x80 -;; 90: csel x1, xzr, x0, hs -;; csdb -;; adr x16, #0xa8 +;; b.hs #0x7c +;; 8c: csel x1, xzr, x0, hs +;; adr x16, #0xa0 ;; ldrsw x1, [x16, w1, uxtw #2] ;; add x16, x16, x1 ;; br x16 -;; a8: .byte 0x08, 0x00, 0x00, 0x00 -;; .byte 0xd8, 0xff, 0xff, 0xff +;; a0: .byte 0x08, 0x00, 0x00, 0x00 +;; .byte 0xdc, 0xff, 0xff, 0xff ;; mov x0, #3 ;; add x28, x28, #0x18 ;; mov sp, x28 @@ -70,4 +68,4 @@ ;; ldr x28, [sp], #0x10 ;; ldp x29, x30, [sp], #0x10 ;; ret -;; cc: .byte 0x1f, 0xc1, 0x00, 0x00 +;; c4: .byte 0x1f, 0xc1, 0x00, 0x00 diff --git a/tests/disas/winch/aarch64/call_indirect/call_indirect.wat b/tests/disas/winch/aarch64/call_indirect/call_indirect.wat index 941c6255db1c..9c0509331350 100644 --- a/tests/disas/winch/aarch64/call_indirect/call_indirect.wat +++ b/tests/disas/winch/aarch64/call_indirect/call_indirect.wat @@ -73,7 +73,7 @@ ;; ldur x2, [x2, #0x30] ;; mov x4, x2 ;; add x2, x2, x16, uxtx -;; cmp w1, w3, uxtx +;; cmp x1, x3, uxtx ;; csel x2, x4, x2, hs ;; ldur x0, [x2] ;; tst x0, x0 @@ -139,7 +139,7 @@ ;; ldur x2, [x2, #0x30] ;; mov x4, x2 ;; add x2, x2, x16, uxtx -;; cmp w1, w3, uxtx +;; cmp x1, x3, uxtx ;; csel x2, x4, x2, hs ;; ldur x0, [x2] ;; tst x0, x0 diff --git a/tests/disas/winch/aarch64/call_indirect/local_arg.wat b/tests/disas/winch/aarch64/call_indirect/local_arg.wat index cd66a45f03c5..e81a9d7d2d8f 100644 --- a/tests/disas/winch/aarch64/call_indirect/local_arg.wat +++ b/tests/disas/winch/aarch64/call_indirect/local_arg.wat @@ -79,7 +79,7 @@ ;; ldur x2, [x2, #0x30] ;; mov x4, x2 ;; add x2, x2, x16, uxtx -;; cmp w1, w3, uxtx +;; cmp x1, x3, uxtx ;; csel x2, x4, x2, hs ;; ldur x0, [x2] ;; tst x0, x0 diff --git a/tests/disas/winch/aarch64/load/dynamic_heap.wat b/tests/disas/winch/aarch64/load/dynamic_heap.wat index 1ee8089ac05d..6e884275c603 100644 --- a/tests/disas/winch/aarch64/load/dynamic_heap.wat +++ b/tests/disas/winch/aarch64/load/dynamic_heap.wat @@ -46,7 +46,7 @@ ;; 5c: cmp x2, x1, uxtx ;; b.hi #0x158 ;; 64: ldur x3, [x9, #0x38] -;; add x3, x3, x0, uxtx +;; add x3, x3, w0, uxtw ;; mov x4, #0 ;; cmp x2, x1, uxtx ;; csel x3, x4, x3, hi @@ -59,7 +59,7 @@ ;; 90: cmp x3, x2, uxtx ;; b.hi #0x160 ;; 98: ldur x4, [x9, #0x38] -;; add x4, x4, x1, uxtx +;; add x4, x4, w1, uxtw ;; add x4, x4, #4 ;; mov x5, #0 ;; cmp x3, x2, uxtx @@ -75,7 +75,7 @@ ;; d0: cmp x4, x3, uxtx ;; b.hi #0x168 ;; d8: ldur x5, [x9, #0x38] -;; add x5, x5, x2, uxtx +;; add x5, x5, w2, uxtw ;; orr x16, xzr, #0xfffff ;; add x5, x5, x16, uxtx ;; mov x6, #0 diff --git a/tests/disas/winch/aarch64/load/f32.wat b/tests/disas/winch/aarch64/load/f32.wat index eaa217d4b55b..29b8e2dcc829 100644 --- a/tests/disas/winch/aarch64/load/f32.wat +++ b/tests/disas/winch/aarch64/load/f32.wat @@ -25,7 +25,7 @@ ;; stur x1, [x28] ;; mov x0, #0 ;; ldur x1, [x9, #0x38] -;; add x1, x1, x0, uxtx +;; add x1, x1, w0, uxtw ;; ldur s0, [x1] ;; add x28, x28, #0x10 ;; mov sp, x28 diff --git a/tests/disas/winch/aarch64/load/f64.wat b/tests/disas/winch/aarch64/load/f64.wat index 415116440d6f..ad5fc6a7437a 100644 --- a/tests/disas/winch/aarch64/load/f64.wat +++ b/tests/disas/winch/aarch64/load/f64.wat @@ -24,7 +24,7 @@ ;; stur x1, [x28] ;; mov x0, #0 ;; ldur x1, [x9, #0x38] -;; add x1, x1, x0, uxtx +;; add x1, x1, w0, uxtw ;; ldur d0, [x1] ;; add x28, x28, #0x10 ;; mov sp, x28 diff --git a/tests/disas/winch/aarch64/load/i32.wat b/tests/disas/winch/aarch64/load/i32.wat index b056feacdc59..fccf976b3622 100644 --- a/tests/disas/winch/aarch64/load/i32.wat +++ b/tests/disas/winch/aarch64/load/i32.wat @@ -25,7 +25,7 @@ ;; stur x1, [x28] ;; mov x0, #0 ;; ldur x1, [x9, #0x38] -;; add x1, x1, x0, uxtx +;; add x1, x1, w0, uxtw ;; ldur w0, [x1] ;; add x28, x28, #0x10 ;; mov sp, x28 diff --git a/tests/disas/winch/aarch64/load/i64.wat b/tests/disas/winch/aarch64/load/i64.wat index 76a80970cae8..999906081a99 100644 --- a/tests/disas/winch/aarch64/load/i64.wat +++ b/tests/disas/winch/aarch64/load/i64.wat @@ -28,13 +28,13 @@ ;; ldur x0, [x28] ;; mov x1, #8 ;; ldur x2, [x9, #0x38] -;; add x2, x2, x1, uxtx +;; add x2, x2, w1, uxtw ;; sub sp, x28, #8 ;; sturb w0, [x2] ;; mov sp, x28 ;; mov x0, #8 ;; ldur x1, [x9, #0x38] -;; add x1, x1, x0, uxtx +;; add x1, x1, w0, uxtw ;; sub sp, x28, #8 ;; ldursb x0, [x1] ;; mov sp, x28 diff --git a/tests/disas/winch/aarch64/memory_offsets/index.wat b/tests/disas/winch/aarch64/memory_offsets/index.wat new file mode 100644 index 000000000000..d727a3bb3c98 --- /dev/null +++ b/tests/disas/winch/aarch64/memory_offsets/index.wat @@ -0,0 +1,1334 @@ +;;! target = "aarch64" +;;! test = "winch" + +(module + (type (;0;) (func (result f64 f32 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 i64 f64))) + (global (;0;) (mut i32) i32.const 10) + (export "main" (func 0)) + (func (;0;) (type 0) (result f64 f32 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 i64 f64) + call 1 + call 1 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + br 0 + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + f64.const 0x1.54e5e9c49a8a3p+224 (;=35900759953881640000000000000000000000000000000000000000000000000000;) + f32.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + i64.const 0 + f64.const 0x0p+0 (;=0;) + ) + (func (;1;) (type 0) (result f64 f32 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 i64 f64) + f64.const 0x0p+0 (;=0;) + f32.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + i64.const 0 + f64.const 0x0p+0 (;=0;) + ) + (func (;2;) (type 0) (result f64 f32 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 i64 f64) + f64.const 0x0p+0 (;=0;) + f32.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + i64.const 0 + f64.const 0x0p+0 (;=0;) + ) +) +;; wasm[0]::function[0]: +;; stp x29, x30, [sp, #-0x10]! +;; mov x29, sp +;; str x28, [sp, #-0x10]! +;; mov x28, sp +;; ldur x16, [x1, #8] +;; ldur x16, [x16, #0x18] +;; mov x17, #0 +;; movk x17, #0xf10 +;; add x16, x16, x17 +;; cmp sp, x16 +;; b.lo #0x8a8 +;; 2c: mov x9, x1 +;; sub x28, x28, #0x18 +;; mov sp, x28 +;; stur x1, [x28, #0x10] +;; stur x2, [x28, #8] +;; stur x0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #0xc +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0xc +;; bl #0x8c0 +;; 64: add x28, x28, #0xc +;; mov sp, x28 +;; ldur x9, [x28, #0x7c] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #8 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #8 +;; bl #0x8c0 +;; 9c: add x28, x28, #8 +;; mov sp, x28 +;; ldur x9, [x28, #0xf0] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #4 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #4 +;; bl #0xaa0 +;; d4: add x28, x28, #4 +;; mov sp, x28 +;; mov x16, #0x164 +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0 +;; bl #0xaa0 +;; 108: ldr x9, [x28, #0x1d8] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #0xc +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0xc +;; bl #0xaa0 +;; 138: add x28, x28, #0xc +;; mov sp, x28 +;; mov x16, #0x24c +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #8 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #8 +;; bl #0xaa0 +;; 174: add x28, x28, #8 +;; mov sp, x28 +;; ldr x9, [x28, #0x2c0] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #4 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #4 +;; bl #0xaa0 +;; 1ac: add x28, x28, #4 +;; mov sp, x28 +;; mov x16, #0x334 +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0 +;; bl #0xaa0 +;; 1e0: ldr x9, [x28, #0x3a8] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #0xc +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0xc +;; bl #0xaa0 +;; 210: add x28, x28, #0xc +;; mov sp, x28 +;; mov x16, #0x41c +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #8 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #8 +;; bl #0xaa0 +;; 24c: add x28, x28, #8 +;; mov sp, x28 +;; ldr x9, [x28, #0x490] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #4 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #4 +;; bl #0xaa0 +;; 284: add x28, x28, #4 +;; mov sp, x28 +;; mov x16, #0x504 +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0 +;; bl #0xaa0 +;; 2b8: ldr x9, [x28, #0x578] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #0xc +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0xc +;; bl #0xaa0 +;; 2e8: add x28, x28, #0xc +;; mov sp, x28 +;; mov x16, #0x5ec +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #8 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #8 +;; bl #0xaa0 +;; 324: add x28, x28, #8 +;; mov sp, x28 +;; ldr x9, [x28, #0x660] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #4 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #4 +;; bl #0xaa0 +;; 35c: add x28, x28, #4 +;; mov sp, x28 +;; mov x16, #0x6d4 +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0 +;; bl #0xaa0 +;; 390: ldr x9, [x28, #0x748] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #0xc +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0xc +;; bl #0xaa0 +;; 3c0: add x28, x28, #0xc +;; mov sp, x28 +;; mov x16, #0x7bc +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #8 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #8 +;; bl #0xaa0 +;; 3fc: add x28, x28, #8 +;; mov sp, x28 +;; ldr x9, [x28, #0x830] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #4 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #4 +;; bl #0xaa0 +;; 434: add x28, x28, #4 +;; mov sp, x28 +;; mov x16, #0x8a4 +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0 +;; bl #0xaa0 +;; 468: ldr x9, [x28, #0x918] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #0xc +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0xc +;; bl #0xaa0 +;; 498: add x28, x28, #0xc +;; mov sp, x28 +;; mov x16, #0x98c +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #8 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #8 +;; bl #0xaa0 +;; 4d4: add x28, x28, #8 +;; mov sp, x28 +;; ldr x9, [x28, #0xa00] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #4 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #4 +;; bl #0xaa0 +;; 50c: add x28, x28, #4 +;; mov sp, x28 +;; mov x16, #0xa74 +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0 +;; bl #0xaa0 +;; 540: ldr x9, [x28, #0xae8] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #0xc +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0xc +;; bl #0xaa0 +;; 570: add x28, x28, #0xc +;; mov sp, x28 +;; mov x16, #0xb5c +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #8 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #8 +;; bl #0xaa0 +;; 5ac: add x28, x28, #8 +;; mov sp, x28 +;; ldr x9, [x28, #0xbd0] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #4 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #4 +;; bl #0xaa0 +;; 5e4: add x28, x28, #4 +;; mov sp, x28 +;; mov x16, #0xc44 +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0 +;; bl #0xaa0 +;; 618: ldr x9, [x28, #0xcb8] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #0xc +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0xc +;; bl #0xaa0 +;; 648: add x28, x28, #0xc +;; mov sp, x28 +;; mov x16, #0xd2c +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #8 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #8 +;; bl #0xaa0 +;; 684: add x28, x28, #8 +;; mov sp, x28 +;; ldr x9, [x28, #0xda0] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #4 +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #4 +;; bl #0xaa0 +;; 6bc: add x28, x28, #4 +;; mov sp, x28 +;; mov x16, #0xe14 +;; ldr x9, [x28, x16, sxtx] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0 +;; bl #0xaa0 +;; 6f0: ldr x9, [x28, #0xe88] +;; sub x28, x28, #8 +;; mov sp, x28 +;; stur d0, [x28] +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; sub x28, x28, #0xc +;; mov sp, x28 +;; mov x1, x9 +;; mov x2, x9 +;; add x0, x28, #0xc +;; bl #0xaa0 +;; 720: add x28, x28, #0xc +;; mov sp, x28 +;; mov x16, #0xefc +;; ldr x9, [x28, x16, sxtx] +;; ldur x16, [x28, #0x64] +;; mov x17, #0xee4 +;; str x16, [x28, x17, sxtx] +;; ldur w16, [x28, #0x60] +;; str w16, [x28, #0xee0] +;; ldur x16, [x28, #0x58] +;; str x16, [x28, #0xed8] +;; ldur x16, [x28, #0x50] +;; str x16, [x28, #0xed0] +;; ldur x16, [x28, #0x48] +;; str x16, [x28, #0xec8] +;; ldur x16, [x28, #0x40] +;; str x16, [x28, #0xec0] +;; ldur x16, [x28, #0x38] +;; str x16, [x28, #0xeb8] +;; ldur x16, [x28, #0x30] +;; str x16, [x28, #0xeb0] +;; ldur x16, [x28, #0x28] +;; str x16, [x28, #0xea8] +;; ldur x16, [x28, #0x20] +;; str x16, [x28, #0xea0] +;; ldur x16, [x28, #0x18] +;; str x16, [x28, #0xe98] +;; ldur x16, [x28, #0x10] +;; str x16, [x28, #0xe90] +;; ldur x16, [x28, #8] +;; str x16, [x28, #0xe88] +;; ldur x16, [x28] +;; str x16, [x28, #0xe80] +;; add x28, x28, #0xe80 +;; mov sp, x28 +;; ldur x0, [x28, #0x6c] +;; ldur x16, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur x16, [x0] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #8] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x10] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x18] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x20] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x28] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x30] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x38] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x40] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x48] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x50] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x58] +;; ldur s31, [x28] +;; add x28, x28, #4 +;; mov sp, x28 +;; stur s31, [x0, #0x60] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x64] +;; add x28, x28, #0x18 +;; mov sp, x28 +;; mov sp, x28 +;; ldr x28, [sp], #0x10 +;; ldp x29, x30, [sp], #0x10 +;; ret +;; 8a8: .byte 0x1f, 0xc1, 0x00, 0x00 +;; +;; wasm[0]::function[1]: +;; stp x29, x30, [sp, #-0x10]! +;; mov x29, sp +;; str x28, [sp, #-0x10]! +;; mov x28, sp +;; ldur x16, [x1, #8] +;; ldur x16, [x16, #0x18] +;; mov x17, #0 +;; movk x17, #0x84 +;; add x16, x16, x17 +;; cmp sp, x16 +;; b.lo #0xa7c +;; 8ec: mov x9, x1 +;; sub x28, x28, #0x18 +;; mov sp, x28 +;; stur x1, [x28, #0x10] +;; stur x2, [x28, #8] +;; stur x0, [x28] +;; ldr d0, #0xa80 +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x16, #0 +;; stur x16, [x28] +;; ldr d31, #0xa80 +;; stur d31, [x28, #8] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x10] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x18] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x20] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x28] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x30] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x38] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x40] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x48] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x50] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x58] +;; ldr s31, #0xa88 +;; stur s31, [x28, #0x60] +;; ldr d31, #0xa80 +;; stur d31, [x28, #0x64] +;; ldur x0, [x28, #0x6c] +;; ldur x16, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur x16, [x0] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #8] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x10] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x18] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x20] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x28] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x30] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x38] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x40] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x48] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x50] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x58] +;; ldur s31, [x28] +;; add x28, x28, #4 +;; mov sp, x28 +;; stur s31, [x0, #0x60] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x64] +;; add x28, x28, #0x18 +;; mov sp, x28 +;; mov sp, x28 +;; ldr x28, [sp], #0x10 +;; ldp x29, x30, [sp], #0x10 +;; ret +;; a7c: .byte 0x1f, 0xc1, 0x00, 0x00 +;; a80: .byte 0x00, 0x00, 0x00, 0x00 +;; a84: .byte 0x00, 0x00, 0x00, 0x00 +;; a88: .byte 0x00, 0x00, 0x00, 0x00 +;; +;; wasm[0]::function[2]: +;; stp x29, x30, [sp, #-0x10]! +;; mov x29, sp +;; str x28, [sp, #-0x10]! +;; mov x28, sp +;; ldur x16, [x1, #8] +;; ldur x16, [x16, #0x18] +;; mov x17, #0 +;; movk x17, #0x84 +;; add x16, x16, x17 +;; cmp sp, x16 +;; b.lo #0xc5c +;; acc: mov x9, x1 +;; sub x28, x28, #0x18 +;; mov sp, x28 +;; stur x1, [x28, #0x10] +;; stur x2, [x28, #8] +;; stur x0, [x28] +;; ldr d0, #0xc60 +;; sub x28, x28, #0x6c +;; mov sp, x28 +;; mov x16, #0 +;; stur x16, [x28] +;; ldr d31, #0xc60 +;; stur d31, [x28, #8] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x10] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x18] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x20] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x28] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x30] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x38] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x40] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x48] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x50] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x58] +;; ldr s31, #0xc68 +;; stur s31, [x28, #0x60] +;; ldr d31, #0xc60 +;; stur d31, [x28, #0x64] +;; ldur x0, [x28, #0x6c] +;; ldur x16, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur x16, [x0] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #8] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x10] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x18] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x20] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x28] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x30] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x38] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x40] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x48] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x50] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x58] +;; ldur s31, [x28] +;; add x28, x28, #4 +;; mov sp, x28 +;; stur s31, [x0, #0x60] +;; ldur d31, [x28] +;; add x28, x28, #8 +;; mov sp, x28 +;; stur d31, [x0, #0x64] +;; add x28, x28, #0x18 +;; mov sp, x28 +;; mov sp, x28 +;; ldr x28, [sp], #0x10 +;; ldp x29, x30, [sp], #0x10 +;; ret +;; c5c: .byte 0x1f, 0xc1, 0x00, 0x00 +;; c60: .byte 0x00, 0x00, 0x00, 0x00 +;; c64: .byte 0x00, 0x00, 0x00, 0x00 +;; c68: .byte 0x00, 0x00, 0x00, 0x00 diff --git a/tests/disas/winch/aarch64/store/dynamic_heap.wat b/tests/disas/winch/aarch64/store/dynamic_heap.wat index 24c7b3455e05..c2be4e9701e8 100644 --- a/tests/disas/winch/aarch64/store/dynamic_heap.wat +++ b/tests/disas/winch/aarch64/store/dynamic_heap.wat @@ -49,7 +49,7 @@ ;; 68: cmp x3, x2, uxtx ;; b.hi #0x12c ;; 70: ldur x4, [x9, #0x38] -;; add x4, x4, x1, uxtx +;; add x4, x4, w1, uxtw ;; mov x5, #0 ;; cmp x3, x2, uxtx ;; csel x4, x5, x4, hi @@ -63,7 +63,7 @@ ;; a0: cmp x3, x2, uxtx ;; b.hi #0x134 ;; a8: ldur x4, [x9, #0x38] -;; add x4, x4, x1, uxtx +;; add x4, x4, w1, uxtw ;; add x4, x4, #4 ;; mov x5, #0 ;; cmp x3, x2, uxtx @@ -80,7 +80,7 @@ ;; e4: cmp x3, x2, uxtx ;; b.hi #0x13c ;; ec: ldur x4, [x9, #0x38] -;; add x4, x4, x1, uxtx +;; add x4, x4, w1, uxtw ;; orr x16, xzr, #0xfffff ;; add x4, x4, x16, uxtx ;; mov x5, #0 diff --git a/tests/disas/winch/aarch64/store/f32.wat b/tests/disas/winch/aarch64/store/f32.wat index c9555ba4dc53..8717dd199fbe 100644 --- a/tests/disas/winch/aarch64/store/f32.wat +++ b/tests/disas/winch/aarch64/store/f32.wat @@ -25,7 +25,7 @@ ;; ldr s0, #0x70 ;; mov x0, #0 ;; ldur x1, [x9, #0x38] -;; add x1, x1, x0, uxtx +;; add x1, x1, w0, uxtw ;; stur s0, [x1] ;; add x28, x28, #0x10 ;; mov sp, x28 diff --git a/tests/disas/winch/aarch64/store/f64.wat b/tests/disas/winch/aarch64/store/f64.wat index f2aee771a286..70bdd13d9f67 100644 --- a/tests/disas/winch/aarch64/store/f64.wat +++ b/tests/disas/winch/aarch64/store/f64.wat @@ -26,7 +26,7 @@ ;; ldr d0, #0x70 ;; mov x0, #0 ;; ldur x1, [x9, #0x38] -;; add x1, x1, x0, uxtx +;; add x1, x1, w0, uxtw ;; stur d0, [x1] ;; add x28, x28, #0x10 ;; mov sp, x28 diff --git a/tests/disas/winch/aarch64/store/i32.wat b/tests/disas/winch/aarch64/store/i32.wat index 48fba5104047..d5ab1b9587ab 100644 --- a/tests/disas/winch/aarch64/store/i32.wat +++ b/tests/disas/winch/aarch64/store/i32.wat @@ -27,7 +27,7 @@ ;; mov x0, #1 ;; mov x1, #0 ;; ldur x2, [x9, #0x38] -;; add x2, x2, x1, uxtx +;; add x2, x2, w1, uxtw ;; stur w0, [x2] ;; add x28, x28, #0x10 ;; mov sp, x28 diff --git a/tests/disas/winch/x64/atomic/fence/fence.wat b/tests/disas/winch/x64/atomic/fence/fence.wat index 5fb841aa8e9a..9b4a4b038f7d 100644 --- a/tests/disas/winch/x64/atomic/fence/fence.wat +++ b/tests/disas/winch/x64/atomic/fence/fence.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x62 +;; ja 0x66 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -24,15 +24,17 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movl %eax, (%rdx) ;; mfence ;; movl $0, %eax ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movl (%rcx), %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 62: ud2 +;; 66: ud2 diff --git a/tests/disas/winch/x64/atomic/load/i32_atomic_load.wat b/tests/disas/winch/x64/atomic/load/i32_atomic_load.wat index f96d7b8d33d0..fca088c46530 100644 --- a/tests/disas/winch/x64/atomic/load/i32_atomic_load.wat +++ b/tests/disas/winch/x64/atomic/load/i32_atomic_load.wat @@ -13,7 +13,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0x5f +;; ja 0x61 ;; 1c: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -22,14 +22,15 @@ ;; movl 0xc(%rsp), %eax ;; andl $3, %eax ;; cmpl $0, %eax -;; jne 0x61 +;; jne 0x63 ;; 46: movl 0xc(%rsp), %eax ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movl (%rcx), %eax ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 5f: ud2 ;; 61: ud2 +;; 63: ud2 diff --git a/tests/disas/winch/x64/atomic/load/i32_atomic_load16_u.wat b/tests/disas/winch/x64/atomic/load/i32_atomic_load16_u.wat index 4c82b1db2687..8f190c3b6b43 100644 --- a/tests/disas/winch/x64/atomic/load/i32_atomic_load16_u.wat +++ b/tests/disas/winch/x64/atomic/load/i32_atomic_load16_u.wat @@ -13,7 +13,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0x61 +;; ja 0x63 ;; 1c: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -22,14 +22,15 @@ ;; movl 0xc(%rsp), %eax ;; andw $1, %ax ;; cmpw $0, %ax -;; jne 0x63 +;; jne 0x65 ;; 46: movl 0xc(%rsp), %eax ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movzwq (%rcx), %rax ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 61: ud2 ;; 63: ud2 +;; 65: ud2 diff --git a/tests/disas/winch/x64/atomic/load/i32_atomic_load8_u.wat b/tests/disas/winch/x64/atomic/load/i32_atomic_load8_u.wat index 9a410d10a368..2a4454b864c2 100644 --- a/tests/disas/winch/x64/atomic/load/i32_atomic_load8_u.wat +++ b/tests/disas/winch/x64/atomic/load/i32_atomic_load8_u.wat @@ -13,16 +13,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x48 +;; ja 0x4a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movzbq (%rcx), %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 48: ud2 +;; 4a: ud2 diff --git a/tests/disas/winch/x64/atomic/load/i64_atomic_load.wat b/tests/disas/winch/x64/atomic/load/i64_atomic_load.wat index be89e8f71b62..1482297c2cfd 100644 --- a/tests/disas/winch/x64/atomic/load/i64_atomic_load.wat +++ b/tests/disas/winch/x64/atomic/load/i64_atomic_load.wat @@ -14,7 +14,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x5c +;; ja 0x5e ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,13 +22,14 @@ ;; movl $0, %eax ;; andq $7, %rax ;; cmpq $0, %rax -;; jne 0x5e +;; jne 0x60 ;; 44: movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movq (%rcx), %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 5c: ud2 ;; 5e: ud2 +;; 60: ud2 diff --git a/tests/disas/winch/x64/atomic/load/i64_atomic_load16_u.wat b/tests/disas/winch/x64/atomic/load/i64_atomic_load16_u.wat index da9c5e1d244a..a27b432cb78d 100644 --- a/tests/disas/winch/x64/atomic/load/i64_atomic_load16_u.wat +++ b/tests/disas/winch/x64/atomic/load/i64_atomic_load16_u.wat @@ -14,7 +14,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x5b +;; ja 0x5d ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,13 +22,14 @@ ;; movl $0, %eax ;; andw $1, %ax ;; cmpw $0, %ax -;; jne 0x5d +;; jne 0x5f ;; 42: movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movzwq (%rcx), %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 5b: ud2 ;; 5d: ud2 +;; 5f: ud2 diff --git a/tests/disas/winch/x64/atomic/load/i64_atomic_load32_u.wat b/tests/disas/winch/x64/atomic/load/i64_atomic_load32_u.wat index be4b6bbe01bc..70a7f10f307b 100644 --- a/tests/disas/winch/x64/atomic/load/i64_atomic_load32_u.wat +++ b/tests/disas/winch/x64/atomic/load/i64_atomic_load32_u.wat @@ -14,7 +14,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x59 +;; ja 0x5b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,13 +22,14 @@ ;; movl $0, %eax ;; andl $3, %eax ;; cmpl $0, %eax -;; jne 0x5b +;; jne 0x5d ;; 42: movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movl (%rcx), %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 59: ud2 ;; 5b: ud2 +;; 5d: ud2 diff --git a/tests/disas/winch/x64/atomic/load/i64_atomic_load8_u.wat b/tests/disas/winch/x64/atomic/load/i64_atomic_load8_u.wat index 4c0117c16526..33ffb6d7e81b 100644 --- a/tests/disas/winch/x64/atomic/load/i64_atomic_load8_u.wat +++ b/tests/disas/winch/x64/atomic/load/i64_atomic_load8_u.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x48 +;; ja 0x4a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movzbq (%rcx), %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 48: ud2 +;; 4a: ud2 diff --git a/tests/disas/winch/x64/atomic/notify/notify_offset.wat b/tests/disas/winch/x64/atomic/notify/notify_offset.wat index 68ec40e22322..6d3cf71f12a1 100644 --- a/tests/disas/winch/x64/atomic/notify/notify_offset.wat +++ b/tests/disas/winch/x64/atomic/notify/notify_offset.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0x84 +;; ja 0x8a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,7 +20,8 @@ ;; movl $0xa, %eax ;; movl $0, %ecx ;; addq $8, %rcx -;; pushq %rcx +;; jb 0x8c +;; 46: pushq %rcx ;; subq $4, %rsp ;; movl %eax, (%rsp) ;; subq $4, %rsp @@ -28,11 +29,12 @@ ;; movl $0, %esi ;; movq 8(%rsp), %rdx ;; movl 4(%rsp), %ecx -;; callq 0x175 +;; callq 0x17d ;; addq $4, %rsp ;; addq $0xc, %rsp ;; movq 8(%rsp), %r14 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 84: ud2 +;; 8a: ud2 +;; 8c: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw16_addu.wat b/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw16_addu.wat index 5c18877214f4..a99c2426dc72 100644 --- a/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw16_addu.wat +++ b/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw16_addu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x68 +;; ja 0x6a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,15 +21,16 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x6a +;; jne 0x6c ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; lock xaddw %ax, (%rdx) ;; movzwl %ax, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 68: ud2 ;; 6a: ud2 +;; 6c: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw8_addu.wat b/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw8_addu.wat index b8652f7b973f..a84a3ddaefd9 100644 --- a/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw8_addu.wat +++ b/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw8_addu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x53 +;; ja 0x55 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; lock xaddb %al, (%rdx) ;; movzbl %al, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 53: ud2 +;; 55: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw_add.wat b/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw_add.wat index 5c1ce19f753b..abc3c93bd8c4 100644 --- a/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw_add.wat +++ b/tests/disas/winch/x64/atomic/rmw/add/i32_atomic_rmw_add.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x64 +;; ja 0x66 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,14 +21,15 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x66 +;; jne 0x68 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; lock xaddl %eax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 64: ud2 ;; 66: ud2 +;; 68: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw16_addu.wat b/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw16_addu.wat index a66156bea0e0..2286d393fc5e 100644 --- a/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw16_addu.wat +++ b/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw16_addu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x69 +;; ja 0x6b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,15 +21,16 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x6b +;; jne 0x6d ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; lock xaddw %ax, (%rdx) ;; movzwq %ax, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 69: ud2 ;; 6b: ud2 +;; 6d: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw32_addu.wat b/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw32_addu.wat index 6adfe16211f4..7ae8f457866b 100644 --- a/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw32_addu.wat +++ b/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw32_addu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x64 +;; ja 0x66 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,14 +21,15 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x66 +;; jne 0x68 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; lock xaddl %eax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 64: ud2 ;; 66: ud2 +;; 68: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw8_addu.wat b/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw8_addu.wat index 05ee601a5ad5..102171394539 100644 --- a/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw8_addu.wat +++ b/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw8_addu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x54 +;; ja 0x56 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; lock xaddb %al, (%rdx) ;; movzbq %al, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 54: ud2 +;; 56: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw_add.wat b/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw_add.wat index 9ed0adca604a..6757431d1a52 100644 --- a/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw_add.wat +++ b/tests/disas/winch/x64/atomic/rmw/add/i64_atomic_rmw_add.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x67 +;; ja 0x69 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,14 +21,15 @@ ;; movl $0, %ecx ;; andq $7, %rcx ;; cmpq $0, %rcx -;; jne 0x69 +;; jne 0x6b ;; 4a: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; lock xaddq %rax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 67: ud2 ;; 69: ud2 +;; 6b: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw16_andu.wat b/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw16_andu.wat index 976fdbea554b..057896fc7960 100644 --- a/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw16_andu.wat +++ b/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw16_andu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x14, %r11 ;; cmpq %rsp, %r11 -;; ja 0x8d +;; ja 0x8f ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x8f +;; jne 0x91 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; subq $4, %rsp ;; movl %eax, (%rsp) @@ -34,10 +35,10 @@ ;; movq %rax, %r11 ;; andq %rcx, %r11 ;; lock cmpxchgw %r11w, (%rdx) -;; jne 0x6f -;; 81: movzwl %ax, %eax +;; jne 0x71 +;; 83: movzwl %ax, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 8d: ud2 ;; 8f: ud2 +;; 91: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw8_andu.wat b/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw8_andu.wat index 59883fd36b79..805b4be84cb6 100644 --- a/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw8_andu.wat +++ b/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw8_andu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x14, %r11 ;; cmpq %rsp, %r11 -;; ja 0x78 +;; ja 0x7a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,6 +21,7 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; subq $4, %rsp ;; movl %eax, (%rsp) @@ -30,9 +31,9 @@ ;; movq %rax, %r11 ;; andq %rcx, %r11 ;; lock cmpxchgb %r11b, (%rdx) -;; jne 0x5b -;; 6c: movzbl %al, %eax +;; jne 0x5d +;; 6e: movzbl %al, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 78: ud2 +;; 7a: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw_and.wat b/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw_and.wat index 5072b914b808..9f7d6cac30ee 100644 --- a/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw_and.wat +++ b/tests/disas/winch/x64/atomic/rmw/and/i32_atomic_rmw_and.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x14, %r11 ;; cmpq %rsp, %r11 -;; ja 0x87 +;; ja 0x89 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x89 +;; jne 0x8b ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; subq $4, %rsp ;; movl %eax, (%rsp) @@ -34,9 +35,9 @@ ;; movq %rax, %r11 ;; andq %rcx, %r11 ;; lock cmpxchgl %r11d, (%rdx) -;; jne 0x6d -;; 7e: addq $0x10, %rsp +;; jne 0x6f +;; 80: addq $0x10, %rsp ;; popq %rbp ;; retq -;; 87: ud2 ;; 89: ud2 +;; 8b: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw16_andu.wat b/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw16_andu.wat index c7af5bece19d..c008dad438d3 100644 --- a/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw16_andu.wat +++ b/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw16_andu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x7c +;; ja 0x7e ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x7e +;; jne 0x80 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -32,10 +33,10 @@ ;; movq %rax, %r11 ;; andq %rcx, %r11 ;; lock cmpxchgw %r11w, (%rdx) -;; jne 0x5d -;; 6f: movzwq %ax, %rax +;; jne 0x5f +;; 71: movzwq %ax, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 7c: ud2 ;; 7e: ud2 +;; 80: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw32_andu.wat b/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw32_andu.wat index d050fe6d3082..ebeac1c6e077 100644 --- a/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw32_andu.wat +++ b/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw32_andu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x75 +;; ja 0x77 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x77 +;; jne 0x79 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -32,9 +33,9 @@ ;; movq %rax, %r11 ;; andq %rcx, %r11 ;; lock cmpxchgl %r11d, (%rdx) -;; jne 0x5b -;; 6c: addq $0x10, %rsp +;; jne 0x5d +;; 6e: addq $0x10, %rsp ;; popq %rbp ;; retq -;; 75: ud2 ;; 77: ud2 +;; 79: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw8_andu.wat b/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw8_andu.wat index a2abddb5c256..0a6d9e215cb6 100644 --- a/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw8_andu.wat +++ b/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw8_andu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x67 +;; ja 0x69 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,6 +21,7 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -28,9 +29,9 @@ ;; movq %rax, %r11 ;; andq %rcx, %r11 ;; lock cmpxchgb %r11b, (%rdx) -;; jne 0x49 -;; 5a: movzbq %al, %rax +;; jne 0x4b +;; 5c: movzbq %al, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 67: ud2 +;; 69: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw_and.wat b/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw_and.wat index 2bea25bb3a12..e314c7be1adb 100644 --- a/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw_and.wat +++ b/tests/disas/winch/x64/atomic/rmw/and/i64_atomic_rmw_and.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x78 +;; ja 0x7a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andq $7, %rcx ;; cmpq $0, %rcx -;; jne 0x7a +;; jne 0x7c ;; 4a: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -32,9 +33,9 @@ ;; movq %rax, %r11 ;; andq %rcx, %r11 ;; lock cmpxchgq %r11, (%rdx) -;; jne 0x5e -;; 6f: addq $0x10, %rsp +;; jne 0x60 +;; 71: addq $0x10, %rsp ;; popq %rbp ;; retq -;; 78: ud2 ;; 7a: ud2 +;; 7c: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw16_cmpxchgu.wat b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw16_cmpxchgu.wat index e7482e01dff8..1b6a30c9f470 100644 --- a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw16_cmpxchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw16_cmpxchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x95 +;; ja 0x97 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,10 +22,11 @@ ;; movl $0, %edx ;; andw $1, %dx ;; cmpw $0, %dx -;; jne 0x97 +;; jne 0x99 ;; 4d: movl $0, %edx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rbx +;; movl %edx, %edx ;; addq %rdx, %rbx ;; subq $4, %rsp ;; movl %ecx, (%rsp) @@ -40,5 +41,5 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 95: ud2 ;; 97: ud2 +;; 99: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw8_cmpxchgu.wat b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw8_cmpxchgu.wat index fdae2a0131b8..4bd5ff094b6c 100644 --- a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw8_cmpxchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw8_cmpxchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x80 +;; ja 0x82 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,6 +22,7 @@ ;; movl $0, %edx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rbx +;; movl %edx, %edx ;; addq %rdx, %rbx ;; subq $4, %rsp ;; movl %ecx, (%rsp) @@ -36,4 +37,4 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 80: ud2 +;; 82: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw_cmpxchg.wat b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw_cmpxchg.wat index f5dc01fd46c1..d2e923d3fbbf 100644 --- a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw_cmpxchg.wat +++ b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i32_atomic_rmw_cmpxchg.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x91 +;; ja 0x93 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,10 +22,11 @@ ;; movl $0, %edx ;; andl $3, %edx ;; cmpl $0, %edx -;; jne 0x93 +;; jne 0x95 ;; 4d: movl $0, %edx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rbx +;; movl %edx, %edx ;; addq %rdx, %rbx ;; subq $4, %rsp ;; movl %ecx, (%rsp) @@ -39,5 +40,5 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 91: ud2 ;; 93: ud2 +;; 95: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw16_cmpxchgu.wat b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw16_cmpxchgu.wat index 43431830faf2..14c5d7b0a300 100644 --- a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw16_cmpxchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw16_cmpxchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0x72 +;; ja 0x74 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,10 +22,11 @@ ;; movl $0, %edx ;; andw $1, %dx ;; cmpw $0, %dx -;; jne 0x74 +;; jne 0x76 ;; 4d: movl $0, %edx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rbx +;; movl %edx, %edx ;; addq %rdx, %rbx ;; pushq %rcx ;; pushq %rax @@ -36,5 +37,5 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 72: ud2 ;; 74: ud2 +;; 76: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw32_cmpxchgu.wat b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw32_cmpxchgu.wat index 83aad3af07ef..2fa2451eae72 100644 --- a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw32_cmpxchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw32_cmpxchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0x6d +;; ja 0x6f ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,10 +22,11 @@ ;; movl $0, %edx ;; andl $3, %edx ;; cmpl $0, %edx -;; jne 0x6f +;; jne 0x71 ;; 4d: movl $0, %edx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rbx +;; movl %edx, %edx ;; addq %rdx, %rbx ;; pushq %rcx ;; pushq %rax @@ -35,5 +36,5 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 6d: ud2 ;; 6f: ud2 +;; 71: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw8_cmpxchgu.wat b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw8_cmpxchgu.wat index 5937429bcce8..96898ac61b72 100644 --- a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw8_cmpxchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw8_cmpxchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0x5d +;; ja 0x5f ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,6 +22,7 @@ ;; movl $0, %edx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rbx +;; movl %edx, %edx ;; addq %rdx, %rbx ;; pushq %rcx ;; pushq %rax @@ -32,4 +33,4 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 5d: ud2 +;; 5f: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw_cmpxchg.wat b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw_cmpxchg.wat index 35d973ffcf81..08c068ef244e 100644 --- a/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw_cmpxchg.wat +++ b/tests/disas/winch/x64/atomic/rmw/cmpxchg/i64_atomic_rmw_cmpxchg.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0x70 +;; ja 0x72 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,10 +22,11 @@ ;; movl $0, %edx ;; andq $7, %rdx ;; cmpq $0, %rdx -;; jne 0x72 +;; jne 0x74 ;; 4f: movl $0, %edx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rbx +;; movl %edx, %edx ;; addq %rdx, %rbx ;; pushq %rcx ;; pushq %rax @@ -35,5 +36,5 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 70: ud2 ;; 72: ud2 +;; 74: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw16_oru.wat b/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw16_oru.wat index 6a38c317e007..ee570769a872 100644 --- a/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw16_oru.wat +++ b/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw16_oru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x14, %r11 ;; cmpq %rsp, %r11 -;; ja 0x8d +;; ja 0x8f ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x8f +;; jne 0x91 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; subq $4, %rsp ;; movl %eax, (%rsp) @@ -34,10 +35,10 @@ ;; movq %rax, %r11 ;; orq %rcx, %r11 ;; lock cmpxchgw %r11w, (%rdx) -;; jne 0x6f -;; 81: movzwl %ax, %eax +;; jne 0x71 +;; 83: movzwl %ax, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 8d: ud2 ;; 8f: ud2 +;; 91: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw8_oru.wat b/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw8_oru.wat index d159cb36d465..59b2b5d1bd7d 100644 --- a/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw8_oru.wat +++ b/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw8_oru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x14, %r11 ;; cmpq %rsp, %r11 -;; ja 0x78 +;; ja 0x7a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,6 +21,7 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; subq $4, %rsp ;; movl %eax, (%rsp) @@ -30,9 +31,9 @@ ;; movq %rax, %r11 ;; orq %rcx, %r11 ;; lock cmpxchgb %r11b, (%rdx) -;; jne 0x5b -;; 6c: movzbl %al, %eax +;; jne 0x5d +;; 6e: movzbl %al, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 78: ud2 +;; 7a: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw_or.wat b/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw_or.wat index 85bd3a14b1b9..1472e2d27f46 100644 --- a/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw_or.wat +++ b/tests/disas/winch/x64/atomic/rmw/or/i32_atomic_rmw_or.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x14, %r11 ;; cmpq %rsp, %r11 -;; ja 0x87 +;; ja 0x89 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x89 +;; jne 0x8b ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; subq $4, %rsp ;; movl %eax, (%rsp) @@ -34,9 +35,9 @@ ;; movq %rax, %r11 ;; orq %rcx, %r11 ;; lock cmpxchgl %r11d, (%rdx) -;; jne 0x6d -;; 7e: addq $0x10, %rsp +;; jne 0x6f +;; 80: addq $0x10, %rsp ;; popq %rbp ;; retq -;; 87: ud2 ;; 89: ud2 +;; 8b: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw16_oru.wat b/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw16_oru.wat index f185ee1a60e1..76a44e34223b 100644 --- a/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw16_oru.wat +++ b/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw16_oru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x7c +;; ja 0x7e ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x7e +;; jne 0x80 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -32,10 +33,10 @@ ;; movq %rax, %r11 ;; orq %rcx, %r11 ;; lock cmpxchgw %r11w, (%rdx) -;; jne 0x5d -;; 6f: movzwq %ax, %rax +;; jne 0x5f +;; 71: movzwq %ax, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 7c: ud2 ;; 7e: ud2 +;; 80: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw32_oru.wat b/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw32_oru.wat index 5c096d070fd7..56f38739df90 100644 --- a/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw32_oru.wat +++ b/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw32_oru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x75 +;; ja 0x77 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x77 +;; jne 0x79 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -32,9 +33,9 @@ ;; movq %rax, %r11 ;; orq %rcx, %r11 ;; lock cmpxchgl %r11d, (%rdx) -;; jne 0x5b -;; 6c: addq $0x10, %rsp +;; jne 0x5d +;; 6e: addq $0x10, %rsp ;; popq %rbp ;; retq -;; 75: ud2 ;; 77: ud2 +;; 79: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw8_oru.wat b/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw8_oru.wat index ddf236cc81d2..78a1f3acf9ff 100644 --- a/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw8_oru.wat +++ b/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw8_oru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x67 +;; ja 0x69 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,6 +21,7 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -28,9 +29,9 @@ ;; movq %rax, %r11 ;; orq %rcx, %r11 ;; lock cmpxchgb %r11b, (%rdx) -;; jne 0x49 -;; 5a: movzbq %al, %rax +;; jne 0x4b +;; 5c: movzbq %al, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 67: ud2 +;; 69: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw_or.wat b/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw_or.wat index e5413396db2d..7e2aee8a68f4 100644 --- a/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw_or.wat +++ b/tests/disas/winch/x64/atomic/rmw/or/i64_atomic_rmw_or.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x78 +;; ja 0x7a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andq $7, %rcx ;; cmpq $0, %rcx -;; jne 0x7a +;; jne 0x7c ;; 4a: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -32,9 +33,9 @@ ;; movq %rax, %r11 ;; orq %rcx, %r11 ;; lock cmpxchgq %r11, (%rdx) -;; jne 0x5e -;; 6f: addq $0x10, %rsp +;; jne 0x60 +;; 71: addq $0x10, %rsp ;; popq %rbp ;; retq -;; 78: ud2 ;; 7a: ud2 +;; 7c: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw16_subu.wat b/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw16_subu.wat index 60303b2447f5..4d36496e59ee 100644 --- a/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw16_subu.wat +++ b/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw16_subu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x6b +;; ja 0x6d ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x6d +;; jne 0x6f ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; negw %ax ;; lock xaddw %ax, (%rdx) @@ -32,5 +33,5 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 6b: ud2 ;; 6d: ud2 +;; 6f: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw8_subu.wat b/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw8_subu.wat index ef6c16374279..e2b8981c78f1 100644 --- a/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw8_subu.wat +++ b/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw8_subu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x55 +;; ja 0x57 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,6 +21,7 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; negb %al ;; lock xaddb %al, (%rdx) @@ -28,4 +29,4 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 55: ud2 +;; 57: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw_sub.wat b/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw_sub.wat index d1f2c4f1a0ac..92c60396b4ce 100644 --- a/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw_sub.wat +++ b/tests/disas/winch/x64/atomic/rmw/sub/i32_atomic_rmw_sub.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x66 +;; ja 0x68 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,15 +21,16 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x68 +;; jne 0x6a ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; negl %eax ;; lock xaddl %eax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 66: ud2 ;; 68: ud2 +;; 6a: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw16_subu.wat b/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw16_subu.wat index a8c7d943c191..11abf189843b 100644 --- a/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw16_subu.wat +++ b/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw16_subu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x6c +;; ja 0x6e ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x6e +;; jne 0x70 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; negw %ax ;; lock xaddw %ax, (%rdx) @@ -32,5 +33,5 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 6c: ud2 ;; 6e: ud2 +;; 70: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw32_subu.wat b/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw32_subu.wat index 177df49343de..889b0e3dfd3c 100644 --- a/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw32_subu.wat +++ b/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw32_subu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x66 +;; ja 0x68 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,15 +21,16 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x68 +;; jne 0x6a ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; negl %eax ;; lock xaddl %eax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 66: ud2 ;; 68: ud2 +;; 6a: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw8_subu.wat b/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw8_subu.wat index c447ab0fa598..3dccc0cffa14 100644 --- a/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw8_subu.wat +++ b/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw8_subu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x56 +;; ja 0x58 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,6 +21,7 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; negb %al ;; lock xaddb %al, (%rdx) @@ -28,4 +29,4 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 56: ud2 +;; 58: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw_sub.wat b/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw_sub.wat index 8853273adc7c..9b3ad2543e1f 100644 --- a/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw_sub.wat +++ b/tests/disas/winch/x64/atomic/rmw/sub/i64_atomic_rmw_sub.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x6a +;; ja 0x6c ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,15 +21,16 @@ ;; movl $0, %ecx ;; andq $7, %rcx ;; cmpq $0, %rcx -;; jne 0x6c +;; jne 0x6e ;; 4a: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; negq %rax ;; lock xaddq %rax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 6a: ud2 ;; 6c: ud2 +;; 6e: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw16_xchgu.wat b/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw16_xchgu.wat index 6b23469c5e16..5652d7a767a5 100644 --- a/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw16_xchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw16_xchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x66 +;; ja 0x68 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,15 +21,16 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x68 +;; jne 0x6a ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; xchgw %ax, (%rdx) ;; movzwl %ax, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 66: ud2 ;; 68: ud2 +;; 6a: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw8_xchgu.wat b/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw8_xchgu.wat index 039f1630aa21..d8d3e86abc7f 100644 --- a/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw8_xchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw8_xchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x51 +;; ja 0x53 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; xchgb %al, (%rdx) ;; movzbl %al, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 51: ud2 +;; 53: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw_xchg.wat b/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw_xchg.wat index 1b55d38f1854..a243a60fbbee 100644 --- a/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw_xchg.wat +++ b/tests/disas/winch/x64/atomic/rmw/xchg/i32_atomic_rmw_xchg.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x62 +;; ja 0x64 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,14 +21,15 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x64 +;; jne 0x66 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; xchgl %eax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 62: ud2 ;; 64: ud2 +;; 66: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw16_xchgu.wat b/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw16_xchgu.wat index 582fdd104be0..2d698249f805 100644 --- a/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw16_xchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw16_xchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x67 +;; ja 0x69 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,15 +21,16 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x69 +;; jne 0x6b ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; xchgw %ax, (%rdx) ;; movzwq %ax, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 67: ud2 ;; 69: ud2 +;; 6b: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw32_xchgu.wat b/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw32_xchgu.wat index c5cbcc489072..87230d713330 100644 --- a/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw32_xchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw32_xchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x62 +;; ja 0x64 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,14 +21,15 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x64 +;; jne 0x66 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; xchgl %eax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 62: ud2 ;; 64: ud2 +;; 66: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw8_xchgu.wat b/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw8_xchgu.wat index 0fe33e382eb5..d3ee516b4fbb 100644 --- a/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw8_xchgu.wat +++ b/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw8_xchgu.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x52 +;; ja 0x54 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; xchgb %al, (%rdx) ;; movzbq %al, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 52: ud2 +;; 54: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw_xchg.wat b/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw_xchg.wat index 8e16eddf529f..72f9400fc6fb 100644 --- a/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw_xchg.wat +++ b/tests/disas/winch/x64/atomic/rmw/xchg/i64_atomic_rmw_xchg.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x65 +;; ja 0x67 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,14 +21,15 @@ ;; movl $0, %ecx ;; andq $7, %rcx ;; cmpq $0, %rcx -;; jne 0x67 +;; jne 0x69 ;; 4a: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; xchgq %rax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 65: ud2 ;; 67: ud2 +;; 69: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw16_xoru.wat b/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw16_xoru.wat index d125eb187fda..0f2ff36f1357 100644 --- a/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw16_xoru.wat +++ b/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw16_xoru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x14, %r11 ;; cmpq %rsp, %r11 -;; ja 0x8d +;; ja 0x8f ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x8f +;; jne 0x91 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; subq $4, %rsp ;; movl %eax, (%rsp) @@ -34,10 +35,10 @@ ;; movq %rax, %r11 ;; xorq %rcx, %r11 ;; lock cmpxchgw %r11w, (%rdx) -;; jne 0x6f -;; 81: movzwl %ax, %eax +;; jne 0x71 +;; 83: movzwl %ax, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 8d: ud2 ;; 8f: ud2 +;; 91: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw8_xoru.wat b/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw8_xoru.wat index 699c3d7fe177..5c9253842bce 100644 --- a/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw8_xoru.wat +++ b/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw8_xoru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x14, %r11 ;; cmpq %rsp, %r11 -;; ja 0x78 +;; ja 0x7a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,6 +21,7 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; subq $4, %rsp ;; movl %eax, (%rsp) @@ -30,9 +31,9 @@ ;; movq %rax, %r11 ;; xorq %rcx, %r11 ;; lock cmpxchgb %r11b, (%rdx) -;; jne 0x5b -;; 6c: movzbl %al, %eax +;; jne 0x5d +;; 6e: movzbl %al, %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 78: ud2 +;; 7a: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw_xor.wat b/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw_xor.wat index 7baad5281bbc..cf0e18bd0931 100644 --- a/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw_xor.wat +++ b/tests/disas/winch/x64/atomic/rmw/xor/i32_atomic_rmw_xor.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x14, %r11 ;; cmpq %rsp, %r11 -;; ja 0x87 +;; ja 0x89 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x89 +;; jne 0x8b ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; subq $4, %rsp ;; movl %eax, (%rsp) @@ -34,9 +35,9 @@ ;; movq %rax, %r11 ;; xorq %rcx, %r11 ;; lock cmpxchgl %r11d, (%rdx) -;; jne 0x6d -;; 7e: addq $0x10, %rsp +;; jne 0x6f +;; 80: addq $0x10, %rsp ;; popq %rbp ;; retq -;; 87: ud2 ;; 89: ud2 +;; 8b: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw16_xoru.wat b/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw16_xoru.wat index 28ab3167a7b8..a97056891f73 100644 --- a/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw16_xoru.wat +++ b/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw16_xoru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x7c +;; ja 0x7e ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x7e +;; jne 0x80 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -32,10 +33,10 @@ ;; movq %rax, %r11 ;; xorq %rcx, %r11 ;; lock cmpxchgw %r11w, (%rdx) -;; jne 0x5d -;; 6f: movzwq %ax, %rax +;; jne 0x5f +;; 71: movzwq %ax, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 7c: ud2 ;; 7e: ud2 +;; 80: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw32_xoru.wat b/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw32_xoru.wat index 7c9007577207..e86fb4dac9d8 100644 --- a/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw32_xoru.wat +++ b/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw32_xoru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x75 +;; ja 0x77 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x77 +;; jne 0x79 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -32,9 +33,9 @@ ;; movq %rax, %r11 ;; xorq %rcx, %r11 ;; lock cmpxchgl %r11d, (%rdx) -;; jne 0x5b -;; 6c: addq $0x10, %rsp +;; jne 0x5d +;; 6e: addq $0x10, %rsp ;; popq %rbp ;; retq -;; 75: ud2 ;; 77: ud2 +;; 79: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw8_xoru.wat b/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw8_xoru.wat index 7e46836ca437..fc8fdb115c1c 100644 --- a/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw8_xoru.wat +++ b/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw8_xoru.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x67 +;; ja 0x69 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,6 +21,7 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -28,9 +29,9 @@ ;; movq %rax, %r11 ;; xorq %rcx, %r11 ;; lock cmpxchgb %r11b, (%rdx) -;; jne 0x49 -;; 5a: movzbq %al, %rax +;; jne 0x4b +;; 5c: movzbq %al, %rax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 67: ud2 +;; 69: ud2 diff --git a/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw_xor.wat b/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw_xor.wat index 5b42ab4712b8..ec89efebde08 100644 --- a/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw_xor.wat +++ b/tests/disas/winch/x64/atomic/rmw/xor/i64_atomic_rmw_xor.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x18, %r11 ;; cmpq %rsp, %r11 -;; ja 0x78 +;; ja 0x7a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,10 +21,11 @@ ;; movl $0, %ecx ;; andq $7, %rcx ;; cmpq $0, %rcx -;; jne 0x7a +;; jne 0x7c ;; 4a: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; pushq %rax ;; popq %rcx @@ -32,9 +33,9 @@ ;; movq %rax, %r11 ;; xorq %rcx, %r11 ;; lock cmpxchgq %r11, (%rdx) -;; jne 0x5e -;; 6f: addq $0x10, %rsp +;; jne 0x60 +;; 71: addq $0x10, %rsp ;; popq %rbp ;; retq -;; 78: ud2 ;; 7a: ud2 +;; 7c: ud2 diff --git a/tests/disas/winch/x64/atomic/store/i32_atomic_store.wat b/tests/disas/winch/x64/atomic/store/i32_atomic_store.wat index 7b42620f4a76..a900070a58c7 100644 --- a/tests/disas/winch/x64/atomic/store/i32_atomic_store.wat +++ b/tests/disas/winch/x64/atomic/store/i32_atomic_store.wat @@ -11,7 +11,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x65 +;; ja 0x67 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,15 +20,16 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x67 +;; jne 0x69 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movl %eax, (%rdx) ;; mfence ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 65: ud2 ;; 67: ud2 +;; 69: ud2 diff --git a/tests/disas/winch/x64/atomic/store/i32_atomic_store16.wat b/tests/disas/winch/x64/atomic/store/i32_atomic_store16.wat index 532bf26102ba..c2f2b03390e0 100644 --- a/tests/disas/winch/x64/atomic/store/i32_atomic_store16.wat +++ b/tests/disas/winch/x64/atomic/store/i32_atomic_store16.wat @@ -11,7 +11,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x66 +;; ja 0x68 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,15 +20,16 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x68 +;; jne 0x6a ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movw %ax, (%rdx) ;; mfence ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 66: ud2 ;; 68: ud2 +;; 6a: ud2 diff --git a/tests/disas/winch/x64/atomic/store/i32_atomic_store8.wat b/tests/disas/winch/x64/atomic/store/i32_atomic_store8.wat index 24f37436f32d..18ff87528199 100644 --- a/tests/disas/winch/x64/atomic/store/i32_atomic_store8.wat +++ b/tests/disas/winch/x64/atomic/store/i32_atomic_store8.wat @@ -11,7 +11,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x51 +;; ja 0x53 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,10 +20,11 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movb %al, (%rdx) ;; mfence ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 51: ud2 +;; 53: ud2 diff --git a/tests/disas/winch/x64/atomic/store/i64_atomic_store.wat b/tests/disas/winch/x64/atomic/store/i64_atomic_store.wat index cce1b25e9de2..9f931f720eaf 100644 --- a/tests/disas/winch/x64/atomic/store/i64_atomic_store.wat +++ b/tests/disas/winch/x64/atomic/store/i64_atomic_store.wat @@ -11,7 +11,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x68 +;; ja 0x6a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,15 +20,16 @@ ;; movl $0, %ecx ;; andq $7, %rcx ;; cmpq $0, %rcx -;; jne 0x6a +;; jne 0x6c ;; 4a: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movq %rax, (%rdx) ;; mfence ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 68: ud2 ;; 6a: ud2 +;; 6c: ud2 diff --git a/tests/disas/winch/x64/atomic/store/i64_atomic_store16.wat b/tests/disas/winch/x64/atomic/store/i64_atomic_store16.wat index 8afe4b346ef0..6d64ff49f8d6 100644 --- a/tests/disas/winch/x64/atomic/store/i64_atomic_store16.wat +++ b/tests/disas/winch/x64/atomic/store/i64_atomic_store16.wat @@ -11,7 +11,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x66 +;; ja 0x68 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,15 +20,16 @@ ;; movl $0, %ecx ;; andw $1, %cx ;; cmpw $0, %cx -;; jne 0x68 +;; jne 0x6a ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movw %ax, (%rdx) ;; mfence ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 66: ud2 ;; 68: ud2 +;; 6a: ud2 diff --git a/tests/disas/winch/x64/atomic/store/i64_atomic_store32.wat b/tests/disas/winch/x64/atomic/store/i64_atomic_store32.wat index 96817f3f6c74..c8a7d91f62eb 100644 --- a/tests/disas/winch/x64/atomic/store/i64_atomic_store32.wat +++ b/tests/disas/winch/x64/atomic/store/i64_atomic_store32.wat @@ -11,7 +11,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x65 +;; ja 0x67 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,15 +20,16 @@ ;; movl $0, %ecx ;; andl $3, %ecx ;; cmpl $0, %ecx -;; jne 0x67 +;; jne 0x69 ;; 48: movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movl %eax, (%rdx) ;; mfence ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 65: ud2 ;; 67: ud2 +;; 69: ud2 diff --git a/tests/disas/winch/x64/atomic/store/i64_atomic_store8.wat b/tests/disas/winch/x64/atomic/store/i64_atomic_store8.wat index 0379939c3b3d..a84bb1000752 100644 --- a/tests/disas/winch/x64/atomic/store/i64_atomic_store8.wat +++ b/tests/disas/winch/x64/atomic/store/i64_atomic_store8.wat @@ -11,7 +11,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x51 +;; ja 0x53 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,10 +20,11 @@ ;; movl $0, %ecx ;; movq 0x30(%r14), %r11 ;; movq (%r11), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movb %al, (%rdx) ;; mfence ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 51: ud2 +;; 53: ud2 diff --git a/tests/disas/winch/x64/atomic/wait/wait32_offset.wat b/tests/disas/winch/x64/atomic/wait/wait32_offset.wat index 8967c73b3128..ce8835b2ba0c 100644 --- a/tests/disas/winch/x64/atomic/wait/wait32_offset.wat +++ b/tests/disas/winch/x64/atomic/wait/wait32_offset.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x30, %r11 ;; cmpq %rsp, %r11 -;; ja 0x91 +;; ja 0x97 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -24,7 +24,8 @@ ;; movl $0, %ecx ;; movl $4, %edx ;; addq $8, %rdx -;; pushq %rdx +;; jb 0x99 +;; 4d: pushq %rdx ;; subq $4, %rsp ;; movl %ecx, (%rsp) ;; pushq %rax @@ -34,11 +35,12 @@ ;; movq 0x18(%rsp), %rdx ;; movl 0x14(%rsp), %ecx ;; movq 0xc(%rsp), %r8 -;; callq 0x182 +;; callq 0x18a ;; addq $0xc, %rsp ;; addq $0x14, %rsp ;; movq 8(%rsp), %r14 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 91: ud2 +;; 97: ud2 +;; 99: ud2 diff --git a/tests/disas/winch/x64/atomic/wait/wait64_offset.wat b/tests/disas/winch/x64/atomic/wait/wait64_offset.wat index a6410773301a..c09815702a4e 100644 --- a/tests/disas/winch/x64/atomic/wait/wait64_offset.wat +++ b/tests/disas/winch/x64/atomic/wait/wait64_offset.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x30, %r11 ;; cmpq %rsp, %r11 -;; ja 0x89 +;; ja 0x8f ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -24,7 +24,8 @@ ;; movl $0, %ecx ;; movl $4, %edx ;; addq $8, %rdx -;; pushq %rdx +;; jb 0x91 +;; 4d: pushq %rdx ;; pushq %rcx ;; pushq %rax ;; subq $8, %rsp @@ -33,11 +34,12 @@ ;; movq 0x18(%rsp), %rdx ;; movq 0x10(%rsp), %rcx ;; movq 8(%rsp), %r8 -;; callq 0x17a +;; callq 0x182 ;; addq $8, %rsp ;; addq $0x18, %rsp ;; movq 8(%rsp), %r14 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 89: ud2 +;; 8f: ud2 +;; 91: ud2 diff --git a/tests/disas/winch/x64/call_indirect/call_indirect.wat b/tests/disas/winch/x64/call_indirect/call_indirect.wat index 667a67a86524..5ecf92d14cb0 100644 --- a/tests/disas/winch/x64/call_indirect/call_indirect.wat +++ b/tests/disas/winch/x64/call_indirect/call_indirect.wat @@ -37,7 +37,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x30, %r11 ;; cmpq %rsp, %r11 -;; ja 0x229 +;; ja 0x22b ;; 1c: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -50,7 +50,7 @@ ;; testl %eax, %eax ;; je 0x55 ;; 4b: movl $1, %eax -;; jmp 0x220 +;; jmp 0x222 ;; 55: movl 0xc(%rsp), %eax ;; subl $2, %eax ;; subq $4, %rsp @@ -59,37 +59,37 @@ ;; movq %r14, %rdx ;; movq 0x38(%rdx), %rbx ;; cmpq %rbx, %rcx -;; jae 0x22b +;; jae 0x22d ;; 7d: movq %rcx, %r11 ;; imulq $8, %r11, %r11 ;; movq 0x30(%rdx), %rdx ;; movq %rdx, %rsi ;; addq %r11, %rdx -;; cmpl %ebx, %ecx +;; cmpq %rbx, %rcx ;; cmovaeq %rsi, %rdx ;; movq (%rdx), %rax ;; testq %rax, %rax -;; jne 0xdd -;; a3: subq $4, %rsp +;; jne 0xde +;; a4: subq $4, %rsp ;; movl %ecx, (%rsp) ;; subq $8, %rsp ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 8(%rsp), %edx -;; callq 0x334 +;; callq 0x336 ;; addq $8, %rsp ;; addq $4, %rsp ;; movq 0x1c(%rsp), %r14 -;; jmp 0xe3 -;; dd: andq $0xfffffffffffffffe, %rax +;; jmp 0xe4 +;; de: andq $0xfffffffffffffffe, %rax ;; testq %rax, %rax -;; je 0x22d -;; ec: movq 0x28(%r14), %r11 +;; je 0x22f +;; ed: movq 0x28(%r14), %r11 ;; movl (%r11), %ecx ;; movl 0x10(%rax), %edx ;; cmpl %edx, %ecx -;; jne 0x22f -;; fe: pushq %rax +;; jne 0x231 +;; ff: pushq %rax ;; popq %rcx ;; movq 0x18(%rcx), %r8 ;; movq 8(%rcx), %rbx @@ -111,37 +111,37 @@ ;; movq %r14, %rdx ;; movq 0x38(%rdx), %rbx ;; cmpq %rbx, %rcx -;; jae 0x231 -;; 161: movq %rcx, %r11 +;; jae 0x233 +;; 162: movq %rcx, %r11 ;; imulq $8, %r11, %r11 ;; movq 0x30(%rdx), %rdx ;; movq %rdx, %rsi ;; addq %r11, %rdx -;; cmpl %ebx, %ecx +;; cmpq %rbx, %rcx ;; cmovaeq %rsi, %rdx ;; movq (%rdx), %rax ;; testq %rax, %rax -;; jne 0x1c1 -;; 187: subq $4, %rsp +;; jne 0x1c3 +;; 189: subq $4, %rsp ;; movl %ecx, (%rsp) ;; subq $4, %rsp ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 4(%rsp), %edx -;; callq 0x334 +;; callq 0x336 ;; addq $4, %rsp ;; addq $4, %rsp ;; movq 0x20(%rsp), %r14 -;; jmp 0x1c7 -;; 1c1: andq $0xfffffffffffffffe, %rax +;; jmp 0x1c9 +;; 1c3: andq $0xfffffffffffffffe, %rax ;; testq %rax, %rax -;; je 0x233 -;; 1d0: movq 0x28(%r14), %r11 +;; je 0x235 +;; 1d2: movq 0x28(%r14), %r11 ;; movl (%r11), %ecx ;; movl 0x10(%rax), %edx ;; cmpl %edx, %ecx -;; jne 0x235 -;; 1e2: pushq %rax +;; jne 0x237 +;; 1e4: pushq %rax ;; popq %rcx ;; movq 0x18(%rcx), %r8 ;; movq 8(%rcx), %rbx @@ -160,10 +160,10 @@ ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 229: ud2 ;; 22b: ud2 ;; 22d: ud2 ;; 22f: ud2 ;; 231: ud2 ;; 233: ud2 ;; 235: ud2 +;; 237: ud2 diff --git a/tests/disas/winch/x64/call_indirect/local_arg.wat b/tests/disas/winch/x64/call_indirect/local_arg.wat index a061482e746e..595446e9795a 100644 --- a/tests/disas/winch/x64/call_indirect/local_arg.wat +++ b/tests/disas/winch/x64/call_indirect/local_arg.wat @@ -42,7 +42,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x30, %r11 ;; cmpq %rsp, %r11 -;; ja 0x156 +;; ja 0x157 ;; 5c: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -55,37 +55,37 @@ ;; movq %r14, %rdx ;; movq 0x38(%rdx), %rbx ;; cmpq %rbx, %rcx -;; jae 0x158 +;; jae 0x159 ;; 9e: movq %rcx, %r11 ;; imulq $8, %r11, %r11 ;; movq 0x30(%rdx), %rdx ;; movq %rdx, %rsi ;; addq %r11, %rdx -;; cmpl %ebx, %ecx +;; cmpq %rbx, %rcx ;; cmovaeq %rsi, %rdx ;; movq (%rdx), %rax ;; testq %rax, %rax -;; jne 0xfe -;; c4: subq $4, %rsp +;; jne 0xff +;; c5: subq $4, %rsp ;; movl %ecx, (%rsp) ;; subq $8, %rsp ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 8(%rsp), %edx -;; callq 0x31d +;; callq 0x31e ;; addq $8, %rsp ;; addq $4, %rsp ;; movq 0x1c(%rsp), %r14 -;; jmp 0x104 -;; fe: andq $0xfffffffffffffffe, %rax +;; jmp 0x105 +;; ff: andq $0xfffffffffffffffe, %rax ;; testq %rax, %rax -;; je 0x15a -;; 10d: movq 0x28(%r14), %r11 +;; je 0x15b +;; 10e: movq 0x28(%r14), %r11 ;; movl (%r11), %ecx ;; movl 0x10(%rax), %edx ;; cmpl %edx, %ecx -;; jne 0x15c -;; 11f: movq 0x18(%rax), %rbx +;; jne 0x15d +;; 120: movq 0x18(%rax), %rbx ;; movq 8(%rax), %rcx ;; subq $0xc, %rsp ;; movq %rbx, %rdi @@ -98,7 +98,7 @@ ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 156: ud2 -;; 158: ud2 -;; 15a: ud2 -;; 15c: ud2 +;; 157: ud2 +;; 159: ud2 +;; 15b: ud2 +;; 15d: ud2 diff --git a/tests/disas/winch/x64/load/f32.wat b/tests/disas/winch/x64/load/f32.wat index b8f518b9a78d..9fdc307e01e0 100644 --- a/tests/disas/winch/x64/load/f32.wat +++ b/tests/disas/winch/x64/load/f32.wat @@ -13,16 +13,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x48 +;; ja 0x4a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movss (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 48: ud2 +;; 4a: ud2 diff --git a/tests/disas/winch/x64/load/f64.wat b/tests/disas/winch/x64/load/f64.wat index 175b91e0487c..d496068814de 100644 --- a/tests/disas/winch/x64/load/f64.wat +++ b/tests/disas/winch/x64/load/f64.wat @@ -12,16 +12,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x48 +;; ja 0x4a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movsd (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 48: ud2 +;; 4a: ud2 diff --git a/tests/disas/winch/x64/load/grow_load.wat b/tests/disas/winch/x64/load/grow_load.wat index 5c88ae5e85eb..1a20b87706e7 100644 --- a/tests/disas/winch/x64/load/grow_load.wat +++ b/tests/disas/winch/x64/load/grow_load.wat @@ -34,7 +34,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x70, %r11 ;; cmpq %rsp, %r11 -;; ja 0x134 +;; ja 0x137 ;; 1c: movq %rsi, %r14 ;; subq $0x60, %rsp ;; movq %rsi, 0x58(%rsp) @@ -58,27 +58,28 @@ ;; subl $1, %eax ;; movl %eax, 0x60(%r14) ;; movq 0x40(%r14), %rax -;; shrl $0x10, %eax +;; shrq $0x10, %rax ;; subq $4, %rsp ;; movl %eax, (%rsp) ;; subq $0xc, %rsp ;; movq %r14, %rdi ;; movl 0xc(%rsp), %esi ;; movl $0, %edx -;; callq 0x2e1 +;; callq 0x2e9 ;; addq $0xc, %rsp ;; addq $4, %rsp ;; movq 0x58(%rsp), %r14 ;; movl %eax, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; addq $0x23024, %rcx ;; movsbq (%rcx), %rax -;; movss 0x5d(%rip), %xmm0 +;; movss 0x62(%rip), %xmm0 ;; subq $0xc, %rsp -;; movsd 0x55(%rip), %xmm15 +;; movsd 0x5a(%rip), %xmm15 ;; movsd %xmm15, (%rsp) -;; movss 0x3e(%rip), %xmm15 +;; movss 0x43(%rip), %xmm15 ;; movss %xmm15, 8(%rsp) ;; movq 0x14(%rsp), %rax ;; movsd (%rsp), %xmm15 @@ -90,13 +91,15 @@ ;; addq $0x60, %rsp ;; popq %rbp ;; retq -;; 134: ud2 -;; 136: addb %al, (%rax) -;; 138: addb %al, (%rax) -;; 13a: addb %al, (%rax) -;; 13c: addb %al, (%rax) -;; 13e: addb %al, (%rax) -;; 140: addb %al, (%rax) -;; 142: addb %al, (%rax) -;; 144: addb %al, (%rax) -;; 146: addb %al, (%rax) +;; 137: ud2 +;; 139: addb %al, (%rax) +;; 13b: addb %al, (%rax) +;; 13d: addb %al, (%rax) +;; 13f: addb %al, (%rax) +;; 141: addb %al, (%rax) +;; 143: addb %al, (%rax) +;; 145: addb %al, (%rax) +;; 147: addb %al, (%rax) +;; 149: addb %al, (%rax) +;; 14b: addb %al, (%rax) +;; 14d: addb %al, (%rax) diff --git a/tests/disas/winch/x64/load/i32.wat b/tests/disas/winch/x64/load/i32.wat index 437421af6714..6b073fc967fa 100644 --- a/tests/disas/winch/x64/load/i32.wat +++ b/tests/disas/winch/x64/load/i32.wat @@ -13,16 +13,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x46 +;; ja 0x48 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movl (%rcx), %eax ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 46: ud2 +;; 48: ud2 diff --git a/tests/disas/winch/x64/load/i64.wat b/tests/disas/winch/x64/load/i64.wat index 510e9f3932b3..658d26807202 100644 --- a/tests/disas/winch/x64/load/i64.wat +++ b/tests/disas/winch/x64/load/i64.wat @@ -14,7 +14,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0x61 +;; ja 0x65 ;; 1c: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -23,13 +23,15 @@ ;; movq 8(%rsp), %rax ;; movl $8, %ecx ;; movq 0x38(%r14), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movb %al, (%rdx) ;; movl $8, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movsbq (%rcx), %rax ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 61: ud2 +;; 65: ud2 diff --git a/tests/disas/winch/x64/load/v128.wat b/tests/disas/winch/x64/load/v128.wat index 7bdfebed682f..c961d2500e2f 100644 --- a/tests/disas/winch/x64/load/v128.wat +++ b/tests/disas/winch/x64/load/v128.wat @@ -13,16 +13,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x48 +;; ja 0x4a ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movdqu (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 48: ud2 +;; 4a: ud2 diff --git a/tests/disas/winch/x64/load/v128_load16_splat_avx2.wat b/tests/disas/winch/x64/load/v128_load16_splat_avx2.wat index a74f5160e003..ba8a93892743 100644 --- a/tests/disas/winch/x64/load/v128_load16_splat_avx2.wat +++ b/tests/disas/winch/x64/load/v128_load16_splat_avx2.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x49 +;; ja 0x4b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpbroadcastw (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 49: ud2 +;; 4b: ud2 diff --git a/tests/disas/winch/x64/load/v128_load16x4_s_avx.wat b/tests/disas/winch/x64/load/v128_load16x4_s_avx.wat index 40cb24794f0a..9ee5e73e3790 100644 --- a/tests/disas/winch/x64/load/v128_load16x4_s_avx.wat +++ b/tests/disas/winch/x64/load/v128_load16x4_s_avx.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x49 +;; ja 0x4b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpmovsxwd (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 49: ud2 +;; 4b: ud2 diff --git a/tests/disas/winch/x64/load/v128_load16x4_u_avx.wat b/tests/disas/winch/x64/load/v128_load16x4_u_avx.wat index 0ccb8a98ea3f..a73fd80c1aa8 100644 --- a/tests/disas/winch/x64/load/v128_load16x4_u_avx.wat +++ b/tests/disas/winch/x64/load/v128_load16x4_u_avx.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x49 +;; ja 0x4b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpmovzxwd (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 49: ud2 +;; 4b: ud2 diff --git a/tests/disas/winch/x64/load/v128_load32_splat_avx2.wat b/tests/disas/winch/x64/load/v128_load32_splat_avx2.wat index 00fa17aa47b9..cb86b36d6978 100644 --- a/tests/disas/winch/x64/load/v128_load32_splat_avx2.wat +++ b/tests/disas/winch/x64/load/v128_load32_splat_avx2.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x49 +;; ja 0x4b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpbroadcastd (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 49: ud2 +;; 4b: ud2 diff --git a/tests/disas/winch/x64/load/v128_load32_zero_avx.wat b/tests/disas/winch/x64/load/v128_load32_zero_avx.wat index 3591b14e3eed..ea090a4d0faf 100644 --- a/tests/disas/winch/x64/load/v128_load32_zero_avx.wat +++ b/tests/disas/winch/x64/load/v128_load32_zero_avx.wat @@ -14,17 +14,18 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x4c +;; ja 0x4e ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movl (%rcx), %r11d ;; vmovd %r11d, %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 4c: ud2 +;; 4e: ud2 diff --git a/tests/disas/winch/x64/load/v128_load32x2_s_avx.wat b/tests/disas/winch/x64/load/v128_load32x2_s_avx.wat index e01996df7590..b3a3c9c60069 100644 --- a/tests/disas/winch/x64/load/v128_load32x2_s_avx.wat +++ b/tests/disas/winch/x64/load/v128_load32x2_s_avx.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x49 +;; ja 0x4b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpmovsxdq (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 49: ud2 +;; 4b: ud2 diff --git a/tests/disas/winch/x64/load/v128_load32x2_s_oob_avx.wat b/tests/disas/winch/x64/load/v128_load32x2_s_oob_avx.wat index d34f49b48288..01f56b4eb664 100644 --- a/tests/disas/winch/x64/load/v128_load32x2_s_oob_avx.wat +++ b/tests/disas/winch/x64/load/v128_load32x2_s_oob_avx.wat @@ -14,15 +14,16 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x64 +;; ja 0x66 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0xfff9, %eax ;; cmpq $0xfff8, %rax -;; ja 0x66 +;; ja 0x68 ;; 40: movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movl $0, %edx ;; cmpq $0xfff8, %rax @@ -31,5 +32,5 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 64: ud2 ;; 66: ud2 +;; 68: ud2 diff --git a/tests/disas/winch/x64/load/v128_load32x2_u_avx.wat b/tests/disas/winch/x64/load/v128_load32x2_u_avx.wat index 911b0b0973cb..a48bc4f2dd49 100644 --- a/tests/disas/winch/x64/load/v128_load32x2_u_avx.wat +++ b/tests/disas/winch/x64/load/v128_load32x2_u_avx.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x49 +;; ja 0x4b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpmovzxdq (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 49: ud2 +;; 4b: ud2 diff --git a/tests/disas/winch/x64/load/v128_load32x2_u_oob_avx.wat b/tests/disas/winch/x64/load/v128_load32x2_u_oob_avx.wat index ba0c46d8b52b..911768638fca 100644 --- a/tests/disas/winch/x64/load/v128_load32x2_u_oob_avx.wat +++ b/tests/disas/winch/x64/load/v128_load32x2_u_oob_avx.wat @@ -14,15 +14,16 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x64 +;; ja 0x66 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0xfff9, %eax ;; cmpq $0xfff8, %rax -;; ja 0x66 +;; ja 0x68 ;; 40: movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movl $0, %edx ;; cmpq $0xfff8, %rax @@ -31,5 +32,5 @@ ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 64: ud2 ;; 66: ud2 +;; 68: ud2 diff --git a/tests/disas/winch/x64/load/v128_load64_splat_avx.wat b/tests/disas/winch/x64/load/v128_load64_splat_avx.wat index 5c64a8da91e2..8e465afb7d7d 100644 --- a/tests/disas/winch/x64/load/v128_load64_splat_avx.wat +++ b/tests/disas/winch/x64/load/v128_load64_splat_avx.wat @@ -14,17 +14,18 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x4d +;; ja 0x4f ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movsd (%rcx), %xmm0 ;; vpshufd $0x44, %xmm0, %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 4d: ud2 +;; 4f: ud2 diff --git a/tests/disas/winch/x64/load/v128_load64_zero_avx.wat b/tests/disas/winch/x64/load/v128_load64_zero_avx.wat index 2d555b2990dc..31fbcdc06057 100644 --- a/tests/disas/winch/x64/load/v128_load64_zero_avx.wat +++ b/tests/disas/winch/x64/load/v128_load64_zero_avx.wat @@ -14,17 +14,18 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x4c +;; ja 0x4e ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movq (%rcx), %r11 ;; vmovq %r11, %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 4c: ud2 +;; 4e: ud2 diff --git a/tests/disas/winch/x64/load/v128_load8_splat_avx2.wat b/tests/disas/winch/x64/load/v128_load8_splat_avx2.wat index 6ca90831c997..7e96449876c6 100644 --- a/tests/disas/winch/x64/load/v128_load8_splat_avx2.wat +++ b/tests/disas/winch/x64/load/v128_load8_splat_avx2.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x49 +;; ja 0x4b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpbroadcastb (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 49: ud2 +;; 4b: ud2 diff --git a/tests/disas/winch/x64/load/v128_load8x8_s_avx.wat b/tests/disas/winch/x64/load/v128_load8x8_s_avx.wat index 4df70e0ef0f0..5e321b7b3de7 100644 --- a/tests/disas/winch/x64/load/v128_load8x8_s_avx.wat +++ b/tests/disas/winch/x64/load/v128_load8x8_s_avx.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x49 +;; ja 0x4b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpmovsxbw (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 49: ud2 +;; 4b: ud2 diff --git a/tests/disas/winch/x64/load/v128_load8x8_u_avx.wat b/tests/disas/winch/x64/load/v128_load8x8_u_avx.wat index 7959ba3716ce..342e4325aa68 100644 --- a/tests/disas/winch/x64/load/v128_load8x8_u_avx.wat +++ b/tests/disas/winch/x64/load/v128_load8x8_u_avx.wat @@ -14,16 +14,17 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x49 +;; ja 0x4b ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) ;; movq %rsi, (%rsp) ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpmovzxbw (%rcx), %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 49: ud2 +;; 4b: ud2 diff --git a/tests/disas/winch/x64/ref/func.wat b/tests/disas/winch/x64/ref/func.wat new file mode 100644 index 000000000000..0bcad41049c6 --- /dev/null +++ b/tests/disas/winch/x64/ref/func.wat @@ -0,0 +1,47 @@ +;;! target = "x86_64" +;;! test = "winch" + +(module + (elem declare func $f) + (func $f) + (func (export "ref-func") (result funcref) + (ref.func $f) + ) +) +;; wasm[0]::function[0]::f: +;; pushq %rbp +;; movq %rsp, %rbp +;; movq 8(%rdi), %r11 +;; movq 0x18(%r11), %r11 +;; addq $0x10, %r11 +;; cmpq %rsp, %r11 +;; ja 0x38 +;; 1c: movq %rdi, %r14 +;; subq $0x10, %rsp +;; movq %rdi, 8(%rsp) +;; movq %rsi, (%rsp) +;; addq $0x10, %rsp +;; popq %rbp +;; retq +;; 38: ud2 +;; +;; wasm[0]::function[1]: +;; pushq %rbp +;; movq %rsp, %rbp +;; movq 8(%rdi), %r11 +;; movq 0x18(%r11), %r11 +;; addq $0x10, %r11 +;; cmpq %rsp, %r11 +;; ja 0x8a +;; 5c: movq %rdi, %r14 +;; subq $0x10, %rsp +;; movq %rdi, 8(%rsp) +;; movq %rsi, (%rsp) +;; movq %r14, %rdi +;; movl $0, %esi +;; callq 0x252 +;; movq 8(%rsp), %r14 +;; addq $0x10, %rsp +;; popq %rbp +;; retq +;; 8a: ud2 diff --git a/tests/disas/winch/x64/ref/is_null.wat b/tests/disas/winch/x64/ref/is_null.wat new file mode 100644 index 000000000000..53d5a08b3278 --- /dev/null +++ b/tests/disas/winch/x64/ref/is_null.wat @@ -0,0 +1,29 @@ +;;! target = "x86_64" +;;! test = "winch" + +(module + (func (export "ref-is-null") (param funcref) (result i32) + (ref.is_null (local.get 0)) + ) +) +;; wasm[0]::function[0]: +;; pushq %rbp +;; movq %rsp, %rbp +;; movq 8(%rdi), %r11 +;; movq 0x18(%r11), %r11 +;; addq $0x20, %r11 +;; cmpq %rsp, %r11 +;; ja 0x4f +;; 1c: movq %rdi, %r14 +;; subq $0x20, %rsp +;; movq %rdi, 0x18(%rsp) +;; movq %rsi, 0x10(%rsp) +;; movq %rdx, 8(%rsp) +;; movq 8(%rsp), %rax +;; cmpq $0, %rax +;; movl $0, %eax +;; sete %al +;; addq $0x20, %rsp +;; popq %rbp +;; retq +;; 4f: ud2 diff --git a/tests/disas/winch/x64/ref/null.wat b/tests/disas/winch/x64/ref/null.wat new file mode 100644 index 000000000000..c680c526038a --- /dev/null +++ b/tests/disas/winch/x64/ref/null.wat @@ -0,0 +1,25 @@ +;;! target = "x86_64" +;;! test = "winch" + +(module + (func (export "ref-null") (result funcref) + (ref.null func) + ) +) +;; wasm[0]::function[0]: +;; pushq %rbp +;; movq %rsp, %rbp +;; movq 8(%rdi), %r11 +;; movq 0x18(%r11), %r11 +;; addq $0x10, %r11 +;; cmpq %rsp, %r11 +;; ja 0x3d +;; 1c: movq %rdi, %r14 +;; subq $0x10, %rsp +;; movq %rdi, 8(%rsp) +;; movq %rsi, (%rsp) +;; movl $0, %eax +;; addq $0x10, %rsp +;; popq %rbp +;; retq +;; 3d: ud2 diff --git a/tests/disas/winch/x64/select/typed.wat b/tests/disas/winch/x64/select/typed.wat new file mode 100644 index 000000000000..e27730f121d6 --- /dev/null +++ b/tests/disas/winch/x64/select/typed.wat @@ -0,0 +1,33 @@ +;;! target = "x86_64" +;;! test = "winch" + +(module + (func (export "typed-select") (param funcref funcref i32) (result funcref) + (select (result funcref) (local.get 0) (local.get 1) (local.get 2)) + ) +) +;; wasm[0]::function[0]: +;; pushq %rbp +;; movq %rsp, %rbp +;; movq 8(%rdi), %r11 +;; movq 0x18(%r11), %r11 +;; addq $0x30, %r11 +;; cmpq %rsp, %r11 +;; ja 0x60 +;; 1c: movq %rdi, %r14 +;; subq $0x30, %rsp +;; movq %rdi, 0x28(%rsp) +;; movq %rsi, 0x20(%rsp) +;; movq %rdx, 0x18(%rsp) +;; movq %rcx, 0x10(%rsp) +;; movl %r8d, 0xc(%rsp) +;; movl 0xc(%rsp), %eax +;; movq 0x10(%rsp), %rcx +;; movq 0x18(%rsp), %rdx +;; cmpl $0, %eax +;; cmovneq %rdx, %rcx +;; movq %rcx, %rax +;; addq $0x30, %rsp +;; popq %rbp +;; retq +;; 60: ud2 diff --git a/tests/disas/winch/x64/store/f32.wat b/tests/disas/winch/x64/store/f32.wat index 542b3f784abf..9b4435c53046 100644 --- a/tests/disas/winch/x64/store/f32.wat +++ b/tests/disas/winch/x64/store/f32.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x50 +;; ja 0x52 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,13 +20,13 @@ ;; movss 0x21(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movss %xmm0, (%rcx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 50: ud2 -;; 52: addb %al, (%rax) +;; 52: ud2 ;; 54: addb %al, (%rax) ;; 56: addb %al, (%rax) ;; 58: addb %al, (%rax) diff --git a/tests/disas/winch/x64/store/f64.wat b/tests/disas/winch/x64/store/f64.wat index e3c936e58b07..8bcea2421d6e 100644 --- a/tests/disas/winch/x64/store/f64.wat +++ b/tests/disas/winch/x64/store/f64.wat @@ -13,7 +13,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x50 +;; ja 0x52 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -21,13 +21,13 @@ ;; movsd 0x21(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movsd %xmm0, (%rcx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 50: ud2 -;; 52: addb %al, (%rax) +;; 52: ud2 ;; 54: addb %al, (%rax) ;; 56: addb %al, (%rax) ;; 58: addb %al, (%rax) diff --git a/tests/disas/winch/x64/store/i32.wat b/tests/disas/winch/x64/store/i32.wat index c27f5a08f6a4..b5e656d808c6 100644 --- a/tests/disas/winch/x64/store/i32.wat +++ b/tests/disas/winch/x64/store/i32.wat @@ -14,7 +14,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x4b +;; ja 0x4d ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -22,9 +22,10 @@ ;; movl $1, %eax ;; movl $0, %ecx ;; movq 0x38(%r14), %rdx +;; movl %ecx, %ecx ;; addq %rcx, %rdx ;; movl %eax, (%rdx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 4b: ud2 +;; 4d: ud2 diff --git a/tests/disas/winch/x64/store/oob.wat b/tests/disas/winch/x64/store/oob.wat index 22ff78b52c40..8d32715bb5ef 100644 --- a/tests/disas/winch/x64/store/oob.wat +++ b/tests/disas/winch/x64/store/oob.wat @@ -17,7 +17,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0x86 +;; ja 0x88 ;; 1c: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -29,10 +29,11 @@ ;; movl %ecx, %ebx ;; movabsq $0x100000000, %r11 ;; addq %r11, %rbx -;; jb 0x88 +;; jb 0x8a ;; 56: cmpq %rdx, %rbx -;; ja 0x8a +;; ja 0x8c ;; 5f: movq 0x38(%r14), %rsi +;; movl %ecx, %ecx ;; addq %rcx, %rsi ;; movl $0xffffffff, %r11d ;; addq %r11, %rsi @@ -43,6 +44,6 @@ ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 86: ud2 ;; 88: ud2 ;; 8a: ud2 +;; 8c: ud2 diff --git a/tests/disas/winch/x64/store/v128.wat b/tests/disas/winch/x64/store/v128.wat index 665e81f482a0..d8f87f3dcec6 100644 --- a/tests/disas/winch/x64/store/v128.wat +++ b/tests/disas/winch/x64/store/v128.wat @@ -12,7 +12,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x50 +;; ja 0x52 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -20,13 +20,13 @@ ;; movdqu 0x29(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movdqu %xmm0, (%rcx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 50: ud2 -;; 52: addb %al, (%rax) +;; 52: ud2 ;; 54: addb %al, (%rax) ;; 56: addb %al, (%rax) ;; 58: addb %al, (%rax) diff --git a/tests/disas/winch/x64/table/fill.wat b/tests/disas/winch/x64/table/fill.wat index 0164d54991bb..cee222f6e751 100644 --- a/tests/disas/winch/x64/table/fill.wat +++ b/tests/disas/winch/x64/table/fill.wat @@ -78,7 +78,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x40, %r11 ;; cmpq %rsp, %r11 -;; ja 0x1f9 +;; ja 0x1fa ;; dc: movq %rdi, %r14 ;; subq $0x30, %rsp ;; movq %rdi, 0x28(%rsp) @@ -96,29 +96,29 @@ ;; movq %r14, %rdx ;; movq 0x38(%rdx), %rbx ;; cmpq %rbx, %rcx -;; jae 0x1fb +;; jae 0x1fc ;; 138: movq %rcx, %r11 ;; imulq $8, %r11, %r11 ;; movq 0x30(%rdx), %rdx ;; movq %rdx, %rsi ;; addq %r11, %rdx -;; cmpl %ebx, %ecx +;; cmpq %rbx, %rcx ;; cmovaeq %rsi, %rdx ;; movq (%rdx), %rax ;; testq %rax, %rax -;; jne 0x198 -;; 15e: subq $4, %rsp +;; jne 0x199 +;; 15f: subq $4, %rsp ;; movl %ecx, (%rsp) ;; subq $0xc, %rsp ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 0xc(%rsp), %edx -;; callq 0x4e6 +;; callq 0x4e7 ;; addq $0xc, %rsp ;; addq $4, %rsp ;; movq 0x28(%rsp), %r14 -;; jmp 0x19e -;; 198: andq $0xfffffffffffffffe, %rax +;; jmp 0x19f +;; 199: andq $0xfffffffffffffffe, %rax ;; movq %rax, 0xc(%rsp) ;; movl 0x1c(%rsp), %r11d ;; subq $4, %rsp @@ -133,11 +133,11 @@ ;; movl 0xc(%rsp), %edx ;; movq 4(%rsp), %rcx ;; movl (%rsp), %r8d -;; callq 0x511 +;; callq 0x512 ;; addq $0x10, %rsp ;; movq 0x28(%rsp), %r14 ;; addq $0x30, %rsp ;; popq %rbp ;; retq -;; 1f9: ud2 -;; 1fb: ud2 +;; 1fa: ud2 +;; 1fc: ud2 diff --git a/tests/disas/winch/x64/table/get.wat b/tests/disas/winch/x64/table/get.wat index 934ceff6fa1f..12d90ebd8844 100644 --- a/tests/disas/winch/x64/table/get.wat +++ b/tests/disas/winch/x64/table/get.wat @@ -34,7 +34,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x30, %r11 ;; cmpq %rsp, %r11 -;; ja 0x10d +;; ja 0x10e ;; 5c: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -48,31 +48,31 @@ ;; movq %r14, %rdx ;; movq 0x38(%rdx), %rbx ;; cmpq %rbx, %rcx -;; jae 0x10f +;; jae 0x110 ;; 9e: movq %rcx, %r11 ;; imulq $8, %r11, %r11 ;; movq 0x30(%rdx), %rdx ;; movq %rdx, %rsi ;; addq %r11, %rdx -;; cmpl %ebx, %ecx +;; cmpq %rbx, %rcx ;; cmovaeq %rsi, %rdx ;; movq (%rdx), %rax ;; testq %rax, %rax -;; jne 0xfe -;; c4: subq $4, %rsp +;; jne 0xff +;; c5: subq $4, %rsp ;; movl %ecx, (%rsp) ;; subq $0xc, %rsp ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 0xc(%rsp), %edx -;; callq 0x2e5 +;; callq 0x2e6 ;; addq $0xc, %rsp ;; addq $4, %rsp ;; movq 0x18(%rsp), %r14 -;; jmp 0x104 -;; fe: andq $0xfffffffffffffffe, %rax +;; jmp 0x105 +;; ff: andq $0xfffffffffffffffe, %rax ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 10d: ud2 -;; 10f: ud2 +;; 10e: ud2 +;; 110: ud2 diff --git a/tests/disas/winch/x64/table/grow.wat b/tests/disas/winch/x64/table/grow.wat index 8994afbea2a1..46284223f8b0 100644 --- a/tests/disas/winch/x64/table/grow.wat +++ b/tests/disas/winch/x64/table/grow.wat @@ -17,7 +17,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x30, %r11 ;; cmpq %rsp, %r11 -;; ja 0x76 +;; ja 0x78 ;; 1c: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -30,11 +30,12 @@ ;; movl $0, %esi ;; movl $0xa, %edx ;; movq 8(%rsp), %rcx -;; callq 0x177 +;; callq 0x179 ;; addq $8, %rsp ;; addq $8, %rsp ;; movq 0x18(%rsp), %r14 +;; movl %eax, %eax ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 76: ud2 +;; 78: ud2 diff --git a/tests/disas/winch/x64/table/init_copy_drop.wat b/tests/disas/winch/x64/table/init_copy_drop.wat index 402bbc1b8f8c..91514ca40b96 100644 --- a/tests/disas/winch/x64/table/init_copy_drop.wat +++ b/tests/disas/winch/x64/table/init_copy_drop.wat @@ -142,11 +142,11 @@ ;; movl $7, %ecx ;; movl $0, %r8d ;; movl $4, %r9d -;; callq 0x939 +;; callq 0x93a ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $1, %esi -;; callq 0x984 +;; callq 0x985 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -154,11 +154,11 @@ ;; movl $0xf, %ecx ;; movl $1, %r8d ;; movl $3, %r9d -;; callq 0x939 +;; callq 0x93a ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $3, %esi -;; callq 0x984 +;; callq 0x985 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -166,7 +166,7 @@ ;; movl $0x14, %ecx ;; movl $0xf, %r8d ;; movl $5, %r9d -;; callq 0x8ee +;; callq 0x8ef ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -174,7 +174,7 @@ ;; movl $0x15, %ecx ;; movl $0x1d, %r8d ;; movl $1, %r9d -;; callq 0x8ee +;; callq 0x8ef ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -182,7 +182,7 @@ ;; movl $0x18, %ecx ;; movl $0xa, %r8d ;; movl $1, %r9d -;; callq 0x8ee +;; callq 0x8ef ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -190,7 +190,7 @@ ;; movl $0xd, %ecx ;; movl $0xb, %r8d ;; movl $4, %r9d -;; callq 0x8ee +;; callq 0x8ef ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -198,7 +198,7 @@ ;; movl $0x13, %ecx ;; movl $0x14, %r8d ;; movl $5, %r9d -;; callq 0x8ee +;; callq 0x8ef ;; movq 8(%rsp), %r14 ;; addq $0x10, %rsp ;; popq %rbp @@ -212,7 +212,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x30, %r11 ;; cmpq %rsp, %r11 -;; ja 0x3c5 +;; ja 0x3c6 ;; 2dc: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -226,37 +226,37 @@ ;; movq %r14, %rdx ;; movq 0xb0(%rdx), %rbx ;; cmpq %rbx, %rcx -;; jae 0x3c7 +;; jae 0x3c8 ;; 321: movq %rcx, %r11 ;; imulq $8, %r11, %r11 ;; movq 0xa8(%rdx), %rdx ;; movq %rdx, %rsi ;; addq %r11, %rdx -;; cmpl %ebx, %ecx +;; cmpq %rbx, %rcx ;; cmovaeq %rsi, %rdx ;; movq (%rdx), %rax ;; testq %rax, %rax -;; jne 0x384 -;; 34a: subq $4, %rsp +;; jne 0x385 +;; 34b: subq $4, %rsp ;; movl %ecx, (%rsp) ;; subq $0xc, %rsp ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 0xc(%rsp), %edx -;; callq 0x9cc +;; callq 0x9cd ;; addq $0xc, %rsp ;; addq $4, %rsp ;; movq 0x18(%rsp), %r14 -;; jmp 0x38a -;; 384: andq $0xfffffffffffffffe, %rax +;; jmp 0x38b +;; 385: andq $0xfffffffffffffffe, %rax ;; testq %rax, %rax -;; je 0x3c9 -;; 393: movq 0x28(%r14), %r11 +;; je 0x3ca +;; 394: movq 0x28(%r14), %r11 ;; movl (%r11), %ecx ;; movl 0x10(%rax), %edx ;; cmpl %edx, %ecx -;; jne 0x3cb -;; 3a5: pushq %rax +;; jne 0x3cc +;; 3a6: pushq %rax ;; popq %rcx ;; movq 0x18(%rcx), %rbx ;; movq 8(%rcx), %rdx @@ -267,7 +267,7 @@ ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 3c5: ud2 -;; 3c7: ud2 -;; 3c9: ud2 -;; 3cb: ud2 +;; 3c6: ud2 +;; 3c8: ud2 +;; 3ca: ud2 +;; 3cc: ud2 diff --git a/tests/disas/winch/x64/table/set.wat b/tests/disas/winch/x64/table/set.wat index 8ec3ab16217b..9dd27dedaa6f 100644 --- a/tests/disas/winch/x64/table/set.wat +++ b/tests/disas/winch/x64/table/set.wat @@ -39,7 +39,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x20, %r11 ;; cmpq %rsp, %r11 -;; ja 0xbc +;; ja 0xbd ;; 5c: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) @@ -51,21 +51,21 @@ ;; movq %r14, %rdx ;; movq 0x38(%rdx), %rbx ;; cmpq %rbx, %rcx -;; jae 0xbe +;; jae 0xbf ;; 90: movq %rcx, %r11 ;; imulq $8, %r11, %r11 ;; movq 0x30(%rdx), %rdx ;; movq %rdx, %rsi ;; addq %r11, %rdx -;; cmpl %ebx, %ecx +;; cmpq %rbx, %rcx ;; cmovaeq %rsi, %rdx ;; orq $1, %rax ;; movq %rax, (%rdx) ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; bc: ud2 -;; be: ud2 +;; bd: ud2 +;; bf: ud2 ;; ;; wasm[0]::function[2]: ;; pushq %rbp @@ -74,8 +74,8 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x30, %r11 ;; cmpq %rsp, %r11 -;; ja 0x1de -;; dc: movq %rdi, %r14 +;; ja 0x1f0 +;; ec: movq %rdi, %r14 ;; subq $0x20, %rsp ;; movq %rdi, 0x18(%rsp) ;; movq %rsi, 0x10(%rsp) @@ -92,47 +92,47 @@ ;; movq %r14, %rdx ;; movq 0x38(%rdx), %rbx ;; cmpq %rbx, %rcx -;; jae 0x1e0 -;; 132: movq %rcx, %r11 +;; jae 0x1f2 +;; 142: movq %rcx, %r11 ;; imulq $8, %r11, %r11 ;; movq 0x30(%rdx), %rdx ;; movq %rdx, %rsi ;; addq %r11, %rdx -;; cmpl %ebx, %ecx +;; cmpq %rbx, %rcx ;; cmovaeq %rsi, %rdx ;; movq (%rdx), %rax ;; testq %rax, %rax -;; jne 0x192 -;; 158: subq $4, %rsp +;; jne 0x1a3 +;; 169: subq $4, %rsp ;; movl %ecx, (%rsp) ;; subq $8, %rsp ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 8(%rsp), %edx -;; callq 0x4a5 +;; callq 0x4b7 ;; addq $8, %rsp ;; addq $4, %rsp ;; movq 0x1c(%rsp), %r14 -;; jmp 0x198 -;; 192: andq $0xfffffffffffffffe, %rax +;; jmp 0x1a9 +;; 1a3: andq $0xfffffffffffffffe, %rax ;; movl (%rsp), %ecx ;; addq $4, %rsp ;; movq %r14, %rdx ;; movq 0x38(%rdx), %rbx ;; cmpq %rbx, %rcx -;; jae 0x1e2 -;; 1b2: movq %rcx, %r11 +;; jae 0x1f4 +;; 1c3: movq %rcx, %r11 ;; imulq $8, %r11, %r11 ;; movq 0x30(%rdx), %rdx ;; movq %rdx, %rsi ;; addq %r11, %rdx -;; cmpl %ebx, %ecx +;; cmpq %rbx, %rcx ;; cmovaeq %rsi, %rdx ;; orq $1, %rax ;; movq %rax, (%rdx) ;; addq $0x20, %rsp ;; popq %rbp ;; retq -;; 1de: ud2 -;; 1e0: ud2 -;; 1e2: ud2 +;; 1f0: ud2 +;; 1f2: ud2 +;; 1f4: ud2 diff --git a/tests/disas/winch/x64/v128_ops/load_lane/load16.wat b/tests/disas/winch/x64/v128_ops/load_lane/load16.wat index 709de01ca258..868ae7ef93d2 100644 --- a/tests/disas/winch/x64/v128_ops/load_lane/load16.wat +++ b/tests/disas/winch/x64/v128_ops/load_lane/load16.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x56 +;; ja 0x58 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -23,14 +23,14 @@ ;; movdqu 0x29(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movzwq (%rcx), %r11 ;; vpinsrw $1, %r11d, %xmm0, %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 56: ud2 -;; 58: addb %al, (%rax) +;; 58: ud2 ;; 5a: addb %al, (%rax) ;; 5c: addb %al, (%rax) ;; 5e: addb %al, (%rax) diff --git a/tests/disas/winch/x64/v128_ops/load_lane/load32.wat b/tests/disas/winch/x64/v128_ops/load_lane/load32.wat index 5865941aeb85..a3c5d244a719 100644 --- a/tests/disas/winch/x64/v128_ops/load_lane/load32.wat +++ b/tests/disas/winch/x64/v128_ops/load_lane/load32.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x55 +;; ja 0x57 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -23,14 +23,14 @@ ;; movdqu 0x29(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movl (%rcx), %r11d ;; vpinsrd $1, %r11d, %xmm0, %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 55: ud2 -;; 57: addb %al, (%rax) +;; 57: ud2 ;; 59: addb %al, (%rax) ;; 5b: addb %al, (%rax) ;; 5d: addb %al, (%rax) diff --git a/tests/disas/winch/x64/v128_ops/load_lane/load64.wat b/tests/disas/winch/x64/v128_ops/load_lane/load64.wat index 1d06ec35c214..93086310b900 100644 --- a/tests/disas/winch/x64/v128_ops/load_lane/load64.wat +++ b/tests/disas/winch/x64/v128_ops/load_lane/load64.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x55 +;; ja 0x57 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -23,14 +23,14 @@ ;; movdqu 0x29(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movq (%rcx), %r11 ;; vpinsrq $1, %r11, %xmm0, %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 55: ud2 -;; 57: addb %al, (%rax) +;; 57: ud2 ;; 59: addb %al, (%rax) ;; 5b: addb %al, (%rax) ;; 5d: addb %al, (%rax) diff --git a/tests/disas/winch/x64/v128_ops/load_lane/load8.wat b/tests/disas/winch/x64/v128_ops/load_lane/load8.wat index 191a4bb31452..f5119c55630d 100644 --- a/tests/disas/winch/x64/v128_ops/load_lane/load8.wat +++ b/tests/disas/winch/x64/v128_ops/load_lane/load8.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x56 +;; ja 0x58 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -23,14 +23,14 @@ ;; movdqu 0x29(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; movzbq (%rcx), %r11 ;; vpinsrb $1, %r11d, %xmm0, %xmm0 ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 56: ud2 -;; 58: addb %al, (%rax) +;; 58: ud2 ;; 5a: addb %al, (%rax) ;; 5c: addb %al, (%rax) ;; 5e: addb %al, (%rax) diff --git a/tests/disas/winch/x64/v128_ops/store_lane/store16.wat b/tests/disas/winch/x64/v128_ops/store_lane/store16.wat index 80d135641a53..64a778736ad8 100644 --- a/tests/disas/winch/x64/v128_ops/store_lane/store16.wat +++ b/tests/disas/winch/x64/v128_ops/store_lane/store16.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x52 +;; ja 0x54 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -23,13 +23,13 @@ ;; movdqu 0x29(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpextrw $1, %xmm0, (%rcx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 52: ud2 -;; 54: addb %al, (%rax) +;; 54: ud2 ;; 56: addb %al, (%rax) ;; 58: addb %al, (%rax) ;; 5a: addb %al, (%rax) diff --git a/tests/disas/winch/x64/v128_ops/store_lane/store32.wat b/tests/disas/winch/x64/v128_ops/store_lane/store32.wat index a9d2dc1ef6f7..5d0de5d2f9d8 100644 --- a/tests/disas/winch/x64/v128_ops/store_lane/store32.wat +++ b/tests/disas/winch/x64/v128_ops/store_lane/store32.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x52 +;; ja 0x54 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -23,13 +23,13 @@ ;; movdqu 0x29(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpextrd $1, %xmm0, (%rcx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 52: ud2 -;; 54: addb %al, (%rax) +;; 54: ud2 ;; 56: addb %al, (%rax) ;; 58: addb %al, (%rax) ;; 5a: addb %al, (%rax) diff --git a/tests/disas/winch/x64/v128_ops/store_lane/store64.wat b/tests/disas/winch/x64/v128_ops/store_lane/store64.wat index 1b3849f7a38d..88f6b69db736 100644 --- a/tests/disas/winch/x64/v128_ops/store_lane/store64.wat +++ b/tests/disas/winch/x64/v128_ops/store_lane/store64.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x52 +;; ja 0x54 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -23,13 +23,13 @@ ;; movdqu 0x29(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpextrq $1, %xmm0, (%rcx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 52: ud2 -;; 54: addb %al, (%rax) +;; 54: ud2 ;; 56: addb %al, (%rax) ;; 58: addb %al, (%rax) ;; 5a: addb %al, (%rax) diff --git a/tests/disas/winch/x64/v128_ops/store_lane/store8.wat b/tests/disas/winch/x64/v128_ops/store_lane/store8.wat index 6ae64d7886a1..8417c548459e 100644 --- a/tests/disas/winch/x64/v128_ops/store_lane/store8.wat +++ b/tests/disas/winch/x64/v128_ops/store_lane/store8.wat @@ -15,7 +15,7 @@ ;; movq 0x18(%r11), %r11 ;; addq $0x10, %r11 ;; cmpq %rsp, %r11 -;; ja 0x52 +;; ja 0x54 ;; 1c: movq %rdi, %r14 ;; subq $0x10, %rsp ;; movq %rdi, 8(%rsp) @@ -23,13 +23,13 @@ ;; movdqu 0x29(%rip), %xmm0 ;; movl $0, %eax ;; movq 0x38(%r14), %rcx +;; movl %eax, %eax ;; addq %rax, %rcx ;; vpextrb $1, %xmm0, (%rcx) ;; addq $0x10, %rsp ;; popq %rbp ;; retq -;; 52: ud2 -;; 54: addb %al, (%rax) +;; 54: ud2 ;; 56: addb %al, (%rax) ;; 58: addb %al, (%rax) ;; 5a: addb %al, (%rax) diff --git a/tests/misc_testsuite/aarch64-extr.wast b/tests/misc_testsuite/aarch64-extr.wast new file mode 100644 index 000000000000..ff48043ee577 --- /dev/null +++ b/tests/misc_testsuite/aarch64-extr.wast @@ -0,0 +1,11 @@ +(module + (memory 1) + (func (export "a64-extr") (param i32 i32) (result i32) + (i32.or + (i32.shl (local.get 0) (i32.const 32)) + (i32.shr_u (local.get 1) (i32.const 0)) + ) + ) +) + +(assert_return (invoke "a64-extr" (i32.const 65536) (i32.const 0)) (i32.const 65536)) diff --git a/tests/misc_testsuite/big-memory-behavior.wast b/tests/misc_testsuite/big-memory-behavior.wast new file mode 100644 index 000000000000..d336193451dd --- /dev/null +++ b/tests/misc_testsuite/big-memory-behavior.wast @@ -0,0 +1,19 @@ +;;! hogs_memory = true + +(module + (memory 0xffff) + + (func (export "grow") (param i32) (result i32) + local.get 0 + memory.grow) + + (func (export "size") (result i32) + memory.size) +) + +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0xffff)) +(assert_return (invoke "size") (i32.const 0xffff)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0xffff)) +(assert_return (invoke "size") (i32.const 0x10000)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0x10000)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) diff --git a/tests/misc_testsuite/component-model/async/cancel-host.wast b/tests/misc_testsuite/component-model/async/cancel-host.wast index d4b5acaa0ddb..362bc7014d12 100644 --- a/tests/misc_testsuite/component-model/async/cancel-host.wast +++ b/tests/misc_testsuite/component-model/async/cancel-host.wast @@ -383,3 +383,94 @@ ) (assert_return (invoke "run")) + +;; If a host task completes, but the guest doesn't actually receive the +;; notification that it's done, it should be possible to cancel it. +(component + (import "host" (instance $host + (export "return-two-slowly" (func async (result s32))) + )) + + (core module $Mem (memory (export "mem") 1)) + (core instance $mem (instantiate $Mem)) + + (core module $m + (import "" "slow-sync" (func $slow-sync (result i32))) + (import "" "slow-async" (func $slow-async (param i32) (result i32))) + (import "" "subtask.cancel" (func $subtask.cancel (param i32) (result i32))) + (import "" "subtask.drop" (func $subtask.drop (param i32))) + (func (export "run") + (local $subtask i32) + + ;; start a subtask for `return-two-slowly` + (local.set $subtask (call $start-slow)) + + ;; Call `return-two-slowly` synchronously a few times. This gives a + ;; chance for the host to finish the previous task, which shouldn't + ;; interfere with the below... + (call $assert-eq (call $slow-sync) (i32.const 2)) + (call $assert-eq (call $slow-sync) (i32.const 2)) + (call $assert-eq (call $slow-sync) (i32.const 2)) + + ;; We've never seen the result of `$subtask`, so it should be safe to + ;; cancel. This should either yield that the subtask returned or that + ;; it's return was cancelled, it's up to hosts. + (call $assert-either (call $subtask.cancel (local.get $subtask)) + (i32.const 4) ;; RETURN_CANCELLED + (i32.const 2)) ;; RETURNED + (call $subtask.drop (local.get $subtask)) + ) + + ;; asserts param 0 == param 1 + (func $assert-eq (param i32 i32) + local.get 0 + local.get 1 + i32.ne + if unreachable end) + + ;; Asserts that param 0 is either equal to param 1 or param 2 + (func $assert-either (param i32 i32 i32) + local.get 0 + local.get 1 + i32.eq + if return end + local.get 0 + local.get 2 + i32.eq + if return end + unreachable) + + (func $start-slow (result i32) + (local $tmp i32) + + ;; Start slow, expect STARTED + (call $slow-async (i32.const 100)) + local.tee $tmp + i32.const 0xf + i32.and + i32.const 1 ;; STARTED + i32.ne + if unreachable end + local.get $tmp + i32.const 4 + i32.shr_u + ) + ) + (core func $slow-sync (canon lower (func $host "return-two-slowly") (memory $mem "mem"))) + (core func $slow-async (canon lower (func $host "return-two-slowly") async (memory $mem "mem"))) + (core func $subtask.cancel (canon subtask.cancel)) + (core func $subtask.drop (canon subtask.drop)) + (core instance $i (instantiate $m + (with "" (instance + (export "slow-sync" (func $slow-sync)) + (export "slow-async" (func $slow-async)) + (export "subtask.cancel" (func $subtask.cancel)) + (export "subtask.drop" (func $subtask.drop)) + )) + )) + + (func (export "run") async + (canon lift (core func $i "run"))) +) + +(assert_return (invoke "run")) diff --git a/tests/misc_testsuite/component-model/async/error-context.wast b/tests/misc_testsuite/component-model/async/error-context.wast index c1be6d80110e..8f6c00410b9c 100644 --- a/tests/misc_testsuite/component-model/async/error-context.wast +++ b/tests/misc_testsuite/component-model/async/error-context.wast @@ -34,3 +34,53 @@ (core func $error-context-drop (canon error-context.drop)) (core instance $i (instantiate $m (with "" (instance (export "error-context.drop" (func $error-context-drop)))))) ) + +;; Test edge-case behavior of `error-context.debug-message`. +(component definition $A + (core module $libc + (memory (export "memory") 1) + (global $bump (mut i32) (i32.const 100)) + (func (export "realloc") (param i32 i32 i32 i32) (result i32) + (local $ret i32) + (if (local.get 0) (then unreachable)) + (if (local.get 1) (then unreachable)) + (if (i32.ne (local.get 2) (i32.const 1)) (then unreachable)) + (local.set $ret (global.get $bump)) + (global.set $bump (i32.add (global.get $bump) (local.get 3))) + (local.get $ret) + ) + ) + (core instance $libc (instantiate $libc)) + + (core module $Core + (import "" "mem" (memory 1)) + (import "" "error-context.new" (func $error-context.new (param i32 i32) (result i32))) + (import "" "error-context.debug-message" (func $error-context.debug-message (param i32 i32))) + (import "" "error-context.drop" (func $error-context.drop (param i32))) + + (func (export "run") (param $dst i32) + (local $handle i32) + (i32.store8 (i32.const 0) (i32.const 0x61)) ;; 'a' + (local.set $handle (call $error-context.new (i32.const 0) (i32.const 1))) + (call $error-context.debug-message (local.get $handle) (local.get $dst)) + (call $error-context.drop (local.get $handle)) + ) + ) + + (core func $error-context.new (canon error-context.new (memory $libc "memory"))) + (core func $error-context.debug-message (canon error-context.debug-message (memory $libc "memory") (realloc (func $libc "realloc")))) + (core func $error-context.drop (canon error-context.drop)) + + (core instance $core (instantiate $Core (with "" (instance + (export "mem" (memory $libc "memory")) + (export "error-context.new" (func $error-context.new)) + (export "error-context.debug-message" (func $error-context.debug-message)) + (export "error-context.drop" (func $error-context.drop)) + )))) + + (func (export "run") (param "p" u32) (canon lift (core func $core "run"))) +) + +(component instance $A $A) +(assert_return (invoke "run" (u32.const 65528))) +(assert_trap (invoke "run" (u32.const 65532)) "invalid debug message pointer") diff --git a/tests/misc_testsuite/component-model/async/intra-futures.wast b/tests/misc_testsuite/component-model/async/intra-futures.wast index b8958712be3e..fe3b646dd7f2 100644 --- a/tests/misc_testsuite/component-model/async/intra-futures.wast +++ b/tests/misc_testsuite/component-model/async/intra-futures.wast @@ -52,4 +52,4 @@ (func (export "run") (canon lift (core func $i "run"))) ) -(assert_trap (invoke "run") "cannot read from and write to intra-component future with non-numeric payload") +(assert_trap (invoke "run") "cannot read from and write to intra-component future/stream with non-numeric payload") diff --git a/tests/misc_testsuite/component-model/async/intra-streams.wast b/tests/misc_testsuite/component-model/async/intra-streams.wast index b39f2e7f05de..b73af5df13cf 100644 --- a/tests/misc_testsuite/component-model/async/intra-streams.wast +++ b/tests/misc_testsuite/component-model/async/intra-streams.wast @@ -52,4 +52,4 @@ (func (export "run") (canon lift (core func $i "run"))) ) -(assert_trap (invoke "run") "cannot read from and write to intra-component stream with non-numeric payload") +(assert_trap (invoke "run") "cannot read from and write to intra-component future/stream with non-numeric payload") diff --git a/tests/misc_testsuite/component-model/async/many-params-with-retptr.wast b/tests/misc_testsuite/component-model/async/many-params-with-retptr.wast index 25624a66ffc3..664c1dc01466 100644 --- a/tests/misc_testsuite/component-model/async/many-params-with-retptr.wast +++ b/tests/misc_testsuite/component-model/async/many-params-with-retptr.wast @@ -2,6 +2,7 @@ ;;! reference_types = true ;;! gc_types = true ;;! multi_memory = true +;;! bulk_memory = true ;; This test (which was generated during fuzzing) composes a sync lower with an ;; async lift such that the maximum number of flat parameters _and_ a return diff --git a/tests/misc_testsuite/component-model/async/stream-cancel-finished-op.wast b/tests/misc_testsuite/component-model/async/stream-cancel-finished-op.wast new file mode 100644 index 000000000000..26cc8c200225 --- /dev/null +++ b/tests/misc_testsuite/component-model/async/stream-cancel-finished-op.wast @@ -0,0 +1,251 @@ +;;! component_model_async = true + +(component definition $A + (core module $libc (memory (export "mem") 1)) + (core instance $libc (instantiate $libc)) + + (core module $m + (import "" "mem" (memory 1)) + (import "" "stream.new" (func $stream.new (result i64))) + (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32))) + (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32))) + (import "" "stream.cancel-read" (func $stream.cancel-read (param i32) (result i32))) + (import "" "stream.cancel-write" (func $stream.cancel-write (param i32) (result i32))) + (import "" "stream.drop-writable" (func $stream.drop-writable (param i32))) + (import "" "stream.drop-readable" (func $stream.drop-readable (param i32))) + (import "" "waitable-set.new" (func $waitable-set.new (result i32))) + (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32))) + (import "" "waitable-set.poll" (func $waitable-set.poll (param i32 i32) (result i32))) + (import "" "waitable.join" (func $waitable.join (param i32) (param i32))) + (import "" "waitable-set.drop" (func $waitable-set.drop (param i32))) + (import "" "future.new" (func $future.new (result i64))) + (import "" "future.read" (func $future.read (param i32 i32) (result i32))) + (import "" "future.write" (func $future.write (param i32 i32) (result i32))) + (import "" "future.cancel-read" (func $future.cancel-read (param i32) (result i32))) + (import "" "future.cancel-write" (func $future.cancel-write (param i32) (result i32))) + (import "" "future.drop-writable" (func $future.drop-writable (param i32))) + (import "" "future.drop-readable" (func $future.drop-readable (param i32))) + + (table 5 funcref) + (elem (i32.const 0) + func + $witness-dropped-via-cancel-read + $witness-dropped-via-cancel-write + $witness-dropped-via-waitable-set.wait + $witness-dropped-via-waitable-set.poll + $witness-dropped-via-future.cancel-write + ) + + (func $witness-dropped-via-cancel-read (param i32) + (local $ret i32) + (local.set $ret (call $stream.cancel-read (local.get 0))) + (if (i32.ne (local.get $ret) (i32.const 0x01 (; DROPPED ;))) + (then unreachable)) + ) + + (func $witness-dropped-via-cancel-write (param i32) + (local $ret i32) + (local.set $ret (call $stream.cancel-write (local.get 0))) + (if (i32.ne (local.get $ret) (i32.const 0x01 (; DROPPED ;))) + (then unreachable)) + ) + + (func $witness-dropped-via-waitable-set.wait (param i32) + (local $ws i32) + (local.set $ws (call $waitable-set.new)) + (call $waitable.join (local.get 0) (local.get $ws)) + (call $waitable-set.wait (local.get $ws) (i32.const 4)) + drop ;; ignore the event + (call $waitable.join (local.get 0) (i32.const 0)) + (call $waitable-set.drop (local.get $ws)) + + (if (i32.ne (i32.load (i32.const 4)) (local.get 0)) + (then unreachable)) + (if (i32.ne (i32.load (i32.const 8)) (i32.const 0x01 (; DROPPED ;))) + (then unreachable)) + ) + + (func $witness-dropped-via-waitable-set.poll (param i32) + (local $ws i32) + (local.set $ws (call $waitable-set.new)) + (call $waitable.join (local.get 0) (local.get $ws)) + (call $waitable-set.poll (local.get $ws) (i32.const 4)) + drop ;; ignore the event + (call $waitable.join (local.get 0) (i32.const 0)) + (call $waitable-set.drop (local.get $ws)) + + (if (i32.ne (i32.load (i32.const 4)) (local.get 0)) + (then unreachable)) + (if (i32.ne (i32.load (i32.const 8)) (i32.const 0x01 (; DROPPED ;))) + (then unreachable)) + ) + + (func $witness-dropped-via-future.cancel-write (param i32) + (local $ret i32) + (local.set $ret (call $future.cancel-write (local.get 0))) + (if (i32.ne (local.get $ret) (i32.const 0x01 (; DROPPED ;))) + (then unreachable)) + ) + + (func (export "test-cancel-read") (param i32) + (local $ret64 i64) + (local $reader i32) + (local $writer i32) + (local $ret i32) + + ;; Create a new stream + (local.set $ret64 (call $stream.new)) + (local.set $reader (i32.wrap_i64 (local.get $ret64))) + (local.set $writer (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32)))) + + ;; Start a read which will block (no writer ready) + (local.set $ret (call $stream.read (local.get $reader) (i32.const 16) (i32.const 100))) + (if (i32.ne (local.get $ret) (i32.const -1 (; BLOCKED ;))) + (then unreachable)) + + ;; Drop the writer, which queues a DROPPED event for the pending read + (call $stream.drop-writable (local.get $writer)) + + ;; Receive the dropped event + local.get $reader + local.get 0 + call_indirect (param i32) + + ;; attempting to read again should fail + (drop (call $stream.read (local.get $reader) (i32.const 16) (i32.const 100))) + + unreachable + ) + + (func (export "test-cancel-write") (param i32) + (local $ret64 i64) + (local $reader i32) + (local $writer i32) + (local $ret i32) + + ;; Create a new stream + (local.set $ret64 (call $stream.new)) + (local.set $reader (i32.wrap_i64 (local.get $ret64))) + (local.set $writer (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32)))) + + ;; Write some data (will block since no reader is ready) + (local.set $ret (call $stream.write (local.get $writer) (i32.const 0) (i32.const 1))) + (if (i32.ne (local.get $ret) (i32.const -1 (; BLOCKED ;))) + (then unreachable)) + + ;; Drop the reader, which queues a DROPPED event for the pending write + (call $stream.drop-readable (local.get $reader)) + + ;; Receive the dropped event + local.get $writer + local.get 0 + call_indirect (param i32) + + ;; attempting to write again should fail + (drop (call $stream.write (local.get $writer) (i32.const 0) (i32.const 1))) + + unreachable + ) + + (func (export "test-cancel-future-write") (param i32) + (local $ret64 i64) + (local $reader i32) + (local $writer i32) + (local $ret i32) + + ;; Create a new future + (local.set $ret64 (call $future.new)) + (local.set $reader (i32.wrap_i64 (local.get $ret64))) + (local.set $writer (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32)))) + + ;; Write some data (will block since no reader is ready) + (local.set $ret (call $future.write (local.get $writer) (i32.const 0))) + (if (i32.ne (local.get $ret) (i32.const -1 (; BLOCKED ;))) + (then unreachable)) + + ;; Drop the reader, which queues a DROPPED event for the pending write + (call $future.drop-readable (local.get $reader)) + + ;; Receive the dropped event + local.get $writer + local.get 0 + call_indirect (param i32) + + ;; attempting to write again should fail + (drop (call $future.write (local.get $writer) (i32.const 0))) + + unreachable + ) + ) + + (core func $waitable-set.new (canon waitable-set.new)) + (core func $waitable.join (canon waitable.join)) + (core func $waitable-set.wait (canon waitable-set.wait (memory $libc "mem"))) + (core func $waitable-set.poll (canon waitable-set.poll (memory $libc "mem"))) + (core func $waitable-set.drop (canon waitable-set.drop)) + (type $s (stream u8)) + (core func $stream.new (canon stream.new $s)) + (core func $stream.read (canon stream.read $s async (memory $libc "mem"))) + (core func $stream.write (canon stream.write $s async (memory $libc "mem"))) + (core func $stream.cancel-read (canon stream.cancel-read $s)) + (core func $stream.cancel-write (canon stream.cancel-write $s)) + (core func $stream.drop-readable (canon stream.drop-readable $s)) + (core func $stream.drop-writable (canon stream.drop-writable $s)) + (type $f (future u8)) + (core func $future.new (canon future.new $f)) + (core func $future.read (canon future.read $f async (memory $libc "mem"))) + (core func $future.write (canon future.write $f async (memory $libc "mem"))) + (core func $future.cancel-read (canon future.cancel-read $f)) + (core func $future.cancel-write (canon future.cancel-write $f)) + (core func $future.drop-readable (canon future.drop-readable $f)) + (core func $future.drop-writable (canon future.drop-writable $f)) + + (core instance $i (instantiate $m (with "" (instance + (export "mem" (memory $libc "mem")) + (export "stream.new" (func $stream.new)) + (export "stream.read" (func $stream.read)) + (export "stream.write" (func $stream.write)) + (export "stream.cancel-read" (func $stream.cancel-read)) + (export "stream.cancel-write" (func $stream.cancel-write)) + (export "stream.drop-writable" (func $stream.drop-writable)) + (export "stream.drop-readable" (func $stream.drop-readable)) + (export "waitable-set.new" (func $waitable-set.new)) + (export "waitable.join" (func $waitable.join)) + (export "waitable-set.wait" (func $waitable-set.wait)) + (export "waitable-set.poll" (func $waitable-set.poll)) + (export "waitable-set.drop" (func $waitable-set.drop)) + (export "future.new" (func $future.new)) + (export "future.read" (func $future.read)) + (export "future.write" (func $future.write)) + (export "future.cancel-read" (func $future.cancel-read)) + (export "future.cancel-write" (func $future.cancel-write)) + (export "future.drop-writable" (func $future.drop-writable)) + (export "future.drop-readable" (func $future.drop-readable)) + )))) + + (func (export "test-cancel-read") async (param "x" u32) (canon lift (core func $i "test-cancel-read"))) + (func (export "test-cancel-write") async (param "x" u32) (canon lift (core func $i "test-cancel-write"))) + (func (export "test-cancel-future-write") async (param "x" u32) (canon lift (core func $i "test-cancel-future-write"))) +) + +(component instance $A $A) +(assert_trap (invoke "test-cancel-read" (u32.const 0)) "cannot read from stream after being notified that the writable end dropped") +(component instance $A $A) +(assert_trap (invoke "test-cancel-read" (u32.const 2)) "cannot read from stream after being notified that the writable end dropped") +(component instance $A $A) +(assert_trap (invoke "test-cancel-read" (u32.const 3)) "cannot read from stream after being notified that the writable end dropped") + +(component instance $A $A) +(assert_trap (invoke "test-cancel-write" (u32.const 1)) "cannot write to stream after being notified that the readable end dropped") +(component instance $A $A) +(assert_trap (invoke "test-cancel-write" (u32.const 2)) "cannot write to stream after being notified that the readable end dropped") +(component instance $A $A) +(assert_trap (invoke "test-cancel-write" (u32.const 3)) "cannot write to stream after being notified that the readable end dropped") + + +(component instance $A $A) +(assert_trap (invoke "test-cancel-future-write" (u32.const 2)) "cannot write to stream after being notified that the readable end dropped") +(component instance $A $A) +(assert_trap (invoke "test-cancel-future-write" (u32.const 3)) "cannot write to stream after being notified that the readable end dropped") +(component instance $A $A) +(assert_trap (invoke "test-cancel-future-write" (u32.const 4)) "cannot write to stream after being notified that the readable end dropped") diff --git a/tests/misc_testsuite/component-model/async/streams-massive-send.wast b/tests/misc_testsuite/component-model/async/streams-massive-send.wast new file mode 100644 index 000000000000..cc44606b909b --- /dev/null +++ b/tests/misc_testsuite/component-model/async/streams-massive-send.wast @@ -0,0 +1,239 @@ +;;! component_model_async = true +;;! reference_types = true + +;; This test exercises corner cases where extremely large values are sent +;; between guests and currently require copying out to the host in Wasmtime +;; which should result in a trap of some form rather than the host spending all +;; its time allocating and copying memory. + +(component definition $A + (type $t (list (list (list (list u8))))) + (type $s (stream $t)) + (type $f (future $t)) + (type $functy (func async (result $s))) + + (component $A + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + + (core module $m + (import "libc" "memory" (memory 1)) + (import "" "stream.new" (func $stream.new (result i64))) + (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32))) + (import "" "future.new" (func $future.new (result i64))) + (import "" "future.write" (func $future.write (param i32 i32) (result i32))) + (import "" "task.return future" (func $task.return-future (param i32))) + (import "" "task.return stream" (func $task.return-stream (param i32))) + + (func (export "big-stream") (result i32) + (local $w i32) + (local $r i32) + (local $s i64) + (local.set $s (call $stream.new)) + (local.set $r (i32.wrap_i64 (local.get $s))) + (local.set $w (i32.wrap_i64 (i64.shr_u (local.get $s) (i64.const 32)))) + + (call $task.return-stream (local.get $r)) + + local.get $w + (call $prepare-list-to-write (i32.const 5) (i32.const 2)) + call $stream.write + unreachable + ) + + (func (export "big-future") (result i32) + (local $w i32) + (local $r i32) + (local $s i64) + (local $base i32) + (local $len i32) + (local.set $s (call $future.new)) + (local.set $r (i32.wrap_i64 (local.get $s))) + (local.set $w (i32.wrap_i64 (i64.shr_u (local.get $s) (i64.const 32)))) + + (call $task.return-future (local.get $r)) + + (call $prepare-list-to-write (i32.const 4) (i32.const 2)) + local.set $len + local.set $base + + (i32.store offset=0 (i32.const 100) (local.get $base)) + (i32.store offset=4 (i32.const 100) (local.get $len)) + + + local.get $w + i32.const 100 + call $future.write + unreachable + ) + + ;; Prepare $depth+1 layers of lists where the leaves point to all of + ;; memory and each layer otherwise is a list of the previous layer. + ;; + ;; Each layer-of-lists is `$pages` large. + (func $prepare-list-to-write (param $depth i32) (param $pages i32) (result i32 i32) + (local $base i32) + (local $len i32) + + (local $c_base i32) + (local $c_len i32) + (local $i i32) + + local.get $depth + if + ;; Case of $depth>0 meaning that this is a list-of-lists layer. + ;; Allocate some memory to store this list itself then generate the + ;; layer down by recursing. + (local.set $base (call $grow (local.get $pages))) + (local.set $len + (i32.div_u + (i32.mul (local.get $pages) (i32.const 65536)) + (i32.const 8) + ) + ) + + (call $prepare-list-to-write + (i32.sub (local.get $depth) (i32.const 1)) + (local.get $pages)) + local.set $c_len + local.set $c_base + + ;; Initialize this list-of-lists with all copies of the previous + ;; layer's list. + loop $l + (i32.store offset=0 + (i32.add (local.get $base) (i32.mul (local.get $i) (i32.const 8))) + (local.get $c_base)) + (i32.store offset=4 + (i32.add (local.get $base) (i32.mul (local.get $i) (i32.const 8))) + (local.get $c_len)) + + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (if (i32.lt_u (local.get $i) (local.get $len)) + (then (br $l))) + end + + else + ;; base case: the bottom list is just a byte list of all of memory. + (local.set $base (i32.const 0)) + (local.set $len (i32.mul (memory.size) (i32.const 65536))) + end + + local.get $base + local.get $len + ) + + (func $grow (param i32) (result i32) + (local $r i32) + (local.set $r (memory.grow (local.get 0))) + local.get $r + i32.const -1 + i32.eq + if unreachable end + local.get $r + i32.const 65536 + i32.mul + ) + + (func (export "cb") (param i32 i32 i32) (result i32) unreachable) + ) + (core func $future.new (canon future.new $f)) + (core func $future.write (canon future.write $f (memory $libc "memory"))) + (core func $stream.new (canon stream.new $s)) + (core func $stream.write (canon stream.write $s (memory $libc "memory"))) + (core func $task.return-future (canon task.return (result $f))) + (core func $task.return-stream (canon task.return (result $s))) + (core instance $m (instantiate $m + (with "libc" (instance $libc)) + (with "" (instance + (export "future.new" (func $future.new)) + (export "future.write" (func $future.write)) + (export "stream.new" (func $stream.new)) + (export "stream.write" (func $stream.write)) + (export "task.return future" (func $task.return-future)) + (export "task.return stream" (func $task.return-stream)) + )) + )) + + (func (export "big-stream") (result $s) + (canon lift (core func $m "big-stream") async + (callback (func $m "cb")))) + (func (export "big-future") (result $f) + (canon lift (core func $m "big-future") async + (callback (func $m "cb")))) + ) + + (component $B + (import "a" (instance $a + (export "big-future" (func (result $f))) + (export "big-stream" (func (result $s))) + )) + + (core module $libc + (memory (export "memory") 1) + (func (export "realloc") (param i32 i32 i32 i32) (result i32) unreachable) + ) + (core instance $libc (instantiate $libc)) + + (core module $m + (import "libc" "memory" (memory 1)) + (import "" "big-stream" (func $big-stream (result i32))) + (import "" "big-future" (func $big-future (result i32))) + (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32))) + (import "" "future.read" (func $future.read (param i32 i32) (result i32))) + + (func (export "stream") + (call $stream.read + (call $big-stream) + i32.const 0 + i32.const 100 + ) + unreachable + ) + (func (export "future") + (call $future.read + (call $big-future) + i32.const 0 + ) + unreachable + ) + ) + (core func $big-stream (canon lower (func $a "big-stream"))) + (core func $big-future (canon lower (func $a "big-future"))) + (core func $stream.read + (canon stream.read $s + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + (core func $future.read + (canon future.read $f + (memory $libc "memory") + (realloc (func $libc "realloc")) + ) + ) + (core instance $m (instantiate $m + (with "libc" (instance $libc)) + (with "" (instance + (export "big-stream" (func $big-stream)) + (export "big-future" (func $big-future)) + (export "future.read" (func $future.read)) + (export "stream.read" (func $stream.read)) + )) + )) + + (func (export "stream") async (canon lift (core func $m "stream"))) + (func (export "future") async (canon lift (core func $m "future"))) + + ) + + (instance $a (instantiate $A)) + (instance $b (instantiate $B (with "a" (instance $a)))) + (export "stream" (func $b "stream")) + (export "future" (func $b "future")) +) + +(component instance $A $A) +(assert_trap (invoke "stream") "fuel allocated for hostcalls has been exhausted") +(component instance $A $A) +(assert_trap (invoke "future") "fuel allocated for hostcalls has been exhausted") diff --git a/tests/misc_testsuite/component-model/async/streams.wast b/tests/misc_testsuite/component-model/async/streams.wast index 35725c92d10e..9cff682c8872 100644 --- a/tests/misc_testsuite/component-model/async/streams.wast +++ b/tests/misc_testsuite/component-model/async/streams.wast @@ -108,3 +108,81 @@ (core func $stream-drop-writable (canon stream.drop-writable $stream-type)) (core instance $i (instantiate $m (with "" (instance (export "stream.drop-writable" (func $stream-drop-writable)))))) ) + +;; Test which exercises an intra-component stream read/write to ensure that +;; there's no Miri violations while doing this. +(component definition $A + (core module $libc (memory (export "mem") 1)) + (core instance $libc (instantiate $libc)) + (core module $ics + (import "" "stream.new" (func $stream.new (result i64))) + (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32))) + (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32))) + (import "" "mem" (memory 1)) + + (global $r (mut i32) (i32.const 0)) + (global $w (mut i32) (i32.const 0)) + + (func (export "read-twice") + call $init + + (call $stream.read + (global.get $r) + (i32.const 100) + (i32.const 100)) + i32.const -1 ;; BLOCKED + i32.ne + if unreachable end + + (call $stream.read + (global.get $r) + (i32.const 0) + (i32.const 0)) + unreachable + ) + + (func (export "write-twice") + call $init + + (call $stream.write + (global.get $w) + (i32.const 100) + (i32.const 100)) + i32.const -1 ;; BLOCKED + i32.ne + if unreachable end + + (call $stream.write + (global.get $w) + (i32.const 0) + (i32.const 0)) + unreachable + ) + + (func $init + (local $t64 i64) + + (local.set $t64 (call $stream.new)) + (global.set $r (i32.wrap_i64 (local.get $t64))) + (global.set $w (i32.wrap_i64 (i64.shr_u (local.get $t64) (i64.const 32)))) + ) + ) + (type $s (stream u8)) + (core func $stream.new (canon stream.new $s)) + (core func $stream.read (canon stream.read $s async (memory $libc "mem"))) + (core func $stream.write (canon stream.write $s async (memory $libc "mem"))) + (core instance $ics (instantiate $ics + (with "" (instance + (export "stream.new" (func $stream.new)) + (export "stream.read" (func $stream.read)) + (export "stream.write" (func $stream.write)) + (export "mem" (memory $libc "mem")) + )) + )) + (func (export "read-twice") async (canon lift (core func $ics "read-twice"))) + (func (export "write-twice") async (canon lift (core func $ics "write-twice"))) +) +(component instance $A $A) +(assert_trap (invoke "read-twice") "cannot have concurrent operations active on a future/stream") +(component instance $A $A) +(assert_trap (invoke "write-twice") "cannot have concurrent operations active on a future/stream") diff --git a/tests/misc_testsuite/component-model/async/task-builtins.wast b/tests/misc_testsuite/component-model/async/task-builtins.wast index 9fec3e1bf76a..86155a312e01 100644 --- a/tests/misc_testsuite/component-model/async/task-builtins.wast +++ b/tests/misc_testsuite/component-model/async/task-builtins.wast @@ -381,9 +381,15 @@ (instance $a (instantiate $A)) (instance $b (instantiate $B (with "a" (instance $a)))) (export "sync-to-sync" (func $b "sync-to-sync")) + (export "sync-to-async" (func $b "sync-to-async")) + (export "async-to-sync" (func $b "async-to-sync")) + (export "async-to-async" (func $b "async-to-async")) ) (assert_return (invoke "sync-to-sync")) +(assert_return (invoke "sync-to-async")) +(assert_return (invoke "async-to-sync")) +(assert_return (invoke "async-to-async")) ;; Same as above, but when calling the host. (component @@ -457,3 +463,258 @@ ) (assert_return (invoke "run")) + +;; Test when realloc is called to communicate stream/future values that various +;; intrinsics work and have their expected values. +(component + ;; Component that will drive the reader forward and write to the future/stream + (component $writer + (type $FT (future string)) + (type $ST (stream string)) + ;; The reader will provide runner functions for futures and streams separately + (import "reader" (instance $reader + (export "run-future" (func async (param "future" $FT) (result u32))) + (export "run-stream" (func async (param "stream" $ST) (result u32))) + )) + (core module $libc + (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core func $run-reader-future (canon lower (func $reader "run-future") (memory $libc "memory") async)) + (core func $run-reader-stream (canon lower (func $reader "run-stream") (memory $libc "memory") async)) + (core module $m + (import "" "future.write" (func $future.write (param i32 i32) (result i32))) + (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32))) + (import "" "waitable.join" (func $waitable.join (param i32 i32))) + (import "" "waitable-set.new" (func $waitable-set.new (result i32))) + (import "" "stream.new" (func $stream.new (result i64))) + (import "" "future.new" (func $future.new (result i64))) + (import "" "task.return" (func $task.return (param i32))) + (import "" "run-reader-future" (func $run-reader-future (param i32 i32) (result i32))) + (import "" "run-reader-stream" (func $run-reader-stream (param i32 i32) (result i32))) + (import "" "memory" (memory 1)) + + (global $ws (mut i32) (i32.const 0)) + (global $fw (mut i32) (i32.const 0)) + (global $sw (mut i32) (i32.const 0)) + (global $state (mut i32) (i32.const 0)) + (global $future-subtask (mut i32) (i32.const 0)) + (global $stream-subtask (mut i32) (i32.const 0)) + (global $future-retp i32 (i32.const 0x60)) + (global $stream-retp i32 (i32.const 0x70)) + + (func (export "run") (result i32) + (local $ret i32) (local $ret64 i64) + (local $fr i32) (local $sr i32) + + ;; store address and length of string + (i32.store offset=0 (i32.const 40) (i32.const 0x100)) + (i32.store offset=4 (i32.const 40) (i32.const 2)) + + (local.set $ret64 (call $future.new)) + (local.set $fr (i32.wrap_i64 (local.get $ret64))) + (global.set $fw (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32)))) + (local.set $ret64 (call $stream.new)) + (local.set $sr (i32.wrap_i64 (local.get $ret64))) + (global.set $sw (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32)))) + + (local.set $ret (call $run-reader-future (local.get $fr) (global.get $future-retp))) + (global.set $future-subtask (i32.shr_u (local.get $ret) (i32.const 4))) + (local.set $ret (call $future.write (global.get $fw) (i32.const 40))) + (if (i32.ne (i32.const 0 (; COMPLETED ;)) (local.get $ret)) + (then unreachable)) + + (local.set $ret (call $run-reader-stream (local.get $sr) (global.get $stream-retp))) + (global.set $stream-subtask (i32.shr_u (local.get $ret) (i32.const 4))) + (local.set $ret (call $stream.write (global.get $sw) (i32.const 40) (i32.const 1))) + (if (i32.ne (i32.const 0x10 (; COMPLETED | 1<<4 ;)) (local.get $ret)) (then (unreachable))) + + ;; Create a waitable set and join both subtasks to wait for both to complete + (global.set $ws (call $waitable-set.new)) + (call $waitable.join (global.get $stream-subtask) (global.get $ws)) + (call $waitable.join (global.get $future-subtask) (global.get $ws)) + (i32.or (i32.const 2 (; WAIT ;)) (i32.shl (global.get $ws) (i32.const 4))) + ) + + (global $future-completed (mut i32) (i32.const 0)) + (global $stream-completed (mut i32) (i32.const 0)) + + ;; Callback invoked when a subtask completes. Since we joined both subtasks to the + ;; same waitable set, this will be called once for each completion. We track which + ;; subtasks have completed and only return when both are done. + (func (export "run-cb") (param $event_code i32) (param $index i32) (param $payload i32) (result i32) + (if (i32.ne (local.get $event_code) (i32.const 1 (; SUBTASK ;))) (then (unreachable))) + (if (i32.ne (local.get $payload) (i32.const 2 (; RETURNED ;))) (then (unreachable))) + + ;; Track which subtask completed + (if (i32.eq (local.get $index) (global.get $future-subtask)) + (then + (if (i32.ne (i32.load (global.get $future-retp)) (i32.const 42)) (then (unreachable))) + (global.set $future-completed (i32.const 1))) + (else + (if (i32.eq (local.get $index) (global.get $stream-subtask)) + (then + (if (i32.ne (i32.load (global.get $stream-retp)) (i32.const 42)) (then (unreachable))) + (global.set $stream-completed (i32.const 1))) + (else unreachable)))) + + ;; If both completed, exit; otherwise keep waiting + (if (result i32) + (i32.and (i32.eq (global.get $future-completed) (i32.const 1)) + (i32.eq (global.get $stream-completed) (i32.const 1))) + (then + (call $task.return (i32.const 42)) + (i32.const 0 (; EXIT ;))) + (else + (i32.or (i32.const 2 (; WAIT ;)) (i32.shl (global.get $ws) (i32.const 4))))) + ) + + (data (i32.const 0x100) "hi") + ) + (canon future.new $FT (core func $future.new)) + (canon future.write $FT async + (memory $libc "memory") (core func $future.write)) + (canon stream.new $ST (core func $stream.new)) + (canon stream.write $ST async + (memory $libc "memory") (core func $stream.write)) + (canon waitable.join (core func $waitable.join)) + (canon waitable-set.new (core func $waitable-set.new)) + (canon task.return (result u32) (memory $libc "memory") (core func $task.return)) + (canon context.set i32 0 (core func $context.set)) + + (core instance $M (instantiate $m (with "" (instance + (export "memory" (memory $libc "memory")) + (export "future.new" (func $future.new)) + (export "future.write" (func $future.write)) + (export "stream.new" (func $stream.new)) + (export "stream.write" (func $stream.write)) + (export "waitable.join" (func $waitable.join)) + (export "waitable-set.new" (func $waitable-set.new)) + (export "task.return" (func $task.return)) + (export "run-reader-future" (func $run-reader-future)) + (export "run-reader-stream" (func $run-reader-stream)) + )))) + + (func (export "run") async (result u32) + (canon lift (core func $M "run") (memory $libc "memory") + async (callback (func $M "run-cb"))) + ) + ) + + (component $reader + (core module $libc + (import "" "backpressure.inc" (func $backpressure.inc)) + (import "" "backpressure.dec" (func $backpressure.dec)) + (import "" "context.get" (func $context.get (result i32))) + (import "" "context.set" (func $context.set (param i32))) + + (memory (export "memory") 1) + (func (export "realloc") (param i32 i32 i32 i32) (result i32) + (if (i32.ne (local.get 0) (i32.const 0)) (then (unreachable))) + (if (i32.ne (local.get 1) (i32.const 0)) (then (unreachable))) + (if (i32.ne (local.get 2) (i32.const 1)) (then (unreachable))) + (if (i32.ne (local.get 3) (i32.const 2)) (then (unreachable))) + + call $context.get + i32.const 400 + i32.ne + if unreachable end + + i32.const 500 + call $context.set + + call $backpressure.inc + call $backpressure.dec + + i32.const 200 + ) + ) + + (core func $backpressure.inc (canon backpressure.inc)) + (core func $backpressure.dec (canon backpressure.dec)) + (core func $context.get (canon context.get i32 0)) + (core func $context.set (canon context.set i32 0)) + + (core instance $libc (instantiate $libc (with "" (instance + (export "backpressure.inc" (func $backpressure.inc)) + (export "backpressure.dec" (func $backpressure.dec)) + (export "context.get" (func $context.get)) + (export "context.set" (func $context.set)) + )))) + + (type $FT (future string)) + (type $ST (stream string)) + (canon future.new $FT (core func $future.new)) + (canon future.read $FT + (memory $libc "memory") (realloc (func $libc "realloc")) (core func $future.read)) + (canon stream.new $ST (core func $stream.new)) + (canon stream.read $ST + (memory $libc "memory") (realloc (func $libc "realloc")) (core func $stream.read)) + (canon waitable.join (core func $waitable.join)) + (canon waitable-set.new (core func $waitable-set.new)) + (canon waitable-set.wait (memory $libc "memory") (core func $waitable-set.wait)) + (canon task.return (result u32) (memory $libc "memory") (core func $task.return)) + + (core module $m + (import "" "future.read" (func $future.read (param i32 i32) (result i32))) + (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32))) + (import "" "context.set" (func $context.set (param i32))) + (import "" "context.get" (func $context.get (result i32))) + (import "" "task.return" (func $task.return (param i32))) + (import "" "memory" (memory 1)) + + ;; Set context[0] to 400, then read the future, which should call realloc and set + ;; context[0] to 500, then check that we see that value. + (func (export "run-future") (param $fr i32) (result i32) + (local $ret i32) + + (call $context.set (i32.const 400)) + (local.set $ret (call $future.read (local.get $fr) (i32.const 40))) + (if (i32.ne (i32.const 0 (; COMPLETED ;)) (local.get $ret)) (then (unreachable))) + (if (i32.ne (call $context.get) (i32.const 500)) (then (unreachable))) + + (call $task.return (i32.const 42)) + (i32.const 0 (; EXIT ;)) + ) + + ;; Same as above, but for streams. + (func (export "run-stream") (param $sr i32) (result i32) + (local $ret i32) + + (call $context.set (i32.const 400)) + (local.set $ret (call $stream.read (local.get $sr) (i32.const 40) (i32.const 1))) + (if (i32.ne (i32.const 0x10 (; COMPLETED | 1<<4 ;)) (local.get $ret)) (then (unreachable))) + (if (i32.ne (call $context.get) (i32.const 500)) (then (unreachable))) + + (call $task.return (i32.const 42)) + (i32.const 0 (; EXIT ;)) + ) + + (func (export "run-cb") (param i32 i32 i32) (result i32) unreachable)) + + (core instance $M (instantiate $m (with "" (instance + (export "future.read" (func $future.read)) + (export "stream.read" (func $stream.read)) + (export "context.set" (func $context.set)) + (export "context.get" (func $context.get)) + (export "task.return" (func $task.return)) + (export "memory" (memory 1)))))) + (func (export "run-future") async (param "future" $FT) (result u32) + (canon lift (core func $M "run-future") (memory $libc "memory") (realloc (func $libc "realloc")) + async (callback (func $M "run-cb")) + ) + ) + (func (export "run-stream") async (param "stream" $ST) (result u32) + (canon lift (core func $M "run-stream") (memory $libc "memory") (realloc (func $libc "realloc")) + async (callback (func $M "run-cb")) + ) + ) + ) + + (instance $Reader (instantiate $reader)) + (instance $Writer (instantiate $writer + (with "reader" (instance $Reader) + ))) + + (func (export "run") (alias export $Writer "run")) +) +(assert_return (invoke "run") (u32.const 42)) diff --git a/tests/misc_testsuite/component-model/big-strings.wast b/tests/misc_testsuite/component-model/big-strings.wast new file mode 100644 index 000000000000..f04151019382 --- /dev/null +++ b/tests/misc_testsuite/component-model/big-strings.wast @@ -0,0 +1,66 @@ +;;! multi_memory = true +;;! hogs_memory = true + +;; Sending a massive string +(component definition $A + (component $A + (core module $m + (memory (export "m") 1) + (func (export "f") (param i32 i32) unreachable) + (func (export "realloc") (param i32 i32 i32 i32) (result i32) unreachable) + ) + (core instance $i (instantiate $m)) + (func (export "f") (param "x" string) + (canon lift + (core func $i "f") + (memory $i "m") + (realloc (func $i "realloc")) + ) + ) + ) + (instance $a (instantiate $A)) + + (component $B + (import "f" (func $f (param "x" string))) + (core module $libc (memory (export "mem") 1)) + (core instance $libc (instantiate $libc)) + (core func $f (canon lower (func $f) (memory $libc "mem"))) + (core module $m + (import "" "f" (func $f (param i32 i32))) + (import "" "mem" (memory 1)) + + (func (export "run") (param i32) + (call $f (i32.const 0) (local.get 0))) + + (func (export "grow") (param i32) (result i32) + (memory.grow (local.get 0))) + ) + (core instance $i (instantiate $m + (with "" (instance + (export "f" (func $f)) + (export "mem" (memory $libc "mem")) + )) + )) + (func (export "run") (param "x" u32) (canon lift (core func $i "run"))) + (func (export "grow") (param "x" u32) (result s32) + (canon lift (core func $i "grow"))) + ) + (instance $b (instantiate $B (with "f" (func $a "f")))) + (export "run" (func $b "run")) + (export "grow" (func $b "grow")) +) + +;; Wildly out of bounds is just rejected +(component instance $A $A) +(assert_trap (invoke "run" (u32.const 0x8000_0000)) "string content out-of-bounds") + +;; In-bounds, and just under the limit. Should hit the `unreachable` in the +;; `realloc`. +(component instance $A $A) +(assert_return (invoke "grow" (u32.const 65530)) (s32.const 1)) +(assert_trap (invoke "run" (u32.const 0x7fff_ffff)) "unreachable") + +;; Size exceeds `(1<<31)-1` +(component instance $A $A) +(assert_return (invoke "grow" (u32.const 65530)) (s32.const 1)) +(assert_trap (invoke "run" (u32.const 0x8000_0000)) "string content out-of-bounds") diff --git a/tests/misc_testsuite/component-model/map-types.wast b/tests/misc_testsuite/component-model/map-types.wast new file mode 100644 index 000000000000..74f4f34ac164 --- /dev/null +++ b/tests/misc_testsuite/component-model/map-types.wast @@ -0,0 +1,8 @@ +;;! component_model_map = true + +(component + (type (map u32 string)) + (type (map string u32)) + (type (map u32 u32)) + (type (map string (map u32 string))) +) diff --git a/tests/misc_testsuite/component-model/string-transcode-invalid.wast b/tests/misc_testsuite/component-model/string-transcode-invalid.wast new file mode 100644 index 000000000000..5e5a87cb15f5 --- /dev/null +++ b/tests/misc_testsuite/component-model/string-transcode-invalid.wast @@ -0,0 +1,62 @@ +;;! multi_memory = true + +;; Transcode a utf16 string to latin1+utf16, but the original string is +;; out-of-bounds. Should report a first-class error. +(component + (component $dst + (core module $m + (func (export "recv") (param i32 i32)) + (func (export "realloc") (param i32 i32 i32 i32) (result i32) i32.const 0) + (memory (export "memory") 1) + ) + (core instance $i (instantiate $m)) + (func (export "recv") (param "a" string) + (canon lift (core func $i "recv") (realloc (func $i "realloc")) (memory $i "memory") + string-encoding=latin1+utf16) + ) + ) + + ;; Source component: uses utf16 encoding. + ;; Passes a string placed near the END of linear memory to $dst. + (component $src + (import "recv" (func $recv (param "a" string))) + (core module $libc + (memory (export "memory") 1) ;; 1 page = 65536 bytes + (func (export "realloc") (param i32 i32 i32 i32) (result i32) i32.const 0) + ) + (core instance $libc (instantiate $libc)) + ;; canon lower with utf16 — the source side of the mismatch. + (core func $recv_lowered (canon lower (func $recv) string-encoding=utf16 (memory $libc "memory"))) + (core module $m + (import "" "" (func $recv (param i32 i32))) + (import "libc" "memory" (memory 0)) + (func (export "run") + ;; Write 8 UTF-16 code units (16 bytes) of 'A' (U+0041) at the end of memory. + ;; Offsets 65520–65535: exactly fills to the last byte of the page. + (i32.store (i32.const 65520) (i32.const 0x00410041)) + (i32.store (i32.const 65524) (i32.const 0x00410041)) + (i32.store (i32.const 65528) (i32.const 0x00410041)) + (i32.store (i32.const 65532) (i32.const 0x00410041)) + + ;; Pass ptr=65520, len=10 CODE UNITS (not bytes). + ;; We wrote 8 code units but claim 10 — the extra 2 are past end of memory. + (call $recv (i32.const 65520) (i32.const 10)) + ) + ) + (core instance $i (instantiate $m + (with "" (instance (export "" (func $recv_lowered)))) + (with "libc" (instance $libc)) + )) + (func (export "run") + (canon lift (core func $i "run")) + ) + ) + + ;; Wire the components together and run. + (instance $dst_inst (instantiate $dst)) + (instance $i (instantiate $src (with "recv" (func $dst_inst "recv")))) + + (export "run" (func $i "run")) +) + +(assert_trap (invoke "run") "string content out-of-bounds") diff --git a/tests/misc_testsuite/component-model/strings.wast b/tests/misc_testsuite/component-model/strings.wast new file mode 100644 index 000000000000..ed24163b9662 --- /dev/null +++ b/tests/misc_testsuite/component-model/strings.wast @@ -0,0 +1,379 @@ +;;! multi_memory = true + +;; Returning an unaligned utf16 string is invalid +(component definition $A + (core module $m + (memory (export "m") 1) + (func (export "f") (result i32) + (i32.store (i32.const 4) (i32.const 1)) + (i32.store (i32.const 8) (i32.const 0)) + i32.const 4 + ) + ) + (core instance $m (instantiate $m)) + (func (export "f1") (result string) + (canon lift (core func $m "f") (memory $m "m") string-encoding=utf16)) + (func (export "f2") (result string) + (canon lift (core func $m "f") (memory $m "m") string-encoding=latin1+utf16)) + +) +(component instance $A $A) +(assert_trap (invoke "f1") "string pointer not aligned to 2") +(component instance $A $A) +(assert_trap (invoke "f2") "string pointer not aligned to 2") + +;; utf8 -> utf16 -- when shrinking memory it must be aligned +(component + (component $c + (core module $m + (func (export "") (param i32 i32) unreachable) + (func (export "realloc") (param $old_ptr i32) (param $old_size i32) + (param $align i32) (param $new_size i32) (result i32) + (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable)) + (if (result i32) (i32.eqz (local.get $old_ptr)) + (then (i32.const 2)) ;; first allocation aligned + (else (i32.const 3)) ;; second allocation unaligned + ) + ) + (memory (export "memory") 1) + ) + (core instance $m (instantiate $m)) + (func (export "a") (param "a" string) + (canon lift + (core func $m "") + (realloc (func $m "realloc")) + (memory $m "memory") + string-encoding=utf16) + ) + ) + + (component $c2 + (import "a" (func $f (param "a" string))) + (core module $libc + (memory (export "memory") 1) + ;; "àà" is 2 UTF-16 code units (4 bytes), and 4 bytes in UTF-8 + ;; Pessimistic alloc = 4 * 2 = 8 bytes, shrinks to 4 bytes after. + (data (memory 0) (i32.const 0) "àà") + ) + (core instance $libc (instantiate $libc)) + (core func $f (canon lower (func $f) string-encoding=utf8 (memory $libc "memory"))) + (core module $m + (import "" "" (func $f (param i32 i32))) + (func (export "f") (call $f (i32.const 0) (i32.const 4))) + ) + (core instance $m (instantiate $m (with "" (instance (export "" (func $f)))))) + (func (export "f") (canon lift (core func $m "f"))) + ) + + (instance $c (instantiate $c)) + (instance $c2 (instantiate $c2 (with "a" (func $c "a")))) + (export "f" (func $c2 "f")) +) + +(assert_trap (invoke "f") "unaligned pointer") + +;; utf16 -> latin1+utf16 -- when shrinking memory it must be aligned +(component + (component $c + (core module $m + (func (export "") (param i32 i32)) + (func (export "realloc") (param $old_ptr i32) (param $old_size i32) + (param $align i32) (param $new_size i32) (result i32) + (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable)) + (if (result i32) (i32.eqz (local.get $old_ptr)) + (then (i32.const 2)) ;; first allocation aligned + (else (i32.const 3)) ;; second allocation unaligned + ) + ) + (memory (export "memory") 1) + ) + (core instance $m (instantiate $m)) + (func (export "a") (param "a" string) + (canon lift + (core func $m "") + (realloc (func $m "realloc")) + (memory $m "memory") + string-encoding=latin1+utf16) + ) + ) + + (component $c2 + (import "a" (func $f (param "a" string))) + (core module $libc + (memory (export "memory") 1) + ;; "AΣ" in UTF-16: 0x41 0x00 0xA3 0x03 (Σ = U+03A3, not Latin-1) + ;; Forces transcoding to take the UTF-16 grow path. + (data (memory 0) (i32.const 0) "\41\00\a3\03") + ) + (core instance $libc (instantiate $libc)) + (core func $f (canon lower (func $f) string-encoding=utf16 (memory $libc "memory"))) + (core module $m + (import "" "" (func $f (param i32 i32))) + (func (export "f") (call $f (i32.const 0) (i32.const 2))) + ) + (core instance $m (instantiate $m (with "" (instance (export "" (func $f)))))) + (func (export "f") (canon lift (core func $m "f"))) + ) + + (instance $c (instantiate $c)) + (instance $c2 (instantiate $c2 (with "a" (func $c "a")))) + (export "f" (func $c2 "f")) +) +(assert_trap (invoke "f") "unaligned pointer") + +;; latin1+utf16 -> latin1+utf16 -- auto-downsize +(component + (component $c + (core module $m + (func (export "") (param i32 i32) unreachable) + (func (export "realloc") (param $old_ptr i32) (param $old_size i32) + (param $align i32) (param $new_size i32) (result i32) + (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable)) + (if (result i32) (i32.eqz (local.get $old_ptr)) + (then (i32.const 2)) ;; first allocation aligned + (else (i32.const 3)) ;; second allocation unaligned + ) + ) + (memory (export "memory") 1) + ) + (core instance $m (instantiate $m)) + (func (export "a") (param "a" string) + (canon lift + (core func $m "") + (realloc (func $m "realloc")) + (memory $m "memory") + string-encoding=latin1+utf16) + ) + ) + + (component $c2 + (import "a" (func $f (param "a" string))) + (core module $libc + (memory (export "memory") 1) + ;; "AA" in UTF-16: 0x41 0x00 0x41 0x00 + (data (memory 0) (i32.const 0) "\41\00\41\00") + ) + (core instance $libc (instantiate $libc)) + (core func $f (canon lower (func $f) string-encoding=latin1+utf16 (memory $libc "memory"))) + (core module $m + (import "" "" (func $f (param i32 i32))) + ;; the length here contains `UTF16_TAG` and it's additionally 1 code + ;; unit. This is a utf-16 encoded string but during transcoding it'll + ;; get shrunk to latin 1 + (func (export "f") (call $f (i32.const 0) (i32.const 0x8000_0002))) + ) + (core instance $m (instantiate $m (with "" (instance (export "" (func $f)))))) + (func (export "f") (canon lift (core func $m "f"))) + ) + + (instance $c (instantiate $c)) + (instance $c2 (instantiate $c2 (with "a" (func $c "a")))) + (export "f" (func $c2 "f")) +) +(assert_trap (invoke "f") "unaligned pointer") + +;; utf8 -> latin1+utf16 -- initial encode finishes but needs downsizing +(component + (component $c + (core module $m + (func (export "") (param i32 i32) unreachable) + (func (export "realloc") (param $old_ptr i32) (param $old_size i32) + (param $align i32) (param $new_size i32) (result i32) + (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable)) + (if (result i32) (i32.eqz (local.get $old_ptr)) + (then (i32.const 2)) ;; first allocation aligned + (else (i32.const 3)) ;; second allocation unaligned + ) + ) + (memory (export "memory") 1) + ) + (core instance $m (instantiate $m)) + (func (export "a") (param "a" string) + (canon lift + (core func $m "") + (realloc (func $m "realloc")) + (memory $m "memory") + string-encoding=latin1+utf16) + ) + ) + + (component $c2 + (import "a" (func $f (param "a" string))) + (core module $libc + (memory (export "memory") 1) + ;; "Ë" in UTF-8 is "\xc3\xab", which is 2 bytes, but in latin1+utf16 it's + ;; 1 byte (0xCB). The initial allocation of 2 bytes completes the entire + ;; transcode but the final allocation needs to be shrunk to 1 byte. + (data (memory 0) (i32.const 0) "Ë") + ) + (core instance $libc (instantiate $libc)) + (core func $f (canon lower (func $f) (memory $libc "memory"))) + (core module $m + (import "" "" (func $f (param i32 i32))) + (func (export "f") (call $f (i32.const 0) (i32.const 2))) + ) + (core instance $m (instantiate $m (with "" (instance (export "" (func $f)))))) + (func (export "f") (canon lift (core func $m "f"))) + ) + + (instance $c (instantiate $c)) + (instance $c2 (instantiate $c2 (with "a" (func $c "a")))) + (export "f" (func $c2 "f")) +) +(assert_trap (invoke "f") "unaligned pointer") + +;; utf8 -> latin1+utf16 +;; - first realloc fails to hold latin1 +;; - second realloc is too big +;; - third realloc shrinks +(component + (component $c + (core module $m + (global $cnt (mut i32) (i32.const 0)) + (func (export "") (param i32 i32) + unreachable + ) + (func (export "realloc") (param $old_ptr i32) (param $old_size i32) + (param $align i32) (param $new_size i32) (result i32) + (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable)) + (global.set $cnt (i32.add (global.get $cnt) (i32.const 1))) + + ;; first allocation is aligned + (if (i32.eq (global.get $cnt) (i32.const 1)) + (then + (if (i32.ne (local.get $old_ptr) (i32.const 0)) (then unreachable)) + (if (i32.ne (local.get $old_size) (i32.const 0)) (then unreachable)) + (if (i32.ne (local.get $new_size) (i32.const 5)) (then unreachable)) + (return (i32.const 2))) + ) + ;; second allocation is aligned + (if (i32.eq (global.get $cnt) (i32.const 2)) + (then + (if (i32.ne (local.get $old_ptr) (i32.const 2)) (then unreachable)) + (if (i32.ne (local.get $old_size) (i32.const 5)) (then unreachable)) + (if (i32.ne (local.get $new_size) (i32.const 10)) (then unreachable)) + (return (i32.const 4))) + ) + ;; third allocation is unaligned + (if (i32.eq (global.get $cnt) (i32.const 3)) + (then + (if (i32.ne (local.get $old_ptr) (i32.const 4)) (then unreachable)) + (if (i32.ne (local.get $old_size) (i32.const 10)) (then unreachable)) + (if (i32.ne (local.get $new_size) (i32.const 4)) (then unreachable)) + (return (i32.const 3))) + ) + + unreachable + ) + (memory (export "memory") 1) + ) + (core instance $m (instantiate $m)) + (func (export "a") (param "a" string) + (canon lift + (core func $m "") + (realloc (func $m "realloc")) + (memory $m "memory") + string-encoding=latin1+utf16) + ) + ) + + (component $c2 + (import "a" (func $f (param "a" string))) + (core module $libc + (memory (export "memory") 1) + ;; "Ë┛" in UTF-8 is "\xc3\xab\xe2\x8c\x9b", 5 bytes. + ;; * First, a 5-byte allocation is made to see if it fits in latin 1. + ;; * This fails since "┛" does not fit in latin1. The second allocation + ;; is over-large at 10 bytes (twice the original length). + ;; * The string encoded in UTF-16 is "\xcb\x00\x1b%", which is 4 bytes. + ;; * The 10-byte allocation is shrunk to 4 bytes, which is what this + ;; test is looking for (proper alignment in the 3rd realloc). + (data (memory 0) (i32.const 0) "Ë┛") + ) + (core instance $libc (instantiate $libc)) + (core func $f (canon lower (func $f) (memory $libc "memory"))) + (core module $m + (import "" "" (func $f (param i32 i32))) + (func (export "f") (call $f (i32.const 0) (i32.const 5))) + ) + (core instance $m (instantiate $m (with "" (instance (export "" (func $f)))))) + (func (export "f") (canon lift (core func $m "f"))) + ) + + (instance $c (instantiate $c)) + (instance $c2 (instantiate $c2 (with "a" (func $c "a")))) + (export "f" (func $c2 "f")) +) +(assert_trap (invoke "f") "unaligned pointer") + +;; utf8 -> latin1+utf16 +;; - first realloc fails to hold latin1 +;; - second realloc is out of bounds +(component + (component $c + (core module $m + (global $cnt (mut i32) (i32.const 0)) + (func (export "") (param i32 i32) + unreachable + ) + (func (export "realloc") (param $old_ptr i32) (param $old_size i32) + (param $align i32) (param $new_size i32) (result i32) + (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable)) + (global.set $cnt (i32.add (global.get $cnt) (i32.const 1))) + + ;; first allocation is aligned + (if (i32.eq (global.get $cnt) (i32.const 1)) + (then + (if (i32.ne (local.get $old_ptr) (i32.const 0)) (then unreachable)) + (if (i32.ne (local.get $old_size) (i32.const 0)) (then unreachable)) + (if (i32.ne (local.get $new_size) (i32.const 5)) (then unreachable)) + (return (i32.const 2))) + ) + ;; second allocation is out of bounds + (if (i32.eq (global.get $cnt) (i32.const 2)) + (then + (if (i32.ne (local.get $old_ptr) (i32.const 2)) (then unreachable)) + (if (i32.ne (local.get $old_size) (i32.const 5)) (then unreachable)) + (if (i32.ne (local.get $new_size) (i32.const 10)) (then unreachable)) + (return (i32.const -2))) + ) + + unreachable + ) + (memory (export "memory") 1) + ) + (core instance $m (instantiate $m)) + (func (export "a") (param "a" string) + (canon lift + (core func $m "") + (realloc (func $m "realloc")) + (memory $m "memory") + string-encoding=latin1+utf16) + ) + ) + + (component $c2 + (import "a" (func $f (param "a" string))) + (core module $libc + (memory (export "memory") 1) + ;; "Ë┛" in UTF-8 is "\xc3\xab\xe2\x8c\x9b", 5 bytes. + ;; * First, a 5-byte allocation is made to see if it fits in latin 1. + ;; * This fails since "┛" does not fit in latin1. The second allocation + ;; is then out of bounds and should trap + (data (memory 0) (i32.const 0) "Ë┛") + ) + (core instance $libc (instantiate $libc)) + (core func $f (canon lower (func $f) (memory $libc "memory"))) + (core module $m + (import "" "" (func $f (param i32 i32))) + (func (export "f") (call $f (i32.const 0) (i32.const 5))) + ) + (core instance $m (instantiate $m (with "" (instance (export "" (func $f)))))) + (func (export "f") (canon lift (core func $m "f"))) + ) + + (instance $c (instantiate $c)) + (instance $c2 (instantiate $c2 (with "a" (func $c "a")))) + (export "f" (func $c2 "f")) +) +(assert_trap (invoke "f") "string content out-of-bounds") diff --git a/tests/misc_testsuite/component-model/types.wast b/tests/misc_testsuite/component-model/types.wast index 9596ebe6eece..2fe0fa25ffae 100644 --- a/tests/misc_testsuite/component-model/types.wast +++ b/tests/misc_testsuite/component-model/types.wast @@ -353,3 +353,26 @@ (export "t" (instance $t (type $t'))) )) ) + +(component definition $A + (type $t' (variant (case "a" u32) (case "b"))) + (export $t "t" (type $t')) + + (core module $m + (memory (export "m") 1) + (func (export "f") (param i32) (result i32) + (i32.store8 + (i32.const 0) + (local.get 0)) + i32.const 0) + ) + (core instance $m (instantiate $m)) + (func (export "f") (param "a" u32) (result $t) + (canon lift (core func $m "f") (memory $m "m")) + ) +) + +(component instance $A $A) +(assert_return (invoke "f" (u32.const 0)) (variant.const "a" (u32.const 0))) +(assert_return (invoke "f" (u32.const 1)) (variant.const "b")) +(assert_trap (invoke "f" (u32.const 2)) "discriminant 2 out of range [0..2)") diff --git a/tests/misc_testsuite/custom-page-sizes/custom-page-sizes.wast b/tests/misc_testsuite/custom-page-sizes/custom-page-sizes.wast index a675d41f1e4c..ec03bb9492fc 100644 --- a/tests/misc_testsuite/custom-page-sizes/custom-page-sizes.wast +++ b/tests/misc_testsuite/custom-page-sizes/custom-page-sizes.wast @@ -1,5 +1,6 @@ ;;! custom_page_sizes = true ;;! multi_memory = true +;;! bulk_memory = true ;; Check all the valid custom page sizes. (module (memory 1 (pagesize 1))) diff --git a/tests/misc_testsuite/custom-page-sizes/max-size-invalid.wast b/tests/misc_testsuite/custom-page-sizes/max-size-invalid.wast new file mode 100644 index 000000000000..cbc1f9d35d8d --- /dev/null +++ b/tests/misc_testsuite/custom-page-sizes/max-size-invalid.wast @@ -0,0 +1,33 @@ +;;! custom_page_sizes = true +;;! hogs_memory = true + +;; FIXME(WebAssembly/custom-page-sizes#45): unclear what the semantics of this +;; test should be. For now Wasmtime traps so this tests that traps happen, but +;; this may change in the specification itself. Either way the problem here is +;; that with 1-byte pages an allocation of 0xffff_ffff bytes is +;; indistinguishable from a growth failure which returns -1. Somehow this needs +;; reconciliation and for now it's done as a trap. + +(assert_trap + (module + (memory 0xffff_ffff (pagesize 1)) + ) + "memory minimum size of 4294967295 pages exceeds memory limits") + +(module $m + (memory (export "memory") 0xffff_fffe (pagesize 1)) +) + +(module + (import "m" "memory" (memory 0 (pagesize 1))) + + (func (export "grow") (param i32) (result i32) + local.get 0 + memory.grow) +) + +(assert_trap (invoke "grow" (i32.const 1)) "disallowing growth to 0xffffffff bytes based on page size") +(assert_trap (invoke "grow" (i32.const 2)) "disallowing growth to 0x100000000 bytes based on page size") +(assert_trap (invoke "grow" (i32.const 100)) "disallowing growth to 0x100000062 bytes based on page size") +(assert_trap (invoke "grow" (i32.const -1)) "disallowing growth to 0x1fffffffd bytes based on page size") +(assert_return (invoke "grow" (i32.const 0)) (i32.const -2)) diff --git a/tests/misc_testsuite/elem-ref-null.wast b/tests/misc_testsuite/elem-ref-null.wast index 0359911a91dc..df113f107053 100644 --- a/tests/misc_testsuite/elem-ref-null.wast +++ b/tests/misc_testsuite/elem-ref-null.wast @@ -1,4 +1,5 @@ ;;! reference_types = true +;;! bulk_memory = true (module (elem funcref (ref.null func))) diff --git a/tests/misc_testsuite/elem_drop.wast b/tests/misc_testsuite/elem_drop.wast index b2310b4e756b..c95194db4bbd 100644 --- a/tests/misc_testsuite/elem_drop.wast +++ b/tests/misc_testsuite/elem_drop.wast @@ -1,4 +1,5 @@ ;;! reference_types = true +;;! bulk_memory = true (module (table 1 1 funcref) diff --git a/tests/misc_testsuite/externref-table-dropped-segment-issue-8281.wast b/tests/misc_testsuite/externref-table-dropped-segment-issue-8281.wast index f2b44ef9b80d..7d7e4af18ded 100644 --- a/tests/misc_testsuite/externref-table-dropped-segment-issue-8281.wast +++ b/tests/misc_testsuite/externref-table-dropped-segment-issue-8281.wast @@ -1,4 +1,5 @@ ;;! reference_types = true +;;! bulk_memory = true (module (table $t 0 0 externref) diff --git a/tests/misc_testsuite/function-references/instance.wast b/tests/misc_testsuite/function-references/instance.wast index 20ab5c1534cd..e9a9db0585fa 100644 --- a/tests/misc_testsuite/function-references/instance.wast +++ b/tests/misc_testsuite/function-references/instance.wast @@ -1,5 +1,6 @@ ;;! gc = true ;;! multi_memory = true +;;! bulk_memory = true ;; Instantiation is generative diff --git a/tests/misc_testsuite/function-references/table_grow.wast b/tests/misc_testsuite/function-references/table_grow.wast index bd7031795589..46aa31159807 100644 --- a/tests/misc_testsuite/function-references/table_grow.wast +++ b/tests/misc_testsuite/function-references/table_grow.wast @@ -1,4 +1,5 @@ ;;! gc = true +;;! bulk_memory = true (module (table $t 0 externref) diff --git a/tests/misc_testsuite/gc/array-init-data.wast b/tests/misc_testsuite/gc/array-init-data.wast index c75403fc50f5..15c47b11d085 100644 --- a/tests/misc_testsuite/gc/array-init-data.wast +++ b/tests/misc_testsuite/gc/array-init-data.wast @@ -1,4 +1,5 @@ ;;! gc = true +;;! bulk_memory = true (module (type $arr (array (mut i8))) diff --git a/tests/misc_testsuite/gc/array-new-data-missing-stack-map.wast b/tests/misc_testsuite/gc/array-new-data-missing-stack-map.wast new file mode 100644 index 000000000000..d9213dc14dca --- /dev/null +++ b/tests/misc_testsuite/gc/array-new-data-missing-stack-map.wast @@ -0,0 +1,20 @@ +;;! gc = true +;;! bulk_memory = true + +(module + (type $arr (array i8)) + (data $d "hello world") + + (import "wasmtime" "gc" (func $gc)) + + (func (export "test") (result i32) + (array.new_data $arr $d (i32.const 0) (i32.const 5)) + + (call $gc) + (drop (array.new $arr (i32.const 0) (i32.const 5))) + + (array.get_u $arr (i32.const 0)) + ) +) + +(assert_return (invoke "test") (i32.const 104)) ;; 'h' diff --git a/tests/misc_testsuite/gc/array-new-data.wast b/tests/misc_testsuite/gc/array-new-data.wast index f7919665e447..97cabb50f2f2 100644 --- a/tests/misc_testsuite/gc/array-new-data.wast +++ b/tests/misc_testsuite/gc/array-new-data.wast @@ -1,4 +1,5 @@ ;;! gc = true +;;! bulk_memory = true (module (type $arr (array (mut i8))) diff --git a/tests/misc_testsuite/gc/array-new-elem-missing-stack-map.wast b/tests/misc_testsuite/gc/array-new-elem-missing-stack-map.wast new file mode 100644 index 000000000000..89468a02158e --- /dev/null +++ b/tests/misc_testsuite/gc/array-new-elem-missing-stack-map.wast @@ -0,0 +1,26 @@ +;;! gc = true +;;! bulk_memory = true + +(module + (type $s (struct (field (mut i32)))) + (type $arr (array (ref null $s))) + + (elem $e (ref null $s) (struct.new $s (i32.const 42))) + + (import "wasmtime" "gc" (func $gc)) + + (func (export "test") (result i32) + (array.new_elem $arr $e (i32.const 0) (i32.const 1)) + + (call $gc) + (drop (struct.new $s (i32.const 0))) + (drop (struct.new $s (i32.const 0))) + (drop (struct.new $s (i32.const 0))) + (drop (struct.new $s (i32.const 0))) + (drop (struct.new $s (i32.const 0))) + + (struct.get $s 0 (array.get $arr (i32.const 0))) + ) +) + +(assert_return (invoke "test") (i32.const 42)) diff --git a/tests/misc_testsuite/gc/array-new-elem.wast b/tests/misc_testsuite/gc/array-new-elem.wast index b44ae9d9a0ba..3cfe840f8fdd 100644 --- a/tests/misc_testsuite/gc/array-new-elem.wast +++ b/tests/misc_testsuite/gc/array-new-elem.wast @@ -1,4 +1,5 @@ ;;! gc = true +;;! bulk_memory = true (module (type $arr (array i31ref)) diff --git a/tests/misc_testsuite/gc/arrays-of-different-types.wast b/tests/misc_testsuite/gc/arrays-of-different-types.wast index b775dde4f1fe..b028c1a12a7b 100644 --- a/tests/misc_testsuite/gc/arrays-of-different-types.wast +++ b/tests/misc_testsuite/gc/arrays-of-different-types.wast @@ -1,5 +1,6 @@ ;;! gc = true ;;! simd = true +;;! bulk_memory = true (module (type $func_ty (func)) diff --git a/tests/misc_testsuite/gc/func-refs-in-gc-heap.wast b/tests/misc_testsuite/gc/func-refs-in-gc-heap.wast index 74bd2b8e2cbe..2bd52965632f 100644 --- a/tests/misc_testsuite/gc/func-refs-in-gc-heap.wast +++ b/tests/misc_testsuite/gc/func-refs-in-gc-heap.wast @@ -1,4 +1,5 @@ ;;! gc = true +;;! bulk_memory = true (module (type $f0 (func (result i32))) diff --git a/tests/misc_testsuite/gc/i31ref-tables.wast b/tests/misc_testsuite/gc/i31ref-tables.wast index cc6b7bf562f5..0430d7706f82 100644 --- a/tests/misc_testsuite/gc/i31ref-tables.wast +++ b/tests/misc_testsuite/gc/i31ref-tables.wast @@ -1,4 +1,5 @@ ;;! gc = true +;;! bulk_memory = true (module $tables_of_i31ref (table $table 3 10 i31ref) diff --git a/tests/misc_testsuite/gc/issue-10397.wast b/tests/misc_testsuite/gc/issue-10397.wast index 9884185e08b1..bf63d7475882 100644 --- a/tests/misc_testsuite/gc/issue-10397.wast +++ b/tests/misc_testsuite/gc/issue-10397.wast @@ -1,4 +1,5 @@ ;;! gc = true +;;! bulk_memory = true (module (type $func (func)) diff --git a/tests/misc_testsuite/gc/issue-10459.wast b/tests/misc_testsuite/gc/issue-10459.wast index 098b07622ede..03f7dcd496cc 100644 --- a/tests/misc_testsuite/gc/issue-10459.wast +++ b/tests/misc_testsuite/gc/issue-10459.wast @@ -1,4 +1,5 @@ ;;! gc = true +;;! bulk_memory = true (module (type $func (func)) diff --git a/tests/misc_testsuite/gc/issue-11753.wast b/tests/misc_testsuite/gc/issue-11753.wast new file mode 100644 index 000000000000..b62659fa33bc --- /dev/null +++ b/tests/misc_testsuite/gc/issue-11753.wast @@ -0,0 +1,28 @@ +;;! gc = true +;;! exceptions = true + +(module + (type $s (struct (field (mut i32)))) + + (import "wasmtime" "gc" (func $gc)) + + (func $observe (param (ref null $s)) (result (ref null $s)) + local.get 0 + ) + + (func (export "run") (result i32) + (struct.new $s (i32.const 42)) + call $observe + + block $b + try_table (catch_all $b) + call $gc + (drop (struct.new $s (i32.const 0))) + end + end + + struct.get $s 0 + ) +) + +(assert_return (invoke "run") (i32.const 42)) diff --git a/tests/misc_testsuite/imported-memory-copy.wast b/tests/misc_testsuite/imported-memory-copy.wast index 4787173cc787..812f900ff88b 100644 --- a/tests/misc_testsuite/imported-memory-copy.wast +++ b/tests/misc_testsuite/imported-memory-copy.wast @@ -1,3 +1,5 @@ +;;! bulk_memory = true + (module $foreign (memory (export "mem") 1 1) (data 0 (i32.const 1000) "hello") diff --git a/tests/misc_testsuite/issue1809.wast b/tests/misc_testsuite/issue1809.wast index e7508e2613e6..a47ce219190e 100644 --- a/tests/misc_testsuite/issue1809.wast +++ b/tests/misc_testsuite/issue1809.wast @@ -479,7 +479,7 @@ (i32.store (i32.const 1048576) (global.get $__data_efd))))) - (memory $memroy (export "memroy") 17) + (memory $memory (export "memory") 17) (global $g0 (mut i32) (i32.const 1048576)) (global $__data_efd (export "__data_efd") i32 (i32.const 1048580)) (global $__heap_bare (export "__heap_bare") i32 (i32.const 1048580))) diff --git a/tests/misc_testsuite/memory-combos.wast b/tests/misc_testsuite/memory-combos.wast index 0eb968fa7a02..74c209ee9452 100644 --- a/tests/misc_testsuite/memory-combos.wast +++ b/tests/misc_testsuite/memory-combos.wast @@ -127,7 +127,7 @@ (assert_trap (invoke "store_m3" (i32.const -1) (i32.const 0)) "out of bounds memory access") (assert_return (invoke "grow_m3" (i32.const 1)) (i32.const 1)) (assert_return (invoke "size_m3") (i32.const 2)) -(assert_return (invoke "grow_m3" (i32.const -1)) (i32.const -1)) +(assert_trap (invoke "grow_m3" (i32.const -1)) "disallowing growth") (assert_return (invoke "load_m3" (i32.const 1)) (i32.const 0)) (assert_return (invoke "store_m3" (i32.const 1) (i32.const 1))) (assert_return (invoke "load_m3" (i32.const 1)) (i32.const 1)) @@ -144,7 +144,7 @@ (assert_trap (invoke "store_m4" (i32.const -1) (i32.const 0)) "out of bounds memory access") (assert_return (invoke "grow_m4" (i32.const 1)) (i32.const 1)) (assert_return (invoke "size_m4") (i32.const 2)) -(assert_return (invoke "grow_m4" (i32.const -1)) (i32.const -1)) +(assert_trap (invoke "grow_m4" (i32.const -1)) "disallowing growth") (assert_return (invoke "load_m4" (i32.const 1)) (i32.const 0)) (assert_return (invoke "store_m4" (i32.const 1) (i32.const 1))) (assert_return (invoke "load_m4" (i32.const 1)) (i32.const 1)) @@ -178,7 +178,7 @@ (assert_trap (invoke "store_m8" (i32.const -1) (i32.const 0)) "out of bounds memory access") (assert_return (invoke "grow_m8" (i32.const 1)) (i32.const 1)) (assert_return (invoke "size_m8") (i32.const 2)) -(assert_return (invoke "grow_m8" (i32.const -1)) (i32.const -1)) +(assert_trap (invoke "grow_m8" (i32.const -1)) "disallowing growth") (assert_return (invoke "load_m8" (i32.const 1)) (i32.const 0)) (assert_return (invoke "store_m8" (i32.const 1) (i32.const 1))) (assert_return (invoke "load_m8" (i32.const 1)) (i32.const 1)) diff --git a/tests/misc_testsuite/memory-copy.wast b/tests/misc_testsuite/memory-copy.wast index d3392644237b..86633f6bc431 100644 --- a/tests/misc_testsuite/memory-copy.wast +++ b/tests/misc_testsuite/memory-copy.wast @@ -1,3 +1,5 @@ +;;! bulk_memory = true + (module (memory 1 1) (data 0 (i32.const 1000) "hello") diff --git a/tests/misc_testsuite/memory64/bounds.wast b/tests/misc_testsuite/memory64/bounds.wast index b11c8f5d1658..5ecafef19a1b 100644 --- a/tests/misc_testsuite/memory64/bounds.wast +++ b/tests/misc_testsuite/memory64/bounds.wast @@ -1,4 +1,5 @@ ;;! memory64 = true +;;! bulk_memory = true (assert_unlinkable (module diff --git a/tests/misc_testsuite/memory64/codegen.wast b/tests/misc_testsuite/memory64/codegen.wast index 369dc1fe02ea..bca0671891dd 100644 --- a/tests/misc_testsuite/memory64/codegen.wast +++ b/tests/misc_testsuite/memory64/codegen.wast @@ -1,4 +1,5 @@ ;;! memory64 = true +;;! bulk_memory = true ;; make sure everything codegens correctly and has no cranelift verifier errors (module diff --git a/tests/misc_testsuite/memory64/multi-memory.wast b/tests/misc_testsuite/memory64/multi-memory.wast index 52c9c52eea7f..28e75b3ef47e 100644 --- a/tests/misc_testsuite/memory64/multi-memory.wast +++ b/tests/misc_testsuite/memory64/multi-memory.wast @@ -1,5 +1,6 @@ ;;! memory64 = true ;;! multi_memory = true +;;! bulk_memory = true ;; 64 => 64 (module diff --git a/tests/misc_testsuite/memory64/table-too-big.wast b/tests/misc_testsuite/memory64/table-too-big.wast new file mode 100644 index 000000000000..ccb0a3cfb28c --- /dev/null +++ b/tests/misc_testsuite/memory64/table-too-big.wast @@ -0,0 +1,17 @@ +;;! memory64 = true +;;! hogs_memory = true +;;! reference_types = true + +(assert_trap + (module (table i64 0x2000_0000_0000_0000 funcref)) + "overflow calculating table allocation size") + +(module + (table i64 0 funcref) + (func (export "grow") (param i64) (result i64) + (table.grow 0 (ref.null func) (local.get 0)) + ) +) + +(assert_trap (invoke "grow" (i64.const 0x2000_0000_0000_0000)) + "failed to allocate") diff --git a/tests/misc_testsuite/memory64/tables-without-bulk-memory.wast b/tests/misc_testsuite/memory64/tables-without-bulk-memory.wast new file mode 100644 index 000000000000..d23f7019bf1a --- /dev/null +++ b/tests/misc_testsuite/memory64/tables-without-bulk-memory.wast @@ -0,0 +1,13 @@ +;;! memory64 = true +;;! reference_types = true +;;! bulk_memory = false + +(module $A + (table (export "table") i64 1 funcref) +) + +(module + (import "A" "table" (table $t i64 1 funcref)) + (func $f) + (elem (i64.const 0) func $f) +) diff --git a/tests/misc_testsuite/memory64/threads.wast b/tests/misc_testsuite/memory64/threads.wast index 192fcc3cd7da..c075a5ca37fc 100644 --- a/tests/misc_testsuite/memory64/threads.wast +++ b/tests/misc_testsuite/memory64/threads.wast @@ -80,3 +80,31 @@ ) (assert_return (invoke "run")) + +(module + (memory i64 1 1 shared) + + (func (export "notify_oob") (param i64) (result i32) + local.get 0 + i32.const 1 + memory.atomic.notify offset=0x200 + ) + (func (export "wait32_oob") (param i64) (result i32) + local.get 0 + i32.const 1 + i64.const -1 + memory.atomic.wait32 offset=0x200 + ) + (func (export "wait64_oob") (param i64) (result i32) + local.get 0 + i64.const 1 + i64.const -1 + memory.atomic.wait64 offset=0x200 + ) +) +(assert_trap (invoke "notify_oob" (i64.const 0xFFFF_FFFF_FFFF_FF00)) + "out of bounds memory access") +(assert_trap (invoke "wait32_oob" (i64.const 0xFFFF_FFFF_FFFF_FF00)) + "out of bounds memory access") +(assert_trap (invoke "wait64_oob" (i64.const 0xFFFF_FFFF_FFFF_FF00)) + "out of bounds memory access") diff --git a/tests/misc_testsuite/multi-memory/simple.wast b/tests/misc_testsuite/multi-memory/simple.wast index 941c5f46d5fe..e86967aef931 100644 --- a/tests/misc_testsuite/multi-memory/simple.wast +++ b/tests/misc_testsuite/multi-memory/simple.wast @@ -1,4 +1,5 @@ ;;! multi_memory = true +;;! bulk_memory = true (module (memory $m1 1) diff --git a/tests/misc_testsuite/partial-init-memory-segment.wast b/tests/misc_testsuite/partial-init-memory-segment.wast index 471e74a643d1..8c1b736b9214 100644 --- a/tests/misc_testsuite/partial-init-memory-segment.wast +++ b/tests/misc_testsuite/partial-init-memory-segment.wast @@ -1,3 +1,5 @@ +;;! bulk_memory = true + (module $m (memory (export "mem") 1) diff --git a/tests/misc_testsuite/partial-init-table-segment.wast b/tests/misc_testsuite/partial-init-table-segment.wast index 0086d4b54227..b3b73352691c 100644 --- a/tests/misc_testsuite/partial-init-table-segment.wast +++ b/tests/misc_testsuite/partial-init-table-segment.wast @@ -1,3 +1,5 @@ +;;! bulk_memory = true + (module $m (table (export "table") funcref (elem $zero $zero $zero $zero $zero $zero $zero $zero $zero $zero)) diff --git a/tests/misc_testsuite/pooling-drop-out-of-order.wast b/tests/misc_testsuite/pooling-drop-out-of-order.wast new file mode 100644 index 000000000000..68a033a02739 --- /dev/null +++ b/tests/misc_testsuite/pooling-drop-out-of-order.wast @@ -0,0 +1,16 @@ +;; Small test case to create a module in one thread, drop another module in +;; another thread, then drop the first module in the original thread. This +;; historically exposed an issue with MPK and using striped indices correctly +;; when purging modules from the pooling allocator. + +(module + (memory 1) + (data (i32.const 0) "\2a\00\00\00") + (func (export "load") (result i32) + i32.const 0 + i32.load)) + +(assert_return (invoke "load") (i32.const 42)) + +(thread $t1 (module (memory 1))) +(wait $t1) diff --git a/tests/misc_testsuite/pooling-oob-on-reuse.wast b/tests/misc_testsuite/pooling-oob-on-reuse.wast new file mode 100644 index 000000000000..7a3924174db7 --- /dev/null +++ b/tests/misc_testsuite/pooling-oob-on-reuse.wast @@ -0,0 +1,16 @@ +(module) + +(thread $t1 + (module $A + (memory 10 100) + (func (export "grow") + (drop (memory.grow (i32.const 10))))) + (invoke "grow") +) +(wait $t1) + +(module $B + (memory 5 100) + (func (export "read_oob") (result i32) + (i32.load (i32.const 983040)))) +(assert_trap (invoke "read_oob") "out of bounds memory access") diff --git a/tests/misc_testsuite/simd/edge-of-memory.wast b/tests/misc_testsuite/simd/edge-of-memory.wast index 03e122ada89f..0d4e5f623ffd 100644 --- a/tests/misc_testsuite/simd/edge-of-memory.wast +++ b/tests/misc_testsuite/simd/edge-of-memory.wast @@ -336,3 +336,10 @@ (assert_return (invoke "2-byte-from-end" (i32.const 65534))) (assert_return (invoke "4-byte-from-end" (i32.const 65532))) (assert_return (invoke "8-byte-from-end" (i32.const 65528))) + +(module + (memory 1) + (func (export "test") (param i32) (result v128) + (f64x2.splat (f64.load (local.get 0))))) + +(assert_return (invoke "test" (i32.const 65528)) (v128.const f64x2 0 0)) diff --git a/tests/misc_testsuite/simd/spillslot-size-fuzzbug.wast b/tests/misc_testsuite/simd/spillslot-size-fuzzbug.wast index c7a6a6367de0..afd41cec4ae1 100644 --- a/tests/misc_testsuite/simd/spillslot-size-fuzzbug.wast +++ b/tests/misc_testsuite/simd/spillslot-size-fuzzbug.wast @@ -1,4 +1,5 @@ ;;! simd = true +;;! bulk_memory = true (module (func (export "test") (result f32 f32) diff --git a/tests/misc_testsuite/table_copy.wast b/tests/misc_testsuite/table_copy.wast index f06258ea249d..e053ad1d1b43 100644 --- a/tests/misc_testsuite/table_copy.wast +++ b/tests/misc_testsuite/table_copy.wast @@ -1,3 +1,5 @@ +;;! bulk_memory = true + (module (func $f (param i32 i32 i32) (result i32) (local.get 0)) (func $g (param i32 i32 i32) (result i32) (local.get 1)) diff --git a/tests/misc_testsuite/table_copy_on_imported_tables.wast b/tests/misc_testsuite/table_copy_on_imported_tables.wast index 05d6fd2122a5..083381d6bf78 100644 --- a/tests/misc_testsuite/table_copy_on_imported_tables.wast +++ b/tests/misc_testsuite/table_copy_on_imported_tables.wast @@ -1,4 +1,5 @@ ;;! reference_types = true +;;! bulk_memory = true (module $m (func $f (param i32 i32 i32 i32 i32 i32) (result i32) (local.get 0)) diff --git a/tests/misc_testsuite/winch/issue-424666628.wast b/tests/misc_testsuite/winch/issue-424666628.wast index fa6444cd0934..fa962793b6cc 100644 --- a/tests/misc_testsuite/winch/issue-424666628.wast +++ b/tests/misc_testsuite/winch/issue-424666628.wast @@ -1,3 +1,5 @@ +;;! bulk_memory = true + (module (type (;0;) (func (param f32 f32) (result i32 f64 i32 f32 f32))) (type (;1;) (func (result f64 i32 f32 f32))) diff --git a/tests/misc_testsuite/winch/memory_offsets.wast b/tests/misc_testsuite/winch/memory_offsets.wast new file mode 100644 index 000000000000..b671cb923264 --- /dev/null +++ b/tests/misc_testsuite/winch/memory_offsets.wast @@ -0,0 +1,559 @@ +(module + (type (;0;) (func (result f64 f32 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 i64 f64))) + (global (;0;) (mut i32) i32.const 10) + (export "main" (func 0)) + (func (;0;) (type 0) (result f64 f32 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 i64 f64) + call 1 + call 1 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + call 2 + br 0 + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + drop + f64.const 0x1.54e5e9c49a8a3p+224 (;=35900759953881640000000000000000000000000000000000000000000000000000;) + f32.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + i64.const 0 + f64.const 0x0p+0 (;=0;) + ) + (func (;1;) (type 0) (result f64 f32 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 i64 f64) + f64.const 0x0p+0 (;=0;) + f32.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + i64.const 0 + f64.const 0x0p+0 (;=0;) + ) + (func (;2;) (type 0) (result f64 f32 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 i64 f64) + f64.const 0x0p+0 (;=0;) + f32.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + i64.const 0 + f64.const 0x0p+0 (;=0;) + ) +) + +(assert_return (invoke "main") + (f64.const 0x0p+0) + (f32.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (f64.const 0x0p+0) + (i64.const 0) + (f64.const 0x0p+0) +) diff --git a/tests/misc_testsuite/winch/oob_extend.wast b/tests/misc_testsuite/winch/oob_extend.wast new file mode 100644 index 000000000000..2044985ef2ea --- /dev/null +++ b/tests/misc_testsuite/winch/oob_extend.wast @@ -0,0 +1,11 @@ +(module + (memory 1) + (data (i32.const 0) "\ff") + (data (i32.const 99) "\de\ad\be\ef") + (func (export "exploit") (result i32) + i32.const 0 + i32.load8_s + i32.load offset=100 + ) +) +(assert_trap (invoke "exploit") "out of bounds memory access") diff --git a/tests/misc_testsuite/winch/oob_table_grow.wast b/tests/misc_testsuite/winch/oob_table_grow.wast new file mode 100644 index 000000000000..4bcdcc054074 --- /dev/null +++ b/tests/misc_testsuite/winch/oob_table_grow.wast @@ -0,0 +1,13 @@ +;;! reference_types = true + +(module + (memory 1) + (table $t 1 2 funcref) + (func $exploit (result i32) + (i32.store offset=1 + (table.grow $t (table.get $t (i32.const 0)) (i32.const 100)) + (i32.const 0xDEADBEEF)) + (i32.load (i32.const 0))) + (export "exploit" (func $exploit)) +) +(assert_trap (invoke "exploit") "out of bounds memory access") diff --git a/tests/misc_testsuite/winch/ref-types-basic.wast b/tests/misc_testsuite/winch/ref-types-basic.wast new file mode 100644 index 000000000000..4e22648e3cc6 --- /dev/null +++ b/tests/misc_testsuite/winch/ref-types-basic.wast @@ -0,0 +1,102 @@ +;;! reference_types = true + +(module + (type $i32-func (func (result i32))) + + (func $returns-42 (type $i32-func) (i32.const 42)) + (func $returns-7 (type $i32-func) (i32.const 7)) + (func $dummy) + + (table $t 10 funcref) + (elem (table $t) (i32.const 0) func $returns-42 $returns-7) + + ;; ref.null func / ref.is_null + (func (export "null-is-null") (result i32) + (ref.is_null (ref.null func)) + ) + + ;; ref.func + ref.is_null + (func (export "func-is-not-null") (result i32) + (ref.is_null (ref.func $returns-42)) + ) + + ;; ref.func + call_indirect + (func (export "call-indirect-0") (result i32) + (call_indirect $t (type $i32-func) (i32.const 0)) + ) + (func (export "call-indirect-1") (result i32) + (call_indirect $t (type $i32-func) (i32.const 1)) + ) + (func (export "call-indirect-null") (result i32) + (call_indirect $t (type $i32-func) (i32.const 9)) + ) + + ;; typed select between two funcrefs + (func (export "select-funcref") (param $c i32) (result funcref) + (select (result funcref) (ref.func $returns-42) (ref.func $returns-7) (local.get $c)) + ) + (func (export "select-null-first") (param $c i32) (result funcref) + (select (result funcref) (ref.null func) (ref.func $returns-7) (local.get $c)) + ) + + ;; table.get / table.set + (func (export "table-get") (param $i i32) (result funcref) + (table.get $t (local.get $i)) + ) + (func (export "table-set-null") (param $i i32) + (table.set $t (local.get $i) (ref.null func)) + ) + (func (export "table-set-ref") (param $i i32) + (table.set $t (local.get $i) (ref.func $returns-42)) + ) + + ;; ref.func -> table.set -> call_indirect + (func (export "set-and-call") (result i32) + (table.set $t (i32.const 5) (ref.func $returns-42)) + (call_indirect $t (type $i32-func) (i32.const 5)) + ) + + ;; table.grow + (func (export "table-grow") (param $n i32) (result i32) + (table.grow $t (ref.null func) (local.get $n)) + ) + (func (export "table-size") (result i32) + (table.size $t) + ) +) + +;; ref.null / ref.is_null +(assert_return (invoke "null-is-null") (i32.const 1)) +(assert_return (invoke "func-is-not-null") (i32.const 0)) + +;; call_indirect through elem-populated table +(assert_return (invoke "call-indirect-0") (i32.const 42)) +(assert_return (invoke "call-indirect-1") (i32.const 7)) +(assert_trap (invoke "call-indirect-null") "uninitialized element") + +;; typed select +(assert_return (invoke "select-funcref" (i32.const 1)) (ref.func 0)) +(assert_return (invoke "select-funcref" (i32.const 0)) (ref.func 1)) +(assert_return (invoke "select-null-first" (i32.const 1)) (ref.null func)) +(assert_return (invoke "select-null-first" (i32.const 0)) (ref.func 1)) + +;; ref.func -> table.set -> call_indirect +(assert_return (invoke "set-and-call") (i32.const 42)) + +;; table.get +(assert_return (invoke "table-get" (i32.const 0)) (ref.func 0)) +(assert_return (invoke "table-get" (i32.const 1)) (ref.func 1)) +(assert_return (invoke "table-get" (i32.const 2)) (ref.null func)) +(assert_trap (invoke "table-get" (i32.const 10)) "out of bounds table access") + +;; table.set +(assert_return (invoke "table-set-null" (i32.const 0))) +(assert_return (invoke "table-get" (i32.const 0)) (ref.null func)) +(assert_return (invoke "table-set-ref" (i32.const 0))) +(assert_return (invoke "table-get" (i32.const 0)) (ref.func 0)) +(assert_trap (invoke "table-set-null" (i32.const 10)) "out of bounds table access") + +;; table.grow +(assert_return (invoke "table-size") (i32.const 10)) +(assert_return (invoke "table-grow" (i32.const 5)) (i32.const 10)) +(assert_return (invoke "table-size") (i32.const 15)) diff --git a/tests/misc_testsuite/winch/table64.wast b/tests/misc_testsuite/winch/table64.wast new file mode 100644 index 000000000000..c9108c9c97a0 --- /dev/null +++ b/tests/misc_testsuite/winch/table64.wast @@ -0,0 +1,15 @@ +;;! memory64 = true +;;! reference_types = true + +(module + (table $t i64 10 funcref) + (func $leak (result i64 i64) + table.size $t + table.size $t + ) + (func (export "test") (result i64) + call $leak + drop + ) +) +(assert_return (invoke "test") (i64.const 10)) diff --git a/tests/misc_testsuite/winch/table_fill.wast b/tests/misc_testsuite/winch/table_fill.wast index 3ead4f2dbee3..0811e1b01335 100644 --- a/tests/misc_testsuite/winch/table_fill.wast +++ b/tests/misc_testsuite/winch/table_fill.wast @@ -65,3 +65,39 @@ (invoke "fill" (i32.const 8) (i32.const 0) (i32.const 3)) "out of bounds table access" ) + +(module $t + (table (export "t") 1 funcref) +) + +(module + (import "t" "t" (table $t1 1 funcref)) + (table $t2 2 funcref) + + (func (export "fill1") (param i32 funcref i32) + local.get 0 + local.get 1 + local.get 2 + table.fill $t1) + + (func (export "fill2") (param i32 funcref i32) + local.get 0 + local.get 1 + local.get 2 + table.fill $t2) +) + +(assert_return (invoke "fill1" (i32.const 0) (ref.null func) (i32.const 0))) +(assert_return (invoke "fill1" (i32.const 0) (ref.null func) (i32.const 1))) +(assert_return (invoke "fill1" (i32.const 1) (ref.null func) (i32.const 0))) +(assert_trap (invoke "fill1" (i32.const 2) (ref.null func) (i32.const 0)) + "out of bounds table access") + +(assert_return (invoke "fill2" (i32.const 0) (ref.null func) (i32.const 0))) +(assert_return (invoke "fill2" (i32.const 0) (ref.null func) (i32.const 1))) +(assert_return (invoke "fill2" (i32.const 0) (ref.null func) (i32.const 2))) +(assert_return (invoke "fill2" (i32.const 1) (ref.null func) (i32.const 0))) +(assert_return (invoke "fill2" (i32.const 1) (ref.null func) (i32.const 1))) +(assert_return (invoke "fill2" (i32.const 2) (ref.null func) (i32.const 0))) +(assert_trap (invoke "fill2" (i32.const 3) (ref.null func) (i32.const 0)) + "out of bounds table access") diff --git a/tests/pcc_memory.rs b/tests/pcc_memory.rs deleted file mode 100644 index bb2dc7f05f3c..000000000000 --- a/tests/pcc_memory.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! Tests for proof-carrying-code-based validation of memory accesses -//! in Wasmtime/Cranelift-compiled Wasm, with various combinations of -//! memory settings. - -#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] -mod pcc_memory_tests { - use wasmtime::*; - - const TESTS: &'static [&'static str] = &[ - r#" - local.get 0 - i32.load8_u - drop - "#, - r#" - local.get 0 - i32.load8_u offset=0x10000 - drop - "#, - r#" - local.get 0 - i32.load16_u - drop - "#, - r#" - local.get 0 - i32.load16_u offset=0x10000 - drop - "#, - r#" - local.get 0 - i32.load - drop - "#, - r#" - local.get 0 - i32.load offset=0x10000 - drop - "#, - r#" - local.get 0 - i64.load - drop - "#, - r#" - local.get 0 - i64.load offset=0x10000 - drop - "#, - ]; - - #[test] - #[cfg_attr(miri, ignore)] - fn test_build() { - let _ = env_logger::try_init(); - const KIB: u64 = 1024; - const MIB: u64 = 1024 * KIB; - const GIB: u64 = 1024 * MIB; - - let mut bodies = vec![]; - for (mem_min, mem_max) in [(1, 1), (10, 20)] { - for &snippet in TESTS { - bodies.push(format!( - "(module (memory {mem_min} {mem_max}) (func (param i32) {snippet}))" - )); - } - let all_snippets = TESTS - .iter() - .map(|s| s.to_owned()) - .collect::>() - .join("\n"); - bodies.push(format!( - "(module (memory {mem_min} {mem_max}) (func (param i32) {all_snippets}))" - )); - } - - for test in &bodies { - for memory_reservation in [4 * GIB] { - for guard_size in [2 * GIB] { - for enable_spectre in [true /* not yet supported by PCC: false */] { - for _memory_bits in [32 /* not yet supported by PCC: 64 */] { - log::trace!("test:\n{test}\n"); - log::trace!("static {memory_reservation:x} guard {guard_size:x}"); - let mut cfg = Config::new(); - cfg.memory_reservation(memory_reservation); - cfg.memory_guard_size(guard_size); - cfg.cranelift_pcc(true); - unsafe { - cfg.cranelift_flag_set( - "enable_heap_access_spectre_mitigation", - &enable_spectre.to_string(), - ); - } - // TODO: substitute memory32/memory64 into - // test module. - - let engine = Engine::new(&cfg).unwrap(); - - let _module = Module::new(&engine, test) - .expect("compilation with PCC should succeed"); - } - } - } - } - } - } -} diff --git a/tests/wast.rs b/tests/wast.rs index c203867ff6c9..d91a82be93c1 100644 --- a/tests/wast.rs +++ b/tests/wast.rs @@ -18,25 +18,37 @@ fn main() { let mut trials = Vec::new(); + // Check for if we are only running GC-related tests. + let gc_keywords = std::env::var("WASMTIME_TEST_GC_KEYWORDS") + .ok() + .map(|s| s.split(" ").map(|s| s.to_string()).collect::>()); + let mut add_trial = |test: &WastTest, config: WastConfig| { - let trial = Trial::test( - format!( - "{:?}/{}{}{}", - config.compiler, - if config.pooling { "pooling/" } else { "" }, - if config.collector != Collector::Auto { - format!("{:?}/", config.collector) - } else { - String::new() - }, - test.path.to_str().unwrap() - ), - { - let test = test.clone(); - move || run_wast(&test, config).map_err(|e| format!("{e:?}").into()) + let name = format!( + "{:?}/{}{}{}", + config.compiler, + if config.pooling { "pooling/" } else { "" }, + if config.collector != Collector::Auto { + format!("{:?}/", config.collector) + } else { + String::new() }, + test.path.to_str().unwrap() ); + // Don't add this trial if we are only running GC-related tests and it + // doesn't look like a GC-related test. + if let Some(ks) = &gc_keywords { + if config.collector == Collector::Auto && !ks.iter().any(|kw| name.contains(kw)) { + return; + } + } + + let trial = Trial::test(name, { + let test = test.clone(); + move || run_wast(&test, config).map_err(|e| format!("{e:?}").into()) + }); + trials.push(trial); }; @@ -255,6 +267,13 @@ fn run_wast(test: &WastTest, config: WastConfig) -> wasmtime::Result<()> { use_shared_memory: true, suppress_prints: true, })?; + if test + .path + .to_str() + .is_some_and(|s| s.contains("misc_testsuite")) + { + wast_context.register_wasmtime()?; + } wast_context .run_wast(test.path.to_str().unwrap(), test.contents.as_bytes()) .with_context(|| format!("failed to run spec test with {desc} engine")) diff --git a/winch/README.md b/winch/README.md index 6640e33c3c74..905b2f21557b 100644 --- a/winch/README.md +++ b/winch/README.md @@ -22,7 +22,7 @@ Winch is a WebAssembly "baseline" or single-pass compiler designed for Wasmtime. Winch's primary goal is compilation performance, therefore only certain, very -limited peephole optimations are applied. +limited peephole optimizations are applied. For more details on the original motivation and goals, refer to the [Bytecode Alliance RFC for Baseline Compilation in Wasmtime.][rfc]. diff --git a/winch/codegen/src/codegen/bounds.rs b/winch/codegen/src/codegen/bounds.rs index be61233cef65..00cd6281d62a 100644 --- a/winch/codegen/src/codegen/bounds.rs +++ b/winch/codegen/src/codegen/bounds.rs @@ -7,9 +7,10 @@ use crate::{ abi::vmctx, codegen::{CodeGenContext, Emission}, isa::reg::{Reg, writable}, - masm::{IntCmpKind, IntScratch, MacroAssembler, OperandSize, RegImm, TrapCode}, + masm::{Imm, IntCmpKind, IntScratch, MacroAssembler, OperandSize, RegImm, TrapCode}, stack::TypedReg, }; +use wasmtime_environ::WasmValType; /// A newtype to represent an immediate offset argument for a heap access. #[derive(Debug, Copy, Clone)] @@ -139,7 +140,7 @@ pub(crate) fn ensure_index_and_offset( masm.checked_uadd( writable!(index.as_typed_reg().into()), index.as_typed_reg().into(), - RegImm::i64(offset as i64), + Imm::i64(offset as i64), heap_ty_size, TrapCode::HEAP_OUT_OF_BOUNDS, )?; @@ -217,8 +218,23 @@ where })?; // Start by adding the index to the heap base addr. - let index_reg = index.as_typed_reg().reg; - masm.add(writable!(dst), dst, index_reg.into(), ptr_size)?; + let index_typed = index.as_typed_reg(); + let heap_size: OperandSize = heap.index_type().try_into()?; + + // Emit a zero-extend add when dealing with 32-bit heaps to ensure + // that the high bits of the 64-bit values are zeroed. + if ptr_size == OperandSize::S64 && heap_size == OperandSize::S32 { + masm.add_uextend( + writable!(dst), + dst, + index_typed.reg, + OperandSize::S32, + ptr_size, + )?; + } else { + assert!(index_typed.ty == WasmValType::I64); + masm.add(writable!(dst), dst, index_typed.reg.into(), ptr_size)?; + } if offset.as_u32() > 0 { masm.add( diff --git a/winch/codegen/src/codegen/env.rs b/winch/codegen/src/codegen/env.rs index 51b457102e18..2d88150670a3 100644 --- a/winch/codegen/src/codegen/env.rs +++ b/winch/codegen/src/codegen/env.rs @@ -41,6 +41,17 @@ pub struct TableData { pub(crate) element_size: OperandSize, /// The size of the current elements field. pub(crate) current_elements_size: OperandSize, + /// The type of this table. + pub ty: Table, +} + +impl TableData { + pub fn index_type(&self) -> WasmValType { + match self.ty.idx_type { + IndexType::I32 => WasmValType::I32, + IndexType::I64 => WasmValType::I64, + } + } } /// Heap metadata. @@ -246,6 +257,7 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> { current_elements_size: OperandSize::from_bytes( self.vmoffsets.size_of_vmtable_definition_current_elements(), ), + ty: self.translation.module.tables[index], }) } } diff --git a/winch/codegen/src/codegen/mod.rs b/winch/codegen/src/codegen/mod.rs index 748078c55493..0d08767192e2 100644 --- a/winch/codegen/src/codegen/mod.rs +++ b/winch/codegen/src/codegen/mod.rs @@ -681,6 +681,7 @@ where let memory_index = MemoryIndex::from_u32(memarg.memory); let heap = self.env.resolve_heap(memory_index); let index = Index::from_typed_reg(self.context.pop_to_reg(self.masm, None)?); + let offset = bounds::ensure_index_and_offset( self.masm, index, @@ -828,7 +829,7 @@ where self.masm.checked_uadd( writable!(index_offset_and_access_size), index_offset_and_access_size, - RegImm::i64(offset_with_access_size as i64), + Imm::i64(offset_with_access_size as i64), ptr_size, TrapCode::HEAP_OUT_OF_BOUNDS, )?; @@ -1047,7 +1048,7 @@ where if self.env.table_access_spectre_mitigation() { // Perform a bounds check and override the value of the // table element address in case the index is out of bounds. - self.masm.cmp(index, bound.into(), OperandSize::S32)?; + self.masm.cmp(index, bound.into(), bound_size)?; self.masm .cmov(writable!(base), tmp, IntCmpKind::GeU, ptr_size)?; } @@ -1073,7 +1074,8 @@ where masm.load(size_addr, writable!(size), table_data.current_elements_size) })?; - self.context.stack.push(TypedReg::i32(size).into()); + let dst = TypedReg::new(table_data.index_type(), size); + self.context.stack.push(dst.into()); Ok(()) } @@ -1100,7 +1102,7 @@ where Imm::i32(pow as i32), dst.into(), ShiftKind::ShrU, - heap_data.index_type().try_into()?, + self.env.ptr_type().try_into()?, )?; self.context.stack.push(dst.into()); Ok(()) @@ -1478,11 +1480,12 @@ where )?; if arg.offset != 0 { - self.masm.add( + self.masm.checked_uadd( writable!(addr.reg), addr.reg, - RegImm::i64(arg.offset as i64), + Imm::i64(arg.offset as i64), OperandSize::S64, + TrapCode::HEAP_OUT_OF_BOUNDS, )?; } @@ -1528,11 +1531,12 @@ where )?; if arg.offset != 0 { - self.masm.add( + self.masm.checked_uadd( writable!(addr.reg), addr.reg, - RegImm::i64(arg.offset as i64), + Imm::i64(arg.offset as i64), OperandSize::S64, + TrapCode::HEAP_OUT_OF_BOUNDS, )?; } diff --git a/winch/codegen/src/isa/aarch64/address.rs b/winch/codegen/src/isa/aarch64/address.rs index 8ea19eb6f05f..af35e46bb049 100644 --- a/winch/codegen/src/isa/aarch64/address.rs +++ b/winch/codegen/src/isa/aarch64/address.rs @@ -1,12 +1,14 @@ //! Aarch64 addressing mode. use super::regs; -use crate::reg::Reg; -use crate::{Context as _, Result, format_err}; -use cranelift_codegen::VCodeConstant; -use cranelift_codegen::{ - ir::types, - isa::aarch64::inst::{AMode, PairAMode, SImm7Scaled, SImm9}, +use crate::Result; +use crate::{ + masm::{IntScratch, MacroAssembler as Masm, OperandSize, RegImm}, + reg::Reg, +}; +use cranelift_codegen::ir::{Type, types}; +use cranelift_codegen::isa::aarch64::inst::{ + AMode, ExtendOp, PairAMode, SImm7Scaled, SImm9, UImm12Scaled, }; /// Aarch64 indexing mode. @@ -21,47 +23,63 @@ pub(crate) enum Indexing { /// Memory address representation. #[derive(Debug, Copy, Clone)] pub(crate) enum Address { - /// Base register with an arbitrary offset. Potentially gets - /// lowered into multiple instructions during code emission - /// depending on the offset. + /// Base register with an arbitrary offset. Offset { /// Base register. base: Reg, /// Offset. offset: i64, }, - /// Specialized indexed register and offset variant using - /// the stack pointer. - IndexedSPOffset { - /// Offset. - offset: i64, - /// Indexing mode. + /// SP-indexed addressing mode for single register loads/stores. + SPIndexedSingle { + /// 9-bit signed offset. + offset: SImm9, + /// Indexing mode (pre or post). + indexing: Indexing, + }, + /// SP-indexed addressing mode for register pair loads/stores. + SPIndexedPair { + /// 7-bit signed scaled offset. + offset: SImm7Scaled, + /// Indexing mode (pre or post). indexing: Indexing, }, - /// Address of a constant in the constant pool. - Const(VCodeConstant), } impl Address { - /// Create a pre-indexed addressing mode from the stack pointer. - pub fn pre_indexed_from_sp(offset: i64) -> Self { - Self::IndexedSPOffset { + /// Create a pre-indexed addressing mode from the stack pointer for single register operations. + pub fn pre_indexed_from_sp(offset: SImm9) -> Self { + Self::SPIndexedSingle { offset, indexing: Indexing::Pre, } } - /// Create a post-indexed addressing mode from the stack pointer. - pub fn post_indexed_from_sp(offset: i64) -> Self { - Self::IndexedSPOffset { + /// Create a post-indexed addressing mode from the stack pointer for single register operations. + pub fn post_indexed_from_sp(offset: SImm9) -> Self { + Self::SPIndexedSingle { offset, indexing: Indexing::Post, } } - /// Create an offset addressing mode with - /// the shadow stack pointer register - /// as a base. + /// Create a pre-indexed addressing mode from the stack pointer for register pair operations. + pub fn pre_indexed_from_sp_for_pair(offset: SImm7Scaled) -> Self { + Self::SPIndexedPair { + offset, + indexing: Indexing::Pre, + } + } + + /// Create a post-indexed addressing mode from the stack pointer for register pair operations. + pub fn post_indexed_from_sp_for_pair(offset: SImm7Scaled) -> Self { + Self::SPIndexedPair { + offset, + indexing: Indexing::Post, + } + } + + /// Create an offset addressing mode with the shadow stack pointer register as a base. pub fn from_shadow_sp(offset: i64) -> Self { Self::Offset { base: regs::shadow_sp(), @@ -75,8 +93,7 @@ impl Address { // sp generally should not be used as a base register in an // address. In the cases where its usage is required and where // we are sure that it's 16-byte aligned, the address should - // be constructed via the `Self::pre_indexed_sp` and - // Self::post_indexed_sp functions. + // be constructed via the SP-indexed constructors. // For more details around the stack pointer and shadow stack // pointer see the docs at regs::shadow_sp(). assert!( @@ -86,77 +103,104 @@ impl Address { Self::Offset { base, offset } } - /// Create an address for a constant. - pub fn constant(data: VCodeConstant) -> Self { - Self::Const(data) - } - - /// Returns the register base and immediate offset of the given [`Address`]. - /// + /// Converts self to cranelift's [`PairAMode`]. /// # Panics - /// This function panics if the [`Address`] is not [`Address::Offset`]. - pub fn unwrap_offset(&self) -> (Reg, i64) { + /// This function panics if self cannot be converted to [`PairAMode`]. + /// NB: that all uses of this function currently guarantee that + /// the offset will fit in a 7-bit signed offset. + pub fn to_pair_addressing_mode(self) -> PairAMode { match self { - Self::Offset { base, offset } => (*base, *offset), - _ => panic!("Expected register and offset addressing mode"), - } - } -} - -// Conversions between `winch-codegen`'s addressing mode representation -// and `cranelift-codegen`s addressing mode representation for aarch64. - -impl TryFrom
for PairAMode { - type Error = crate::Error; - - fn try_from(addr: Address) -> Result { - use Address::*; - use Indexing::*; - - match addr { - IndexedSPOffset { offset, indexing } => { - let simm7 = SImm7Scaled::maybe_from_i64(offset, types::I64).with_context(|| { - format!("Failed to convert {offset} to signed scaled 7 bit offset") - })?; - - if indexing == Pre { - Ok(PairAMode::SPPreIndexed { simm7 }) + Self::SPIndexedPair { offset, indexing } => { + if indexing == Indexing::Pre { + PairAMode::SPPreIndexed { simm7: offset } } else { - Ok(PairAMode::SPPostIndexed { simm7 }) + PairAMode::SPPostIndexed { simm7: offset } } } - other => Err(format_err!( - "Could not convert {other:?} to addressing mode for register pairs" - )), + _ => panic!("Could not convert addressing mode to PairAMode"), } } -} -impl TryFrom
for AMode { - type Error = crate::Error; - - fn try_from(addr: Address) -> Result { + /// Converts self to cranelift's [`AMode`]. + /// The closure parameter ensures that the caller scope is kept in + /// sync with the scratch register used for materializing the + /// general register and offset addressing mode. + /// # Panics + /// This function panics if self cannot be converted to [`AMode`]. + pub fn to_addressing_mode( + self, + masm: &mut M, + size: OperandSize, + f: impl FnOnce(&mut M, AMode) -> Result<()>, + ) -> Result<()> { use Address::*; use Indexing::*; - match addr { - IndexedSPOffset { offset, indexing } => { - let simm9 = SImm9::maybe_from_i64(offset).ok_or_else(|| { - // TODO: non-string error - format_err!("Failed to convert {offset} to signed 9-bit offset") - })?; + match self { + SPIndexedSingle { offset, indexing } => { + let amode = if indexing == Pre { + AMode::SPPreIndexed { simm9: offset } + } else { + AMode::SPPostIndexed { simm9: offset } + }; - if indexing == Pre { - Ok(AMode::SPPreIndexed { simm9 }) + f(masm, amode) + } + Offset { base, offset } => { + if let Some(simm9) = SImm9::maybe_from_i64(offset) { + f( + masm, + AMode::Unscaled { + rn: base.into(), + simm9, + }, + ) + } else if let Some(uimm12) = + UImm12Scaled::maybe_from_i64(offset, map_to_scale_type(size)) + { + f( + masm, + AMode::UnsignedOffset { + rn: base.into(), + uimm12, + }, + ) } else { - Ok(AMode::SPPostIndexed { simm9 }) + masm.with_scratch::(|masm, temp| { + masm.mov(temp.writable(), RegImm::i64(offset), OperandSize::S64)?; + f( + masm, + AMode::RegExtended { + rn: base.into(), + rm: temp.inner().into(), + extendop: ExtendOp::SXTX, + }, + ) + }) } } - Offset { base, offset } => Ok(AMode::RegOffset { - rn: base.into(), - off: offset, - }), - Const(data) => Ok(AMode::Const { addr: data }), + _ => panic!("Could not convert addressing mode to AMode"), + } + } + + /// Returns the register base and immediate offset of the given [`Address`]. + /// + /// # Panics + /// This function panics if the [`Address`] is not [`Address::Offset`]. + pub fn unwrap_offset(&self) -> (Reg, i64) { + match self { + Self::Offset { base, offset } => (*base, *offset), + _ => panic!("Expected register and offset addressing mode"), } } } + +fn map_to_scale_type(size: OperandSize) -> Type { + match size { + OperandSize::S8 => types::I8, + OperandSize::S16 => types::I16, + OperandSize::S32 => types::I32, + OperandSize::S64 => types::I64, + OperandSize::S128 => types::I8X16, + } +} diff --git a/winch/codegen/src/isa/aarch64/asm.rs b/winch/codegen/src/isa/aarch64/asm.rs index 548e0b824736..2b48bf4b68b9 100644 --- a/winch/codegen/src/isa/aarch64/asm.rs +++ b/winch/codegen/src/isa/aarch64/asm.rs @@ -1,5 +1,5 @@ //! Assembler library implementation for Aarch64. -use super::{address::Address, regs}; +use super::regs; use crate::CallingConvention; use crate::aarch64::regs::zero; use crate::masm::{ @@ -12,7 +12,7 @@ use crate::{ reg::{Reg, WritableReg, writable}, }; -use cranelift_codegen::PatchRegion; +use cranelift_codegen::isa::aarch64; use cranelift_codegen::isa::aarch64::inst::emit::{enc_arith_rrr, enc_move_wide, enc_movk}; use cranelift_codegen::isa::aarch64::inst::{ ASIMDFPModImm, FpuToIntOp, MoveWideConst, NZCV, UImm5, @@ -31,6 +31,7 @@ use cranelift_codegen::{ }, settings, }; +use cranelift_codegen::{PatchRegion, VCodeConstant}; use regalloc2::RegClass; use wasmtime_core::math::{f32_cvt_to_int_bounds, f64_cvt_to_int_bounds}; @@ -112,11 +113,11 @@ pub(crate) struct Assembler { impl Assembler { /// Create a new Aarch64 assembler. - pub fn new(shared_flags: settings::Flags) -> Self { + pub fn new(shared_flags: settings::Flags, isa_flags: aarch64::settings::Flags) -> Self { Self { buffer: MachBuffer::::new(), emit_state: Default::default(), - emit_info: EmitInfo::new(shared_flags), + emit_info: EmitInfo::new(shared_flags, isa_flags), pool: ConstantPool::new(), } } @@ -151,14 +152,13 @@ impl Assembler { } /// Adds a constant to the constant pool, returning its address. - pub fn add_constant(&mut self, constant: &[u8]) -> Address { + pub fn add_constant(&mut self, constant: &[u8]) -> VCodeConstant { let handle = self.pool.register(constant, &mut self.buffer); - Address::constant(handle) + handle } /// Store a pair of registers. - pub fn stp(&mut self, xt1: Reg, xt2: Reg, addr: Address) { - let mem: PairAMode = addr.try_into().unwrap(); + pub fn stp(&mut self, xt1: Reg, xt2: Reg, mem: PairAMode) { self.emit(Inst::StoreP64 { rt: xt1.into(), rt2: xt2.into(), @@ -168,9 +168,7 @@ impl Assembler { } /// Store a register. - pub fn str(&mut self, reg: Reg, addr: Address, size: OperandSize, flags: MemFlags) { - let mem: AMode = addr.try_into().unwrap(); - + pub fn str(&mut self, reg: Reg, mem: AMode, size: OperandSize, flags: MemFlags) { use OperandSize::*; let inst = match (reg.is_int(), size) { (_, S8) => Inst::Store8 { @@ -214,19 +212,19 @@ impl Assembler { } /// Load a signed register. - pub fn sload(&mut self, addr: Address, rd: WritableReg, size: OperandSize, flags: MemFlags) { - self.ldr(addr, rd, size, true, flags); + pub fn sload(&mut self, mem: AMode, rd: WritableReg, size: OperandSize, flags: MemFlags) { + self.ldr(mem, rd, size, true, flags); } /// Load an unsigned register. - pub fn uload(&mut self, addr: Address, rd: WritableReg, size: OperandSize, flags: MemFlags) { - self.ldr(addr, rd, size, false, flags); + pub fn uload(&mut self, mem: AMode, rd: WritableReg, size: OperandSize, flags: MemFlags) { + self.ldr(mem, rd, size, false, flags); } /// Load address into a register. fn ldr( &mut self, - addr: Address, + mem: AMode, rd: WritableReg, size: OperandSize, signed: bool, @@ -234,7 +232,6 @@ impl Assembler { ) { use OperandSize::*; let writable_reg = rd.map(Into::into); - let mem: AMode = addr.try_into().unwrap(); let inst = match (rd.to_reg().is_int(), signed, size) { (_, false, S8) => Inst::ULoad8 { @@ -293,10 +290,9 @@ impl Assembler { } /// Load a pair of registers. - pub fn ldp(&mut self, xt1: Reg, xt2: Reg, addr: Address) { + pub fn ldp(&mut self, xt1: Reg, xt2: Reg, mem: PairAMode) { let writable_xt1 = Writable::from_reg(xt1.into()); let writable_xt2 = Writable::from_reg(xt2.into()); - let mem = addr.try_into().unwrap(); self.emit(Inst::LoadP64 { rt: writable_xt1, @@ -326,7 +322,8 @@ impl Assembler { }); } _ => { - let addr = self.add_constant(&imm.to_bytes()); + let constant = self.add_constant(&imm.to_bytes()); + let addr = AMode::Const { addr: constant }; self.uload(addr, rd, size, TRUSTED_FLAGS); } } @@ -393,12 +390,24 @@ impl Assembler { /// Add with three registers. pub fn add_rrr(&mut self, rm: Reg, rn: Reg, rd: WritableReg, size: OperandSize) { - self.alu_rrr_extend(ALUOp::Add, rm, rn, rd, size); + self.alu_rrr_extend(ALUOp::Add, rm, rn, rd, size, ExtendOp::UXTX); + } + + /// Add with three registers and explicit extend operation. + pub fn add_rrr_with_extend( + &mut self, + rm: Reg, + rn: Reg, + rd: WritableReg, + size: OperandSize, + extendop: ExtendOp, + ) { + self.alu_rrr_extend(ALUOp::Add, rm, rn, rd, size, extendop); } /// Add with three registers, setting overflow flags. pub fn adds_rrr(&mut self, rm: Reg, rn: Reg, rd: WritableReg, size: OperandSize) { - self.alu_rrr_extend(ALUOp::AddS, rm, rn, rd, size); + self.alu_rrr_extend(ALUOp::AddS, rm, rn, rd, size, ExtendOp::UXTX); } /// Add across Vector. @@ -423,12 +432,19 @@ impl Assembler { /// Subtract with three registers. pub fn sub_rrr(&mut self, rm: Reg, rn: Reg, rd: WritableReg, size: OperandSize) { - self.alu_rrr_extend(ALUOp::Sub, rm, rn, rd, size); + self.alu_rrr_extend(ALUOp::Sub, rm, rn, rd, size, ExtendOp::UXTX); } /// Subtract with three registers, setting flags. pub fn subs_rrr(&mut self, rm: Reg, rn: Reg, size: OperandSize) { - self.alu_rrr_extend(ALUOp::SubS, rm, rn, writable!(regs::zero()), size); + self.alu_rrr_extend( + ALUOp::SubS, + rm, + rn, + writable!(regs::zero()), + size, + ExtendOp::UXTX, + ); } /// Multiply with three registers. @@ -996,14 +1012,22 @@ impl Assembler { }); } - fn alu_rrr_extend(&mut self, op: ALUOp, rm: Reg, rn: Reg, rd: WritableReg, size: OperandSize) { + fn alu_rrr_extend( + &mut self, + op: ALUOp, + rm: Reg, + rn: Reg, + rd: WritableReg, + size: OperandSize, + extendop: ExtendOp, + ) { self.emit(Inst::AluRRRExtend { alu_op: op, size: size.into(), rd: rd.map(Into::into), rn: rn.into(), rm: rm.into(), - extendop: ExtendOp::UXTX, + extendop, }); } diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs index ef72fa490c17..bfa446fe25bc 100644 --- a/winch/codegen/src/isa/aarch64/masm.rs +++ b/winch/codegen/src/isa/aarch64/masm.rs @@ -17,7 +17,7 @@ use crate::{ reg::{Reg, WritableReg, writable}, }, masm::{ - CalleeKind, DivKind, Extend, ExtendKind, ExtractLaneKind, FloatCmpKind, FloatScratch, + CalleeKind, DivKind, Extend, ExtendKind, ExtractLaneKind, FloatCmpKind, FloatScratch, Imm, Imm as I, IntCmpKind, IntScratch, LoadKind, MacroAssembler as Masm, MulWideKind, OperandSize, RegImm, RemKind, ReplaceLaneKind, RmwOp, RoundingMode, SPOffset, Scratch, ScratchType, ShiftKind, SplatKind, StackSlot, StoreKind, TRUSTED_FLAGS, TrapCode, @@ -30,8 +30,11 @@ use crate::{ use cranelift_codegen::{ Final, MachBufferFinalized, MachLabel, binemit::CodeOffset, - ir::{MemFlags, RelSourceLoc, SourceLoc}, - isa::aarch64::inst::{self, Cond, Imm12, ImmLogic, ImmShift, VectorSize}, + ir::{MemFlags, RelSourceLoc, SourceLoc, types}, + isa::aarch64, + isa::aarch64::inst::{ + self, Cond, ExtendOp, Imm12, ImmLogic, ImmShift, SImm7Scaled, SImm9, VectorSize, + }, settings, }; use regalloc2::RegClass; @@ -62,11 +65,15 @@ pub(crate) struct MacroAssembler { impl MacroAssembler { /// Create an Aarch64 MacroAssembler. - pub fn new(ptr_size: impl PtrSize, shared_flags: settings::Flags) -> Result { + pub fn new( + ptr_size: impl PtrSize, + shared_flags: settings::Flags, + isa_flags: aarch64::settings::Flags, + ) -> Result { Ok(Self { sp_max: 0, stack_max_use_add: None, - asm: Assembler::new(shared_flags), + asm: Assembler::new(shared_flags, isa_flags), sp_offset: 0u32, ptr_size: ptr_type_from_ptr_size(ptr_size.size()).try_into()?, scratch_scope: RegAlloc::from(scratch_gpr_bitset(), scratch_fpr_bitset()), @@ -137,13 +144,20 @@ impl Masm for MacroAssembler { let fp = regs::fp(); let sp = regs::sp(); - let addr = Address::pre_indexed_from_sp(-16); - self.asm.stp(fp, lr, addr); + let offset = SImm7Scaled::maybe_from_i64(-16, types::I64) + .expect("Frame pointer offset of -16 is valid for pair addressing"); + let addr = Address::pre_indexed_from_sp_for_pair(offset); + self.asm.stp(fp, lr, addr.to_pair_addressing_mode()); self.asm.mov_rr(sp, writable!(fp), OperandSize::S64); - let addr = Address::pre_indexed_from_sp(-(SHADOW_STACK_POINTER_SLOT_SIZE as i64)); - self.asm - .str(regs::shadow_sp(), addr, OperandSize::S64, TRUSTED_FLAGS); + let offset = SImm9::maybe_from_i64(-(SHADOW_STACK_POINTER_SLOT_SIZE as i64)) + .expect("Shadow stack pointer slot size is valid for single addressing"); + let addr = Address::pre_indexed_from_sp(offset); + addr.to_addressing_mode(self, OperandSize::S64, |masm, mem| { + masm.asm + .str(regs::shadow_sp(), mem, OperandSize::S64, TRUSTED_FLAGS); + Ok(()) + })?; self.move_sp_to_shadow_sp(); Ok(()) @@ -207,20 +221,27 @@ impl Masm for MacroAssembler { // Pop the shadow stack pointer. It's assumed that at this point // `sp_offset` is 0 and therefore the real stack pointer should be // 16-byte aligned. - let addr = Address::post_indexed_from_sp(SHADOW_STACK_POINTER_SLOT_SIZE as i64); - self.asm.uload( - addr, - writable!(regs::shadow_sp()), - OperandSize::S64, - TRUSTED_FLAGS, - ); + let offset = SImm9::maybe_from_i64(SHADOW_STACK_POINTER_SLOT_SIZE as i64) + .expect("Shadow stack pointer slot size is valid for single addressing"); + let addr = Address::post_indexed_from_sp(offset); + addr.to_addressing_mode(self, OperandSize::S64, |masm, mem| { + masm.asm.uload( + mem, + writable!(regs::shadow_sp()), + OperandSize::S64, + TRUSTED_FLAGS, + ); + Ok(()) + })?; // Restore the link register and frame pointer. let lr = regs::lr(); let fp = regs::fp(); - let addr = Address::post_indexed_from_sp(16); + let offset = SImm7Scaled::maybe_from_i64(16, types::I64) + .expect("Frame pointer offset 16 is valid for pair addressing"); + let addr = Address::post_indexed_from_sp_for_pair(offset); - self.asm.ldp(fp, lr, addr); + self.asm.ldp(fp, lr, addr.to_pair_addressing_mode()); self.asm.ret(); Ok(()) } @@ -330,34 +351,40 @@ impl Masm for MacroAssembler { RegImm::Imm(v) => { match v { I::I32(_) | I::I64(_) => { - self.with_scratch::(|masm, scratch| { + self.with_scratch::(|masm, scratch| -> Result<()> { masm.asm.mov_ir(scratch.writable(), v, v.size()); - masm.asm.str(scratch.inner(), dst, size, TRUSTED_FLAGS); - }); + dst.to_addressing_mode(masm, size, |masm, mem| { + masm.asm.str(scratch.inner(), mem, size, TRUSTED_FLAGS); + Ok(()) + }) + })?; } imm @ (I::F32(_) | I::F64(_)) => { - self.with_scratch::(|masm, scratch| { + self.with_scratch::(|masm, scratch| -> Result<()> { masm.asm.mov_ir(scratch.writable(), imm, imm.size()); - masm.asm.str(scratch.inner(), dst, size, TRUSTED_FLAGS); - }); + dst.to_addressing_mode(masm, size, |masm, mem| { + masm.asm.str(scratch.inner(), mem, size, TRUSTED_FLAGS); + Ok(()) + }) + })?; } _ => bail!(CodeGenError::unsupported_wasm_type()), }; Ok(()) } - RegImm::Reg(r) => { - self.asm.str(r, dst, size, TRUSTED_FLAGS); + RegImm::Reg(r) => dst.to_addressing_mode(self, size, |masm, mem| { + masm.asm.str(r, mem, size, TRUSTED_FLAGS); Ok(()) - } + }), } } fn wasm_store(&mut self, src: Reg, dst: Self::Address, op_kind: StoreKind) -> Result<()> { self.with_aligned_sp(|masm| match op_kind { - StoreKind::Operand(size) => { - masm.asm.str(src, dst, size, UNTRUSTED_FLAGS); + StoreKind::Operand(size) => dst.to_addressing_mode(masm, size, |masm, mem| { + masm.asm.str(src, mem, size, UNTRUSTED_FLAGS); Ok(()) - } + }), StoreKind::Atomic(_size) => { Err(format_err!(CodeGenError::unimplemented_masm_instruction())) } @@ -400,8 +427,9 @@ impl Masm for MacroAssembler { } fn load(&mut self, src: Address, dst: WritableReg, size: OperandSize) -> Result<()> { - self.asm.uload(src, dst, size, TRUSTED_FLAGS); - Ok(()) + src.to_addressing_mode(self, size, |masm, mem| { + Ok(masm.asm.uload(mem, dst, size, TRUSTED_FLAGS)) + }) } fn load_ptr(&mut self, src: Self::Address, dst: WritableReg) -> Result<()> { @@ -415,19 +443,25 @@ impl Masm for MacroAssembler { if size == OperandSize::S128 { bail!(CodeGenError::UnimplementedWasmLoadKind) } else { - Ok(masm.asm.uload(src, dst, size, UNTRUSTED_FLAGS)) + src.to_addressing_mode(masm, size, |masm, mem| { + Ok(masm.asm.uload(mem, dst, size, UNTRUSTED_FLAGS)) + }) } } LoadKind::Splat(_) => bail!(CodeGenError::UnimplementedWasmLoadKind), LoadKind::ScalarExtend(extend_kind) => { if extend_kind.signed() { - masm.asm.sload(src, dst, size, UNTRUSTED_FLAGS); + src.to_addressing_mode(masm, size, |masm, mem| { + masm.asm.sload(mem, dst, size, UNTRUSTED_FLAGS); + Ok(()) + }) } else { - // unlike x64, unused bits are set to zero so we don't need to extend - masm.asm.uload(src, dst, size, UNTRUSTED_FLAGS); + src.to_addressing_mode(masm, size, |masm, mem| { + // unlike x64, unused bits are set to zero so we don't need to extend + masm.asm.uload(mem, dst, size, UNTRUSTED_FLAGS); + Ok(()) + }) } - - Ok(()) } LoadKind::VectorExtend(_vector_extend_kind) => { bail!(CodeGenError::UnimplementedWasmLoadKind) @@ -454,7 +488,10 @@ impl Masm for MacroAssembler { fn pop(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> { let addr = self.address_from_sp(SPOffset::from_u32(self.sp_offset))?; - self.asm.uload(addr, dst, size, TRUSTED_FLAGS); + addr.to_addressing_mode(self, size, |masm, mem| { + masm.asm.uload(mem, dst, size, TRUSTED_FLAGS); + Ok(()) + })?; self.free_stack(size.bytes()) } @@ -522,11 +559,34 @@ impl Masm for MacroAssembler { } } + fn add_uextend( + &mut self, + dst: WritableReg, + lhs: Reg, + rhs: Reg, + from_size: OperandSize, + size: OperandSize, + ) -> Result<()> { + assert!(from_size.num_bits() <= size.num_bits()); + let extendop = match from_size { + OperandSize::S8 => ExtendOp::UXTB, + OperandSize::S16 => ExtendOp::UXTH, + OperandSize::S32 => ExtendOp::UXTW, + OperandSize::S64 => ExtendOp::UXTX, + OperandSize::S128 => { + return Err(format_err!(CodeGenError::invalid_operand_combination())); + } + }; + + self.asm.add_rrr_with_extend(rhs, lhs, dst, size, extendop); + Ok(()) + } + fn checked_uadd( &mut self, dst: WritableReg, lhs: Reg, - rhs: RegImm, + rhs: Imm, size: OperandSize, trap: TrapCode, ) -> Result<()> { @@ -534,25 +594,17 @@ impl Masm for MacroAssembler { // ensure that the real SP is 16-byte aligned in case control flow is // transferred to a signal handler. self.with_aligned_sp(|masm| { - match (rhs, lhs, dst) { - // NB: we don't use `Self::add_ir` since we explicitly - // want to emit the add variant which sets overflow - // flags. - (RegImm::Imm(i), rn, rd) => { - let imm = i.unwrap_as_u64(); - match Imm12::maybe_from_u64(imm) { - Some(imm12) => masm.asm.adds_ir(imm12, rn, rd, size), - None => { - masm.with_scratch::(|masm, scratch| { - masm.asm.mov_ir(scratch.writable(), i, i.size()); - masm.asm.adds_rrr(scratch.inner(), rn, rd, size); - }); - } - } - } - - (RegImm::Reg(rm), rn, rd) => { - masm.asm.adds_rrr(rm, rn, rd, size); + // NB: we don't use `Self::add_ir` since we explicitly + // want to emit the add variant which sets overflow + // flags. + let imm = rhs.unwrap_as_u64(); + match Imm12::maybe_from_u64(imm) { + Some(imm12) => masm.asm.adds_ir(imm12, lhs, dst, size), + None => { + masm.with_scratch::(|masm, scratch| { + masm.asm.mov_ir(scratch.writable(), rhs, rhs.size()); + masm.asm.adds_rrr(scratch.inner(), lhs, dst, size); + }); } } masm.asm.trapif(Cond::Hs, trap); @@ -971,7 +1023,10 @@ impl Masm for MacroAssembler { fn push(&mut self, reg: Reg, size: OperandSize) -> Result { self.reserve_stack(size.bytes())?; let address = self.address_from_sp(SPOffset::from_u32(self.sp_offset))?; - self.asm.str(reg, address, size, TRUSTED_FLAGS); + address.to_addressing_mode(self, size, |masm, mem| { + masm.asm.str(reg, mem, size, TRUSTED_FLAGS); + Ok(()) + })?; Ok(StackSlot { offset: SPOffset::from_u32(self.sp_offset), @@ -1571,7 +1626,7 @@ impl MacroAssembler { self.asm.mov_rr(sp, writable!(shadow_sp), OperandSize::S64); } - /// Heloper to add an immediate to a register. + /// Helper to add an immediate to a register. fn add_ir(&mut self, dst: WritableReg, lhs: Reg, rhs: I, size: OperandSize) -> Result<()> { let imm = rhs.unwrap_as_u64(); match Imm12::maybe_from_u64(imm) { diff --git a/winch/codegen/src/isa/aarch64/mod.rs b/winch/codegen/src/isa/aarch64/mod.rs index 98f1e93d307d..2c564b02a726 100644 --- a/winch/codegen/src/isa/aarch64/mod.rs +++ b/winch/codegen/src/isa/aarch64/mod.rs @@ -92,7 +92,11 @@ impl TargetIsa for Aarch64 { let pointer_bytes = self.pointer_bytes(); let vmoffsets = VMOffsets::new(pointer_bytes, &translation.module); let mut body = body.get_binary_reader(); - let mut masm = Aarch64Masm::new(pointer_bytes, self.shared_flags.clone())?; + let mut masm = Aarch64Masm::new( + pointer_bytes, + self.shared_flags.clone(), + self.isa_flags.clone(), + )?; let stack = Stack::new(); let abi_sig = wasm_sig::(sig)?; diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs index 7902d3339b31..eda638034f29 100644 --- a/winch/codegen/src/isa/x64/masm.rs +++ b/winch/codegen/src/isa/x64/masm.rs @@ -6,13 +6,13 @@ use super::{ regs::{self, rbp, rsp, scratch_fpr_bitset, scratch_gpr_bitset}, }; use crate::masm::{ - DivKind, Extend, ExtendKind, ExtractLaneKind, FloatCmpKind, FloatScratch, Imm as I, IntCmpKind, - IntScratch, LaneSelector, LoadKind, MacroAssembler as Masm, MulWideKind, OperandSize, RegImm, - RemKind, ReplaceLaneKind, RmwOp, RoundingMode, Scratch, ScratchType, ShiftKind, SplatKind, - StoreKind, TRUSTED_FLAGS, TrapCode, TruncKind, UNTRUSTED_FLAGS, V128AbsKind, V128AddKind, - V128ConvertKind, V128ExtAddKind, V128ExtMulKind, V128ExtendKind, V128MaxKind, V128MinKind, - V128MulKind, V128NarrowKind, V128NegKind, V128SubKind, V128TruncKind, VectorCompareKind, - VectorEqualityKind, Zero, + DivKind, Extend, ExtendKind, ExtractLaneKind, FloatCmpKind, FloatScratch, Imm, Imm as I, + IntCmpKind, IntScratch, LaneSelector, LoadKind, MacroAssembler as Masm, MulWideKind, + OperandSize, RegImm, RemKind, ReplaceLaneKind, RmwOp, RoundingMode, Scratch, ScratchType, + ShiftKind, SplatKind, StoreKind, TRUSTED_FLAGS, TrapCode, TruncKind, UNTRUSTED_FLAGS, + V128AbsKind, V128AddKind, V128ConvertKind, V128ExtAddKind, V128ExtMulKind, V128ExtendKind, + V128MaxKind, V128MinKind, V128MulKind, V128NarrowKind, V128NegKind, V128SubKind, V128TruncKind, + VectorCompareKind, VectorEqualityKind, Zero, }; use crate::{ Result, @@ -484,15 +484,40 @@ impl Masm for MacroAssembler { Ok(()) } + fn add_uextend( + &mut self, + dst: WritableReg, + lhs: Reg, + rhs: Reg, + from_size: OperandSize, + size: OperandSize, + ) -> Result<()> { + assert!(size == OperandSize::S64); + assert!(from_size == OperandSize::S32 || from_size == OperandSize::S64); + + Self::ensure_two_argument_form(&dst.to_reg(), &lhs)?; + if from_size == OperandSize::S32 && size == OperandSize::S64 { + self.extend( + writable!(rhs), + rhs, + ExtendKind::Unsigned(Extend::I64Extend32), + )?; + } + + self.asm.add_rr(rhs, dst, size); + + Ok(()) + } + fn checked_uadd( &mut self, dst: WritableReg, lhs: Reg, - rhs: RegImm, + rhs: Imm, size: OperandSize, trap: TrapCode, ) -> Result<()> { - self.add(dst, lhs, rhs, size)?; + self.add(dst, lhs, RegImm::Imm(rhs), size)?; self.asm.trapif(CC::B, trap); Ok(()) } diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs index 0955bf46197e..01fd8d988535 100644 --- a/winch/codegen/src/masm.rs +++ b/winch/codegen/src/masm.rs @@ -1624,13 +1624,26 @@ pub(crate) trait MacroAssembler { /// Perform add operation. fn add(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()>; + /// Perform add with unsigned extension. + fn add_uextend( + &mut self, + dst: WritableReg, + lhs: Reg, + rhs: Reg, + from_size: OperandSize, + size: OperandSize, + ) -> Result<()>; + /// Perform a checked unsigned integer addition, emitting the provided trap /// if the addition overflows. + /// + /// Note: This only accepts immediate operands. For register operands with + /// proper extension, use add_uextend with manual overflow checking. fn checked_uadd( &mut self, dst: WritableReg, lhs: Reg, - rhs: RegImm, + rhs: Imm, size: OperandSize, trap: TrapCode, ) -> Result<()>; diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs index 4d643266ed48..9d358c20fbf0 100644 --- a/winch/codegen/src/visitor.rs +++ b/winch/codegen/src/visitor.rs @@ -23,12 +23,13 @@ use crate::{Result, bail, ensure, format_err}; use regalloc2::RegClass; use smallvec::{SmallVec, smallvec}; use wasmparser::{ - BlockType, BrTable, Ieee32, Ieee64, MemArg, V128, VisitOperator, VisitSimdOperator, + BlockType, BrTable, HeapType, Ieee32, Ieee64, MemArg, V128, ValType, VisitOperator, + VisitSimdOperator, }; use wasmtime_cranelift::TRAP_INDIRECT_CALL_TO_NULL; use wasmtime_environ::{ - FUNCREF_INIT_BIT, FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TypeIndex, WasmHeapType, - WasmValType, + FUNCREF_INIT_BIT, FuncIndex, GlobalIndex, IndexType, MemoryIndex, TableIndex, TypeIndex, + WasmHeapType, WasmValType, }; /// A macro to define unsupported WebAssembly operators. @@ -203,6 +204,10 @@ macro_rules! def_unsupported { (emit GlobalGet $($rest:tt)*) => {}; (emit GlobalSet $($rest:tt)*) => {}; (emit Select $($rest:tt)*) => {}; + (emit TypedSelect $($rest:tt)*) => {}; + (emit RefNull $($rest:tt)*) => {}; + (emit RefIsNull $($rest:tt)*) => {}; + (emit RefFunc $($rest:tt)*) => {}; (emit Drop $($rest:tt)*) => {}; (emit BrTable $($rest:tt)*) => {}; (emit CallIndirect $($rest:tt)*) => {}; @@ -1700,8 +1705,12 @@ where fn visit_table_grow(&mut self, table: u32) -> Self::Output { let table_index = TableIndex::from_u32(table); - let table_ty = self.env.table(table_index); - let builtin = match table_ty.ref_type.heap_type { + let ptr_type = self.env.ptr_type(); + let (heap_type, idx_type) = { + let table_ty = self.env.table(table_index); + (table_ty.ref_type.heap_type, table_ty.idx_type) + }; + let builtin = match heap_type { WasmHeapType::Func => self.env.builtins.table_grow_func_ref::()?, _ => bail!(CodeGenError::unsupported_wasm_type()), }; @@ -1722,7 +1731,20 @@ where FnCall::emit::(&mut self.env, self.masm, &mut self.context, builtin)?; - Ok(()) + // Similar to the memory.grow builtin, `table.grow` returns a + // pointer, however, we need to ensure that the returned index + // is representative of the address space for tables. + match (ptr_type, idx_type) { + (WasmValType::I64, IndexType::I64) => Ok(()), + (WasmValType::I64, IndexType::I32) => { + let top: Reg = self.context.pop_to_reg(self.masm, None)?.into(); + self.masm.wrap(writable!(top), top)?; + self.context.stack.push(TypedReg::i32(top).into()); + Ok(()) + } + + _ => Err(format_err!(CodeGenError::unsupported_32_bit_platform())), + } } fn visit_table_size(&mut self, table: u32) -> Self::Output { @@ -1744,13 +1766,9 @@ where let at = self.context.stack.ensure_index_at(3)?; - self.context.stack.insert_many(at, &[table.try_into()?]); - FnCall::emit::( - &mut self.env, - self.masm, - &mut self.context, - Callee::Builtin(builtin.clone()), - )?; + let callee = self.prepare_builtin_defined_table_arg(table_index, at, builtin)?; + FnCall::emit::(&mut self.env, self.masm, &mut self.context, callee)?; + self.context.pop_and_free(self.masm) } @@ -2207,6 +2225,48 @@ where Ok(()) } + fn visit_typed_select(&mut self, _ty: ValType) -> Self::Output { + self.visit_select() + } + + fn visit_ref_null(&mut self, hty: HeapType) -> Self::Output { + match hty { + HeapType::FUNC => { + let ptr_type = self.env.ptr_type(); + match ptr_type { + WasmValType::I64 => self.context.stack.push(Val::i64(0)), + WasmValType::I32 => self.context.stack.push(Val::i32(0)), + _ => bail!(CodeGenError::unsupported_wasm_type()), + } + Ok(()) + } + _ => Err(format_err!(CodeGenError::unsupported_wasm_type())), + } + } + + fn visit_ref_is_null(&mut self) -> Self::Output { + let (zero, size) = match self.env.ptr_type() { + WasmValType::I64 => (RegImm::i64(0), OperandSize::S64), + WasmValType::I32 => (RegImm::i32(0), OperandSize::S32), + _ => bail!(CodeGenError::unsupported_wasm_type()), + }; + self.context.unop(self.masm, |masm, reg| { + masm.cmp_with_set(writable!(reg), zero, IntCmpKind::Eq, size)?; + Ok(TypedReg::i32(reg)) + }) + } + + fn visit_ref_func(&mut self, function_index: u32) -> Self::Output { + let ref_func = self.env.builtins.ref_func::()?; + self.context.stack.extend([function_index.try_into()?]); + FnCall::emit::( + &mut self.env, + self.masm, + &mut self.context, + Callee::Builtin(ref_func), + ) + } + fn visit_i32_load(&mut self, memarg: MemArg) -> Self::Output { self.emit_wasm_load( &memarg,