diff --git a/.github/ISSUE_TEMPLATE/blank-issue.md b/.github/ISSUE_TEMPLATE/blank-issue.md deleted file mode 100644 index a08ad07cbf8d..000000000000 --- a/.github/ISSUE_TEMPLATE/blank-issue.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Blank Issue -about: Create a blank issue. -title: '' -labels: '' -assignees: '' - ---- - - diff --git a/.github/ISSUE_TEMPLATE/clif-bug-report.md b/.github/ISSUE_TEMPLATE/clif-bug-report.md deleted file mode 100644 index 2acc0b06f4f7..000000000000 --- a/.github/ISSUE_TEMPLATE/clif-bug-report.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -name: Cranelift Bug Report -about: Report a bug or a crash in Cranelift. -title: 'Cranelift: ' -labels: bug, cranelift -assignees: '' - ---- - -Thanks for filing an issue! Please fill out the TODOs below. - -### `.clif` Test Case - -``` -TODO: paste .clif test case here. Ideally, a test case that has been reduced via -the `clif-util bugpoint` command. -``` - -### Steps to Reproduce - -* TODO: First, ... -* TODO: Then, ... -* Etc... - -### Expected Results - -TODO: What do you expect to happen? - -### Actual Results - -TODO: What actually happens? Panic? Segfault? Incorrect result? - -### Versions and Environment - -Cranelift version or commit: TODO - -Operating system: TODO - -Architecture: TODO - -### Extra Info - -Anything else you'd like to add? diff --git a/.github/ISSUE_TEMPLATE/fuzzbug.md b/.github/ISSUE_TEMPLATE/fuzzbug.md deleted file mode 100644 index 52cd30bf6144..000000000000 --- a/.github/ISSUE_TEMPLATE/fuzzbug.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: Fuzz Bug Report -about: Report a fuzz bug in Wasmtime or Cranelift -title: ' fuzzbug: ' -labels: bug, fuzz-bug -assignees: '' ---- - -Thanks for filing an issue! Please fill out the TODOs below, and change `` in the title to the corresponding fuzzing target. - - - -
-Test case input - - - -``` -TODO_paste_the_base64_encoded_input_here -``` - -
- -
-`cargo +nightly fuzz fmt` output - - - -``` -TODO_paste_cargo_fuzz_fmt_output_here -``` - -
- -
-Stack trace or other relevant details - - - -``` -TODO_paste_the_report_here -``` - -
diff --git a/.github/ISSUE_TEMPLATE/improvement.md b/.github/ISSUE_TEMPLATE/improvement.md deleted file mode 100644 index 8146e0c58c0a..000000000000 --- a/.github/ISSUE_TEMPLATE/improvement.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: Improvement -about: A feature request or code improvement. -title: '' -labels: '' -assignees: '' - ---- - -Thanks for filing a feature request! Please fill out the TODOs below. - -#### Feature - -TODO: Brief description of the feature/improvement you'd like to see in -Cranelift/Wasmtime. - -#### Benefit - -TODO: What is the value of adding this in Cranelift/Wasmtime? What problems does -it solve? - -#### Implementation - -TODO: Do you have an implementation plan, and/or ideas for data structures or -algorithms to use? - -#### Alternatives - -TODO: What are the alternative implementation approaches or alternative ways to -solve the problem that this feature would solve? How do these alternatives -compare to this proposal? diff --git a/.github/ISSUE_TEMPLATE/wasmtime-bug-report.md b/.github/ISSUE_TEMPLATE/wasmtime-bug-report.md deleted file mode 100644 index cc2e349af8c0..000000000000 --- a/.github/ISSUE_TEMPLATE/wasmtime-bug-report.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -name: Wasmtime Bug Report -about: Report a bug or a crash in Wasmtime -title: '' -labels: bug -assignees: '' - ---- - -Thanks for filing a bug report! Please fill out the TODOs below. - -**Note: if you want to report a security issue, please read our [security policy](https://bytecodealliance.org/security)!** - -### Test Case - -TODO: upload Wasm file here - -### Steps to Reproduce - -* TODO: first, ... -* TODO: second, ... -* Etc... - -### Expected Results - -TODO: What do you expect to happen? - -### Actual Results - -TODO: What actually happens? Panic? Segfault? Incorrect result? - -### Versions and Environment - -Wasmtime version or commit: TODO - -Operating system: TODO - -Architecture: TODO - -### Extra Info - -Anything else you'd like to add? diff --git a/.github/actions/android-ndk/action.yml b/.github/actions/android-ndk/action.yml new file mode 100644 index 000000000000..cbf88a14f891 --- /dev/null +++ b/.github/actions/android-ndk/action.yml @@ -0,0 +1,32 @@ +# This is a small action which uses the pre-installed Android NDK on GitHub +# Actions builders, configured with `$ANDROID_NDK`, to compile and link Android +# code. For Rust we mostly need to configure the linker to Cargo and the C +# compiler to the `cc` crate, so this sets various environment variables to the +# appropriate tool within `$ANDROID_NDK`. + +name: 'Setup Rust to use the Android NDK' +description: 'Setup Rust to use the android NDK' + +inputs: + target: + description: 'Rust target being used' + required: true + android-platform: + description: 'Platform version to use for the C compiler' + required: false + default: '26' + +runs: + using: composite + steps: + - run: | + target=${{ inputs.target }} + cc_target=$(echo ${{ inputs.target }} | tr - _) + upcase=$(echo $target | tr a-z- A-Z_) + ndk_bin=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin + linker=$ndk_bin/${target}${{ inputs.android-platform }}-clang + echo CARGO_TARGET_${upcase}_LINKER=$linker >> $GITHUB_ENV + echo CC_${cc_target}=$linker >> $GITHUB_ENV + echo RANLIB_${cc_target}=$ndk_bin/llvm-ranlib >> $GITHUB_ENV + echo AR_${cc_target}=$ndk_bin/llvm-ar >> $GITHUB_ENV + shell: bash diff --git a/.github/actions/binary-compatible-builds/action.yml b/.github/actions/binary-compatible-builds/action.yml index b5a190c4a06f..8abc72d63742 100644 --- a/.github/actions/binary-compatible-builds/action.yml +++ b/.github/actions/binary-compatible-builds/action.yml @@ -2,7 +2,7 @@ name: 'Set up a CentOS 6 container to build releases in' description: 'Set up a CentOS 6 container to build releases in' runs: - using: node16 + using: node20 main: 'main.js' inputs: name: diff --git a/.github/actions/binary-compatible-builds/main.js b/.github/actions/binary-compatible-builds/main.js index 378c5c202c9c..1101d78e4a41 100755 --- a/.github/actions/binary-compatible-builds/main.js +++ b/.github/actions/binary-compatible-builds/main.js @@ -21,12 +21,37 @@ if (process.platform == 'win32') { return; } +// Android doesn't use a container as it's controlled by the installation of the +// SDK/NDK. +if (process.env.INPUT_NAME && process.env.INPUT_NAME.indexOf("android") >= 0) { + return; +} + // ... and on Linux we do fancy things with containers. We'll spawn an old // CentOS container in the background with a super old glibc, and then we'll run // commands in there with the `$CENTOS` env var. if (process.env.CENTOS !== undefined) { - const args = ['exec', '-w', process.cwd(), '-i', 'build-container']; + const args = ['exec', '--workdir', process.cwd(), '--interactive']; + // Forward any rust-looking env vars from the environment into the container + // itself. + for (let key in process.env) { + if (key.startsWith('CARGO') || key.startsWith('RUST')) { + args.push('--env'); + args.push(key); + } + } + args.push('build-container') + + // Start the container by appending to `$PATH` with the `/rust/bin` path that + // is mounted below. + args.push('bash'); + args.push('-c'); + args.push('export PATH=$PATH:/rust/bin; export RUSTFLAGS="$RUSTFLAGS $EXTRA_RUSTFLAGS"; exec "$@"'); + args.push('bash'); + + // Add in whatever we're running which will get executed in the sub-shell with + // an augmented PATH. for (const arg of process.argv.slice(2)) { args.push(arg); } @@ -34,7 +59,7 @@ if (process.env.CENTOS !== undefined) { return; } -const name = process.env.INPUT_NAME; +const name = process.env.INPUT_NAME.replace(/-min$/, ''); child_process.execFileSync('docker', [ 'build', diff --git a/.github/actions/build-adapter-provider/action.yml b/.github/actions/build-adapter-provider/action.yml new file mode 100644 index 000000000000..60b9d08a37a0 --- /dev/null +++ b/.github/actions/build-adapter-provider/action.yml @@ -0,0 +1,25 @@ +name: 'Build wasi component adapter provider' +description: 'Build the wasi-preview1-component-adapter provider using the adapter artefacts' + +inputs: + run-id: + description: 'run id of the main.yml action that produced the adapter artefacts' + required: true + +runs: + using: composite + steps: + - uses: actions/download-artifact@v4 + with: + name: bins-wasi-preview1-component-adapter + path: crates/wasi-preview1-component-adapter/provider/artefacts + github-token: ${{ github.token }} + run-id: ${{ inputs.run-id }} + + - name: Install required Rust components + shell: bash + run: rustup component add rustfmt clippy + + - name: Build and checl the adapter provider + shell: bash + run: ./ci/build-wasi-preview1-component-adapter-provider.sh diff --git a/.github/actions/fetch-run-id/action.yml b/.github/actions/fetch-run-id/action.yml new file mode 100644 index 000000000000..614f2e37c64a --- /dev/null +++ b/.github/actions/fetch-run-id/action.yml @@ -0,0 +1,18 @@ +name: 'Fetch run id for commit' +description: 'Fetch the main.yml run id for the current commit' + +runs: + using: composite + steps: + - name: Fetch run id + shell: bash + run: | + run_id=$( + gh api -H 'Accept: application/vnd.github+json' \ + /repos/${{ github.repository }}/actions/workflows/main.yml/runs\?exclude_pull_requests=true \ + | jq '.workflow_runs' \ + | jq "map(select(.head_commit.id == \"${{ github.sha }}\"))[0].id" \ + ) + echo COMMIT_RUN_ID=${run_id} >> "$GITHUB_ENV" + env: + GH_TOKEN: ${{ github.token }} diff --git a/.github/actions/github-release/main.js b/.github/actions/github-release/main.js index d61b24ed5ef3..bcf4f75b490a 100644 --- a/.github/actions/github-release/main.js +++ b/.github/actions/github-release/main.js @@ -91,13 +91,25 @@ async function runOnce() { } catch (e) { console.log("ERROR: ", JSON.stringify(e, null, 2)); core.info(`creating a release`); + + const releaseNotes = fs.readFileSync('RELEASES.md').toString(); + let notes = null; + const opts = { + owner, + repo, + tag_name: name, + prerelease: name === 'dev', + }; + if (name !== 'dev') { + for (let x of releaseNotes.split(/^---+$/m)) { + if (x.indexOf(name.substring(1)) == -1) + continue; + opts.body = x; + break; + } + } try { - release = await octokit.rest.repos.createRelease({ - owner, - repo, - tag_name: name, - prerelease: name === 'dev', - }); + release = await octokit.rest.repos.createRelease(opts); } catch(e) { console.log("ERROR: ", JSON.stringify(e, null, 2)); core.info(`fetching one more time`); diff --git a/.github/actions/install-cargo-vet/action.yml b/.github/actions/install-cargo-vet/action.yml index 0d481d744236..7757f78956b5 100644 --- a/.github/actions/install-cargo-vet/action.yml +++ b/.github/actions/install-cargo-vet/action.yml @@ -5,16 +5,16 @@ inputs: version: description: 'Version to install' required: false - default: '0.8.0' + default: '0.9.0' runs: using: composite steps: - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ${{ runner.tool_cache }}/cargo-vet key: cargo-vet-bin-${{ inputs.version }} - run: echo "${{ runner.tool_cache }}/cargo-vet/bin" >> $GITHUB_PATH shell: bash - - run: cargo install --root ${{ runner.tool_cache }}/cargo-vet --version ${{ inputs.version }} cargo-vet + - run: cargo install --root ${{ runner.tool_cache }}/cargo-vet --version ${{ inputs.version }} cargo-vet --locked shell: bash diff --git a/.github/actions/install-rust/action.yml b/.github/actions/install-rust/action.yml index 003c994f6dfc..c5f40462ac58 100644 --- a/.github/actions/install-rust/action.yml +++ b/.github/actions/install-rust/action.yml @@ -1,15 +1,11 @@ name: 'Install Rust toolchain' -description: 'Install a rust toolchain and cache the crates index' +description: 'Install a rust toolchain' inputs: toolchain: description: 'Default toolchan to install' required: false default: 'default' - lockfiles: - description: 'Path glob for Cargo.lock files to use as cache keys' - required: false - default: '**/Cargo.lock' runs: using: composite @@ -26,6 +22,8 @@ runs: echo "version=1.$((msrv+2)).0" >> "$GITHUB_OUTPUT" elif [ "${{ inputs.toolchain }}" = "msrv" ]; then echo "version=1.$msrv.0" >> "$GITHUB_OUTPUT" + elif [ "${{ inputs.toolchain }}" = "wasmtime-ci-pinned-nightly" ]; then + echo "version=nightly-2024-08-12" >> "$GITHUB_OUTPUT" else echo "version=${{ inputs.toolchain }}" >> "$GITHUB_OUTPUT" fi @@ -55,49 +53,10 @@ runs: EOF fi - # Use a more efficient method for fetching the crates.io-index than - # the (currently) default git-based index. - cat >> "$GITHUB_ENV" <> $GITHUB_ENV + run: echo WIT_REQUIRE_SEMICOLONS=1 >> "$GITHUB_ENV" - - name: Cache Cargo registry index - uses: actions/cache@v3 - with: - path: ~/.cargo/registry/index/ - key: cargo-registry-${{ env.CARGO_REGISTRY_CACHE_KEY }} - # Any older registry-index cache is still valid. It's a git clone, so - # cargo only has to pull down the changes since the index was cached. - restore-keys: cargo-registry- - - - name: Cache crate sources for dependencies - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: cargo-crates-${{ inputs.lockfiles }}-${{ hashFiles(inputs.lockfiles) }} - # If Cargo.lock has changed, we probably will need to get the source - # code for some crates we don't already have. But any crates we have - # source cached for are still valid. The only problem is nothing - # removes old crate sources from the cache, so using `restore-keys` - # this way may use more of our GitHub cache quota than we'd like. - # - # Also, scope this cache by which Cargo.lock we're building from. - # Otherwise, whichever job writes the cache first will get its - # dependencies cached, and that cache will be used as the basis for the - # next job, even though odds are pretty good the cache is useless. - restore-keys: cargo-crates-${{ inputs.lockfiles }}- - -# TODO: on cache miss, after cargo has updated the registry index, run `git gc` + - name: Install the WASI target + shell: bash + run: rustup target add wasm32-wasip1 wasm32-unknown-unknown diff --git a/.github/labeler.yml b/.github/labeler.yml index 0c91d826b44d..e404e6b4920c 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -56,6 +56,9 @@ - "cranelift/codegen/src/isa/*/lower/isle/**" - "cranelift/codegen/src/opts/*" +"pulley": + - "pulley/**" + "wasi": - "crates/wasi/**" - "crates/wasi-common/**" @@ -76,8 +79,12 @@ - "docs/**" "wasmtime:ref-types": - - "crates/wasmtime/src/ref.rs" - - "crates/runtime/src/externref.rs" + - "crates/cranelift/src/gc.rs" + - "crates/cranelift/src/gc/**" + - "crates/wasmtime/src/runtime/gc.rs" + - "crates/wasmtime/src/runtime/gc/**" + - "crates/wasmtime/src/runtime/vm/gc.rs" + - "crates/wasmtime/src/runtime/vm/gc/**" "winch": - "winch/**" diff --git a/.github/subscribe-to-label.json b/.github/subscribe-to-label.json index a39d316323eb..6623355d469b 100644 --- a/.github/subscribe-to-label.json +++ b/.github/subscribe-to-label.json @@ -1,6 +1,5 @@ { "cfallin": ["isle"], - "fitzgen": ["fuzzing", "isle", "wasmtime:ref-types"], - "peterhuene": ["wasmtime:api", "wasmtime:c-api"], + "fitzgen": ["fuzzing", "isle", "pulley", "wasmtime:ref-types"], "saulecabrera": ["winch"] } diff --git a/.github/workflows/aslp.yml b/.github/workflows/aslp.yml new file mode 100644 index 000000000000..20b57b66a041 --- /dev/null +++ b/.github/workflows/aslp.yml @@ -0,0 +1,83 @@ +name: aslp + +on: + push: + branches: + - veriisle + paths: + - '.github/workflows/aslp.yml' + - '**/aslp/**' + - '**/isaspec/**' + - 'cranelift/codegen/src/isa/aarch64/spec/*.isle' + pull_request: + branches: + - veriisle + paths: + - '.github/workflows/aslp.yml' + - '**/aslp/**' + - '**/isaspec/**' + - 'cranelift/codegen/src/isa/aarch64/spec/*.isle' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + env: + ASLP_PATH: ${{ github.workspace }}/tools/aslp + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + path: main + - name: Setup Rust Toolchain + uses: dtolnay/rust-toolchain@stable + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: main + - name: Install LLVM + env: + LLVM_VERSION: 18 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh "${LLVM_VERSION}" + echo "/usr/lib/llvm-${LLVM_VERSION}/bin" >> "${GITHUB_PATH}" + - name: Install OPAM + run: sudo apt-get install -y opam + - name: Cache ASLp + id: cache-aslp + uses: actions/cache@v4 + with: + path: ${{ env.ASLP_PATH }} + key: aslp-${{ runner.os }}-${{ hashFiles('main/cranelift/isle/veri/veri/script/install/aslp.sh') }} + - name: Install ASLp + if: steps.cache-aslp.outputs.cache-hit != 'true' + working-directory: main/cranelift/isle/veri/veri + run: | + mkdir -p "${ASLP_PATH}" + ./script/install/aslp.sh -i "${ASLP_PATH}" -t "${RUNNER_TEMP}" + - name: Configure ASLp + run: echo "${ASLP_PATH}/bin" >> $GITHUB_PATH + + - name: Generate ISA Specifications + working-directory: main/cranelift/isle/veri/isaspec + run: ./script/generate.sh -l + - name: Git Status + working-directory: main + run: | + git diff + test -z "$(git status --porcelain)" + + - name: Generate ASLp Test Data + working-directory: main/cranelift/isle/veri/aslp/tests/data + run: ./generate.sh + - name: Git Status + working-directory: main + run: | + git diff + test -z "$(git status --porcelain)" diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml deleted file mode 100644 index d17cc3674230..000000000000 --- a/.github/workflows/cargo-audit.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Run cargo-audit -on: - schedule: - - cron: '0 0 * * *' -jobs: - security_audit: - if: github.repository == 'bytecodealliance/wasmtime' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: actions-rs/audit-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000000..d6064760f399 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,95 @@ +name: ci + +on: + push: + branches: + - veriisle + pull_request: + branches: + - veriisle + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + - name: Setup Rust Toolchain + uses: dtolnay/rust-toolchain@1.85 + with: + targets: wasm32-wasip1,wasm32-unknown-unknown + components: rustfmt + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + - name: Setup Tools Directory + run: | + mkdir -p "${{ runner.workspace }}/tools" + echo "${{ runner.workspace }}/tools/bin" >> $GITHUB_PATH + - name: Install CVC5 + working-directory: cranelift/isle/veri/veri + run: ./script/install/cvc5.sh -i "${{ runner.workspace }}/tools" -t "${RUNNER_TEMP}" + - name: Show CVC5 Version + run: cvc5 --version + - name: Install Z3 + working-directory: cranelift/isle/veri/veri + run: ./script/install/z3.sh -b "${{ runner.workspace }}/tools/bin" -t "${RUNNER_TEMP}" + - name: Show Z3 Version + run: z3 --version + - name: Build + run: cargo build --features cranelift-isle/printer --package 'cranelift-isle*' + - name: Test + run: cargo test --features cranelift-isle/printer --package 'cranelift-isle*' + - name: Wast Tests + run: cargo test --test wast + - name: Format + run: cargo fmt --check --verbose + + veri: + strategy: + matrix: + arch: [aarch64, x64] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + path: main + - name: Setup Rust Toolchain + uses: dtolnay/rust-toolchain@stable + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: main + - name: Setup Tools Directory + run: | + mkdir -p "${{ runner.workspace }}/tools" + echo "${{ runner.workspace }}/tools/bin" >> $GITHUB_PATH + - name: Install CVC5 + working-directory: main/cranelift/isle/veri/veri + run: ./script/install/cvc5.sh -i "${{ runner.workspace }}/tools" -t "${RUNNER_TEMP}" + - name: Show CVC5 Version + run: cvc5 --version + - name: Install Z3 + working-directory: main/cranelift/isle/veri/veri + run: ./script/install/z3.sh -b "${{ runner.workspace }}/tools/bin" -t "${RUNNER_TEMP}" + - name: Show Z3 Version + run: z3 --version + - name: Build + run: cargo build --release -p cranelift-isle-veri + working-directory: main + - name: Verify + working-directory: main/cranelift/isle/veri/veri + run: ./script/verify/ci.sh -a ${{ matrix.arch }} -p release -t "${RUNNER_TEMP}" -o "${{ runner.workspace }}/output" + - uses: actions/upload-artifact@v4 + if: github.event_name == 'push' + with: + name: veri-${{ matrix.arch }} + path: ${{ runner.workspace }}/output + if-no-files-found: error + compression-level: 9 diff --git a/.github/workflows/explorer.yml b/.github/workflows/explorer.yml new file mode 100644 index 000000000000..fe15ec67cf94 --- /dev/null +++ b/.github/workflows/explorer.yml @@ -0,0 +1,51 @@ +name: explorer + +on: + push: + branches: + - veriisle + workflow_dispatch: + pull_request: + branches: + - veriisle + paths: + - '.github/workflows/explorer.yml' + - 'cranelift/isle/veri/veri/script/explorer.sh' + +# Sets permissions to allow deployment to Github Pages. +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Rust Toolchain + uses: dtolnay/rust-toolchain@stable + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + - name: Install Tools + run: sudo apt-get install -y graphviz + - name: Build Explorer + working-directory: cranelift/isle/veri/veri + run: ./script/explorer.sh -o "${{ runner.temp }}/explorer" + env: + ISLE_EXPLORER_GRAPHS: 'true' + - name: Upload artifact + if: github.event_name != 'pull_request' + id: upload + uses: actions/upload-pages-artifact@v3 + with: + path: ${{ runner.temp }}/explorer + - name: Deploy to GitHub Pages + if: steps.upload.outcome == 'success' + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index a1ab4c206332..000000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,836 +0,0 @@ -name: CI -on: - # Run CI for PRs to `main` and to release branches. - # - # Note that PRs to `main` will run a subset of tests and PRs to the - # `release-*` branches will run full CI. - pull_request: - branches: - - main - - 'release-*' - - # Run full CI on the `main` branch once a day to prime the GitHub Actions - # caches used by PRs and the merge queue. - schedule: - - cron: '13 4 * * *' - - # This is the CI that runs for PRs-to-merge. - merge_group: - - push: - branches: - # Right now merge queues can't be used with wildcards in branch protections - # so full CI runs both on PRs to release branches as well as merges to - # release branches. Note that the merge to a release branch may produce a - # tag at the end of CI if successful and the tag will trigger the artifact - # uploads as well as publication to crates.io. - - 'release-*' - -defaults: - run: - shell: bash - -# Cancel any in-flight jobs for the same PR/branch so there's only one active -# at a time -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - # Check Code style quickly by running `rustfmt` over all code - rustfmt: - name: Rustfmt - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - - run: rustup component add rustfmt - - run: cargo fmt --all -- --check - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # Lint dependency graph for security advisories, duplicate versions, and - # incompatible licences - cargo_deny: - name: Cargo deny - needs: determine - if: needs.determine.outputs.audit - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - - run: | - set -e - curl -L https://github.com/EmbarkStudios/cargo-deny/releases/download/0.8.5/cargo-deny-0.8.5-x86_64-unknown-linux-musl.tar.gz | tar xzf - - mv cargo-deny-*-x86_64-unknown-linux-musl/cargo-deny cargo-deny - echo `pwd` >> $GITHUB_PATH - - run: cargo deny check bans licenses - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # Ensure dependencies are vetted. See https://mozilla.github.io/cargo-vet/ - cargo_vet: - name: Cargo vet - needs: determine - if: needs.determine.outputs.audit - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - - uses: ./.github/actions/install-cargo-vet - - run: cargo vet --locked - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # This job is a dependency of many of the jobs below. This calculates what's - # actually being run for this workflow. For example: - # - # * Pushes to branches, which is currently both pushes to merge queue branches - # as well as release branches, perform full CI. - # * PRs to release branches (not `main`) run full CI. - # * PRs to `main` will only run a few smoke tests above plus some elements of - # the test matrix. The test matrix here is determined dynamically by the - # `./ci/build-test-matrix.js` script given the commits that happened and - # the files modified. - determine: - name: Determine CI jobs to run - runs-on: ubuntu-latest - outputs: - run-full: ${{ steps.calculate.outputs.run-full }} - test-matrix: ${{ steps.calculate.outputs.test-matrix }} - test-capi: ${{ steps.calculate.outputs.test-capi }} - build-fuzz: ${{ steps.calculate.outputs.build-fuzz }} - audit: ${{ steps.calculate.outputs.audit }} - preview1-adapter: ${{ steps.calculate.outputs.preview1-adapter }} - steps: - - uses: actions/checkout@v3 - - id: calculate - env: - GH_TOKEN: ${{ github.token }} - run: | - touch commits.log names.log - # Note that CI doesn't run on pushes to `main`, only pushes to merge - # queue branches and release branches, so this only runs full CI in - # those locations. - if [ "${{ github.event_name }}" != "pull_request" ]; then - run_full=true - else - pr=${{ github.event.number }} - gh pr view $pr --json commits | tee commits.log - gh pr diff $pr --name-only | tee names.log - if [ "${{ github.base_ref }}" != "main" ]; then - run_full=true - elif grep -q 'prtest:full' commits.log; then - run_full=true - fi - if grep -q crates.c-api names.log; then - echo test-capi=true >> $GITHUB_OUTPUT - fi - if grep -q fuzz names.log; then - echo build-fuzz=true >> $GITHUB_OUTPUT - fi - if grep -q Cargo.lock names.log; then - echo audit=true >> $GITHUB_OUTPUT - fi - if grep -q supply-chain names.log; then - echo audit=true >> $GITHUB_OUTPUT - fi - if grep -q component-adapter names.log; then - echo preview1-adapter=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 - echo "$matrix" - - if [ "$run_full" = "true" ]; then - echo run-full=true >> $GITHUB_OUTPUT - echo test-capi=true >> $GITHUB_OUTPUT - echo build-fuzz=true >> $GITHUB_OUTPUT - echo audit=true >> $GITHUB_OUTPUT - echo preview1-adapter=true >> $GITHUB_OUTPUT - fi - - # Build all documentation of Wasmtime, including the C API documentation, - # mdbook documentation, etc. This produces a `gh-pages` artifact which is what - # gets uploaded to the `gh-pages` branch later on. - doc: - needs: determine - if: needs.determine.outputs.run-full - name: Doc build - runs-on: ubuntu-latest - env: - CARGO_MDBOOK_VERSION: 0.4.21 - RUSTDOCFLAGS: -Dbroken_intra_doc_links --cfg nightlydoc - OPENVINO_SKIP_LINKING: 1 - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - with: - toolchain: nightly-2023-07-02 - - # Build C API documentation - - run: curl -L https://sourceforge.net/projects/doxygen/files/rel-1.9.3/doxygen-1.9.3.linux.bin.tar.gz/download | tar xzf - - - run: echo "`pwd`/doxygen-1.9.3/bin" >> $GITHUB_PATH - - run: cd crates/c-api && doxygen doxygen.conf - - # install mdbook, build the docs, and test the docs - - uses: actions/cache@v3 - with: - path: ${{ runner.tool_cache }}/mdbook - key: cargo-mdbook-bin-${{ env.CARGO_MDBOOK_VERSION }} - - run: | - echo "${{ runner.tool_cache }}/mdbook/bin" >> $GITHUB_PATH - cargo install --root ${{ runner.tool_cache }}/mdbook --version ${{ env.CARGO_MDBOOK_VERSION }} mdbook - - run: (cd docs && mdbook build) - - run: cargo build -p wasmtime-wasi --features wasmtime/wat,wasmtime/cranelift - - run: (cd docs/rust_wasi_markdown_parser && cargo build) - - run: (cd docs && mdbook test -L ../target/debug/deps) - - # Build Rust API documentation. - # We pass in the `component-model` feature - # to match the docs.rs metadata in - # crates/wasmtime/Cargo.toml. - - run: | - cargo doc --no-deps --workspace \ - --exclude wasmtime-cli \ - --exclude test-programs \ - --exclude wasi-http-tests \ - --exclude cranelift-codegen-meta \ - --features component-model - - run: cargo doc --package cranelift-codegen-meta --document-private-items - - # Assemble the documentation, and always upload it as an artifact for - # inspection on PRs and such. - - run: | - mv docs/book gh-pages - 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@v3 - with: - name: gh-pages - path: gh-pages.tar.gz - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # Checks of various feature combinations and whether things - # compile. The goal here isn't to run tests, mostly just serve as a - # double-check that Rust code compiles and is likely to work everywhere else. - checks: - needs: determine - if: needs.determine.outputs.run-full - name: Check - runs-on: ubuntu-latest - env: - CARGO_NDK_VERSION: 2.12.2 - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - - # Check some feature combinations of the `wasmtime` crate - - run: cargo check -p wasmtime --no-default-features - - run: cargo check -p wasmtime --no-default-features --features wat - - run: cargo check -p wasmtime --no-default-features --features jitdump - - run: cargo check -p wasmtime --no-default-features --features vtune - - run: cargo check -p wasmtime --no-default-features --features cache - - run: cargo check -p wasmtime --no-default-features --features async - - run: cargo check -p wasmtime --no-default-features --features pooling-allocator - - run: cargo check -p wasmtime --no-default-features --features cranelift - - run: cargo check -p wasmtime --no-default-features --features component-model - - run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache - - run: cargo check -p wasmtime --no-default-features --features winch - - run: cargo check -p wasmtime --no-default-features --features wmemcheck - - run: cargo check --features component-model - - run: cargo check -p wasmtime --features incremental-cache - - # Check that benchmarks of the cranelift project build - - run: cargo check --benches -p cranelift-codegen - - # Check some feature combinations of the `wasmtime-c-api` crate - - run: cargo check -p wasmtime-c-api --no-default-features - - run: cargo check -p wasmtime-c-api --no-default-features --features wat - - run: cargo check -p wasmtime-c-api --no-default-features --features wasi - - # Check a few builds of the cranelift backend - # - only x86 backend support, - # - only arm64 backend support, - # - no debug_assertions. - - run: cargo check --manifest-path=./cranelift/Cargo.toml --bin clif-util --no-default-features --features=cranelift-codegen/arm64 - - run: cargo check --manifest-path=./cranelift/Cargo.toml --bin clif-util --no-default-features --features=cranelift-codegen/x86 - - run: cargo check --manifest-path=./cranelift/Cargo.toml --bin clif-util - env: - CARGO_PROFILE_DEV_DEBUG_ASSERTIONS: false - - # Check whether `wasmtime` cross-compiles to x86_64-unknown-freebsd - # TODO: We aren't building with default features since the `ittapi` crate fails to compile on freebsd. - - run: rustup target add x86_64-unknown-freebsd - - run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache --target x86_64-unknown-freebsd - - # Check whether `wasmtime` cross-compiles to aarch64-linux-android - - run: rustup target add aarch64-linux-android - - name: Setup Android SDK - uses: android-actions/setup-android@v2 - - uses: actions/cache@v3 - with: - path: ${{ runner.tool_cache }}/cargo-ndk - key: cargo-ndk-bin-${{ env.CARGO_NDK_VERSION }} - - run: echo "${{ runner.tool_cache }}/cargo-ndk/bin" >> $GITHUB_PATH - - run: cargo install --root ${{ runner.tool_cache }}/cargo-ndk --version ${{ env.CARGO_NDK_VERSION }} cargo-ndk - - run: cargo ndk -t arm64-v8a check -p wasmtime - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # Check whether `wasmtime` cross-compiles to aarch64-pc-windows-msvc - # We don't build nor test it because it lacks trap handling. - # Tracking issue: https://github.com/bytecodealliance/wasmtime/issues/4992 - checks_winarm64: - needs: determine - if: needs.determine.outputs.run-full - name: Check Windows ARM64 - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - - run: rustup target add aarch64-pc-windows-msvc - - run: cargo check -p wasmtime --target aarch64-pc-windows-msvc - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # Verify all fuzz targets compile successfully - fuzz_targets: - needs: determine - if: needs.determine.outputs.build-fuzz - name: Fuzz Targets - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - # Note that building with fuzzers requires nightly since it uses unstable - # flags to rustc. - - uses: ./.github/actions/install-rust - with: - toolchain: nightly-2023-07-02 - - run: cargo install cargo-fuzz --vers "^0.11" - # Install the OCaml packages necessary for fuzz targets that use the - # `wasm-spec-interpreter`. - - run: sudo apt-get update && sudo apt install -y ocaml-nox ocamlbuild ocaml-findlib libzarith-ocaml-dev - - run: cargo fetch - working-directory: ./fuzz - - run: cargo fuzz build --dev -s none - # Check that the ISLE fuzz targets build too. - - run: cargo fuzz build --dev -s none --fuzz-dir ./cranelift/isle/fuzz - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # Perform all tests (debug mode) for `wasmtime`. - # - # Note that the full matrix for what may run here is defined within - # `./ci/build-test-matrix.js` and the execution of the `determine` step will - # calculate whether the tests are actually run as part of PRs and such. - test: - needs: determine - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - env: - QEMU_BUILD_VERSION: 8.1.1 - strategy: - fail-fast: true - matrix: ${{ fromJson(needs.determine.outputs.test-matrix) }} - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - with: - toolchain: ${{ matrix.rust }} - - # Install targets in order to build various tests throughout the repo - - run: rustup target add wasm32-wasi wasm32-unknown-unknown ${{ matrix.target }} - - run: echo CARGO_BUILD_TARGET=${{ matrix.target }} >> $GITHUB_ENV - if: matrix.target != '' - - # Fix an ICE for now in gcc when compiling zstd with debuginfo (??) - - run: echo CFLAGS=-g0 >> $GITHUB_ENV - if: matrix.target == 'x86_64-pc-windows-gnu' - - - run: cargo fetch --locked - - run: cargo fetch --locked --manifest-path crates/test-programs/wasi-tests/Cargo.toml - - run: cargo fetch --locked --manifest-path crates/test-programs/wasi-http-tests/Cargo.toml - - - uses: actions/cache@v3 - with: - path: ${{ runner.tool_cache }}/qemu - key: qemu-${{ matrix.target }}-${{ env.QEMU_BUILD_VERSION }}-patchcpuinfo - if: matrix.target != '' && matrix.os == 'ubuntu-latest' - - name: Install cross-compilation tools - run: | - set -ex - sudo apt-get update - sudo apt-get install -y ${{ matrix.gcc_package }} ninja-build - - # Configure Cargo for cross compilation and tell it how it can run - # cross executables - upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g') - echo CARGO_TARGET_${upcase}_RUNNER=${{ runner.tool_cache }}/qemu/bin/${{ matrix.qemu }} >> $GITHUB_ENV - echo CARGO_TARGET_${upcase}_LINKER=${{ matrix.gcc }} >> $GITHUB_ENV - - # QEMU emulation is not always the speediest, so total testing time - # goes down if we build the libs in release mode when running tests. - echo CARGO_PROFILE_DEV_OPT_LEVEL=2 >> $GITHUB_ENV - - # See comments in the source for why we enable this during QEMU - # emulation. - echo WASMTIME_TEST_NO_HOG_MEMORY=1 >> $GITHUB_ENV - - # See if qemu is already in the cache - if [ -f ${{ runner.tool_cache }}/qemu/built ]; then - exit 0 - fi - - # Download and build qemu from source since the most recent release is - # way faster at arm emulation than the current version github actions' - # ubuntu image uses. Disable as much as we can to get it to build - # quickly. - curl https://download.qemu.org/qemu-$QEMU_BUILD_VERSION.tar.xz | tar xJf - - cd qemu-$QEMU_BUILD_VERSION - ./configure --target-list=${{ matrix.qemu_target }} --prefix=${{ runner.tool_cache}}/qemu --disable-tools --disable-slirp --disable-fdt --disable-capstone --disable-docs - ninja -C build install - touch ${{ runner.tool_cache }}/qemu/built - if: matrix.gcc != '' - - # Build and test the C API with example C programs along with the example - # Rust programs. Note that this only executes if the `determine` step told - # us to test the capi which is off-by-default for PRs. - - run: cmake -Sexamples -Bexamples/build -DBUILD_SHARED_LIBS=OFF - if: matrix.target == '' && needs.determine.outputs.test-capi - - run: cmake --build examples/build --config Debug - if: matrix.target == '' && needs.determine.outputs.test-capi - - run: cmake -E env CTEST_OUTPUT_ON_FAILURE=1 cmake --build examples/build --config Debug --target RUN_TESTS - env: - RUST_BACKTRACE: 1 - if: matrix.target == '' && matrix.os == 'windows-latest' && needs.determine.outputs.test-capi - - run: cmake -E env CTEST_OUTPUT_ON_FAILURE=1 cmake --build examples/build --config Debug --target test - env: - RUST_BACKTRACE: 1 - if: matrix.target == '' && matrix.os != 'windows-latest' && needs.determine.outputs.test-capi - - # Ensure wit definitions are in sync: both wasmtime-wasi and wasmtime-wasi-http need their own - # copy of the wit definitions so publishing works, but we need to ensure they are identical copies. - - name: Check that the wasi and wasi-http wit directories agree - run: | - diff -ru crates/wasi/wit crates/wasi-http/wit - - # Build and test all features - - run: ./ci/run-tests.sh --locked - env: - RUST_BACKTRACE: 1 - - # Test debug (DWARF) related functionality. - - run: | - sudo apt-get update && sudo apt-get install -y gdb lldb llvm - cargo test test_debug_dwarf -- --ignored --test-threads 1 - if: matrix.os == 'ubuntu-latest' && matrix.target == ''&& needs.determine.outputs.run-full - env: - RUST_BACKTRACE: 1 - - # NB: the test job here is explicitly lacking in cancellation of this run if - # something goes wrong. These take the longest anyway and otherwise if - # Windows fails GitHub Actions will confusingly mark the failed Windows job - # as cancelled instead of failed. - - # Build and test the wasi-nn module. - test_wasi_nn: - needs: determine - # FIXME(#7125) flaky at the moment - if: needs.determine.outputs.run-full && false - name: Test wasi-nn module - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - - run: rustup target add wasm32-wasi - - uses: abrown/install-openvino-action@v6 - with: - version: 2022.3.0 - apt: true - - run: ./ci/run-wasi-nn-example.sh - env: - RUST_BACKTRACE: 1 - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - build-preview1-component-adapter: - name: Build wasi-preview1-component-adapter - needs: determine - if: needs.determine.outputs.preview1-adapter - runs-on: ubuntu-latest - permissions: - deployments: write - contents: write - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - run: rustup update stable && rustup default stable - - run: rustup target add wasm32-wasi wasm32-unknown-unknown - - - name: Install wasm-tools - run: | - curl -L https://github.com/bytecodealliance/wasm-tools/releases/download/wasm-tools-1.0.27/wasm-tools-1.0.27-x86_64-linux.tar.gz | tar xfz - - echo `pwd`/wasm-tools-1.0.27-x86_64-linux >> $GITHUB_PATH - - - run: ./ci/build-wasi-preview1-component-adapter.sh - env: - VERSION: ${{ github.sha }} - - - uses: actions/upload-artifact@v3 - with: - name: bins-wasi-preview1-component-adapter - path: target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.*.wasm - - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - - bench: - needs: determine - if: needs.determine.outputs.run-full - name: Run benchmarks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - - run: rustup target add wasm32-wasi - - run: cargo test --benches --release - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # Verify that cranelift's code generation is deterministic - meta_deterministic_check: - needs: determine - if: needs.determine.outputs.run-full - name: Meta deterministic check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - - run: cd cranelift/codegen && cargo build --features all-arch - - run: ci/ensure_deterministic_build.sh - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - verify-publish: - needs: determine - if: github.repository == 'bytecodealliance/wasmtime' && needs.determine.outputs.run-full - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - run: rustup update stable && rustup default stable - - run: | - cd ${{ runner.tool_cache }} - curl -L https://github.com/mozilla/sccache/releases/download/0.2.13/sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz | tar xzf - - echo "`pwd`/sccache-0.2.13-x86_64-unknown-linux-musl" >> $GITHUB_PATH - echo RUSTC_WRAPPER=sccache >> $GITHUB_ENV - - run: rustc scripts/publish.rs - # Make sure the tree is publish-able as-is - - run: ./publish verify - # Make sure we can bump version numbers for the next release - - run: ./publish bump - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # Run a subset of tests under MIRI on CI to help check the `unsafe` code in - # Wasmtime to make sure it's at least not obviously incorrect for basic usage. - # Note that this doesn't run the full test suite since MIRI can't actually run - # WebAssembly itself at this time (aka it doesn't support a JIT). There are a - # number of annotations throughout the code which gates some tests on MIRI not - # being run. - # - # Note that `cargo nextest` is used here additionally to get parallel test - # execution by default to help cut down on the time in CI. - miri: - needs: determine - if: needs.determine.outputs.run-full && github.repository == 'bytecodealliance/wasmtime' - name: Miri - runs-on: ubuntu-latest - env: - CARGO_NEXTEST_VERSION: 0.9.51 - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - with: - toolchain: nightly-2023-07-02 - - run: rustup component add rust-src miri - - uses: actions/cache@v3 - with: - path: ${{ runner.tool_cache }}/cargo-nextest - key: cargo-nextest-bin-${{ env.CARGO_NEXTEST_VERSION }} - - run: echo "${{ runner.tool_cache }}/cargo-nextest/bin" >> $GITHUB_PATH - - run: cargo install --root ${{ runner.tool_cache }}/cargo-nextest --version ${{ env.CARGO_NEXTEST_VERSION }} cargo-nextest - - run: | - cargo miri nextest run -j4 --no-fail-fast \ - -p wasmtime \ - -p wasmtime-cli \ - -p wasmtime-runtime \ - -p wasmtime-environ - env: - MIRIFLAGS: -Zmiri-strict-provenance - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # Perform release builds of `wasmtime` and `libwasmtime.so`. Builds a variety - # of platforms and architectures and then uploads the release artifacts to - # this workflow run's list of artifacts. - build: - needs: determine - if: needs.determine.outputs.run-full - name: Release build for ${{ matrix.build }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - include: - - build: x86_64-linux - os: ubuntu-latest - - build: x86_64-macos - os: macos-latest - - build: aarch64-macos - os: macos-latest - target: aarch64-apple-darwin - - build: x86_64-windows - os: windows-latest - - build: x86_64-mingw - os: windows-latest - target: x86_64-pc-windows-gnu - - build: aarch64-linux - os: ubuntu-latest - target: aarch64-unknown-linux-gnu - - build: s390x-linux - os: ubuntu-latest - target: s390x-unknown-linux-gnu - - build: riscv64gc-linux - os: ubuntu-latest - target: riscv64gc-unknown-linux-gnu - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ./.github/actions/install-rust - # On one builder produce the source tarball since there's no need to produce - # it everywhere - - run: ./ci/build-src-tarball.sh - if: matrix.build == 'x86_64-linux' - - uses: ./.github/actions/binary-compatible-builds - with: - name: ${{ matrix.build }} - - run: | - echo CARGO_BUILD_TARGET=${{ matrix.target }} >> $GITHUB_ENV - rustup target add ${{ matrix.target }} - if: matrix.target != '' - - # Build `wasmtime` and executables. Note that we include some non-default - # features so the # release artifacts can be maximally feature-ful. - - run: $CENTOS cargo build --release --bin wasmtime --features all-arch,component-model - - # Build `libwasmtime.so` - - run: $CENTOS cargo build --release --manifest-path crates/c-api/Cargo.toml - - # Assemble release artifats appropriate for this platform, then upload them - # 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@v3 - with: - name: bins-${{ matrix.build }} - path: dist - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - - # This is a "join node" which depends on all prior workflows. The merge queue, - # for example, gates on this to ensure that everything has executed - # successfully. - # - # Note that this is required currently for odd reasons with github. Notably - # the set of checks to enter the merge queue and leave the merge queue must - # be the same which means that the "build" step for example shows as skipped - # for PRs but expands to many different steps for merge-queue-based PRs. That - # means that for that step there's no single name to gate on, so it's required - # to have a "join" node here which joins everything. - # - # Note that this currently always runs to always report a status, even on - # cancellation and even if dependency steps fail. Each dependency tries to - # cancel the whole run if it fails, so if a test matrix entry fails, for - # example, it cancels the build matrix entries too. This step then tries to - # fail on cancellation to ensure that the dependency failures are propagated - # correctly. - ci-status: - name: Record the result of testing and building steps - runs-on: ubuntu-latest - needs: - - test - - build - - rustfmt - - cargo_deny - - cargo_vet - - doc - - checks - - checks_winarm64 - - fuzz_targets - - test_wasi_nn - - bench - - meta_deterministic_check - - verify-publish - - determine - - miri - - build-preview1-component-adapter - if: always() - steps: - - name: Dump needs context - env: - CONTEXT: ${{ toJson(needs) }} - run: | - echo -e "\033[33;1;4mDump context\033[0m" - echo -e "$CONTEXT\n" - - name: Successful test and build - if: ${{ !(contains(needs.*.result, 'failure')) }} - run: exit 0 - - name: Failing test and build - if: ${{ contains(needs.*.result, 'failure') }} - run: exit 1 - - name: Report failure on cancellation - if: ${{ contains(needs.*.result, 'cancelled') || cancelled() }} - run: exit 1 - - # The purpose of this jobs is to watch for changes on the `release-*` - # branches of this repository and look for the term - # "automatically-tag-and-release-this-commit" within merged PRs/commits. Once - # that term is found the current version of `Cargo.toml`, the `wasmtime-cli` - # Cargo.toml, is created as a tag and the tag is pushed to the repo. - # Currently the tag is created through the GitHub API with an access token to - # ensure that CI is further triggered for the tag itself which performs the - # full release process. - # - # Note that this depends on the `ci-status` step above which is the "join" - # point of this workflow for when everything succeeds. the purpose of that is - # so that the tag is only created after the aftifacts have been uploaded for - # this workflow as the `publish-artifacts.yml` workflow will download these - # artifacts and then publish them to the tag. - push-tag: - runs-on: ubuntu-latest - needs: ci-status - if: | - always() - && needs.ci-status.result == 'success' - && github.event_name == 'push' - && startsWith(github.ref, 'refs/heads/release-') - && github.repository == 'bytecodealliance/wasmtime' - steps: - - uses: actions/checkout@v3 - with: - submodules: true - fetch-depth: 0 - - name: Test if tag is needed - run: | - git log ${{ github.event.before }}...${{ github.event.after }} | tee main.log - version=$(grep '^version =' Cargo.toml | head -n 1 | sed 's/.*"\(.*\)"/\1/') - echo "version: $version" - echo "version=$version" >> $GITHUB_OUTPUT - echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - if grep -q "automatically-tag-and-release-this-commit" main.log; then - echo push-tag - echo "push_tag=yes" >> $GITHUB_OUTPUT - else - echo no-push-tag - echo "push_tag=no" >> $GITHUB_OUTPUT - fi - id: tag - - name: Push the tag - run: | - git_refs_url=$(jq .repository.git_refs_url $GITHUB_EVENT_PATH | tr -d '"' | sed 's/{\/sha}//g') - curl -iX POST $git_refs_url \ - -H "Authorization: token ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \ - -d @- << EOF - { - "ref": "refs/tags/v${{ steps.tag.outputs.version }}", - "sha": "${{ steps.tag.outputs.sha }}" - } - EOF - if: steps.tag.outputs.push_tag == 'yes' diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml deleted file mode 100644 index 0b39524a70f6..000000000000 --- a/.github/workflows/performance.yml +++ /dev/null @@ -1,133 +0,0 @@ -# This is a workflow triggered by PR or triggered manually -# Runs quick performance tests and reports the comparison against HEAD -# Test should take less than 10 minutes to run on current self-hosted devices -name: "Performance Testing" - -# Controls when the action will run. -# This workflow runs when manually triggered by keywords used in the start of a review comment -# Currently that phrase is /bench_x64. /bench_aarch64 and /bench_all are TODOs. -on: - issue_comment: - types: [created] - push: - -# Env variables -env: - SG_COMMIT: 2ab01ac - GITHUB_CONTEXT: ${{ toJson(github) }} - -jobs: - Wasmtime_Repo_On_PR_Comment: - name: Benchmark x64 on PR comment Wasmtime repo - runs-on: ubuntu-latest - if: | - (github.event_name == 'issue_comment') && - (github.event.issue.pull_request.url) && - (contains(github.event.comment.body, '/bench_x64')) && - (('abrown' == github.event.comment.user.login) - || ('afonso360' == github.event.comment.user.login) - || ('akirilov-arm' == github.event.comment.user.login) - || ('alexcrichton' == github.event.comment.user.login) - || ('bbouvier' == github.event.comment.user.login) - || ('bjorn3' == github.event.comment.user.login) - || ('cfallin' == github.event.comment.user.login) - || ('elliottt' == github.event.comment.user.login) - || ('fitzgen' == github.event.comment.user.login) - || ('jameysharp' == github.event.comment.user.login) - || ('jlb6740' == github.event.comment.user.login) - || ('sparker-arm' == github.event.comment.user.login) - || ('uweigand' == github.event.comment.user.login)) - steps: - - run: echo "$GITHUB_CONTEXT" - - run: | - # Create and Push Branch - git clone https://wasmtime-publish:${{secrets.PERSONAL_ACCESS_TOKEN}}@github.com/bytecodealliance/wasmtime-sightglass-benchmarking.git - cd wasmtime-sightglass-benchmarking - git remote add wasmtime ${{ github.event.repository.clone_url }} - git fetch wasmtime refs/pull/*/merge:refs/remotes/wasmtime/pull/*/merge - export issue_pr_url=${{ github.event.issue.pull_request.url }} - export issue_commits_url=${{ github.event.issue.comments_url }} - export issue_ref_name=$(curl -sSL $issue_pr_url | jq -r '.head.ref' | head -n 1) - export issue_number=$(curl -sSL $issue_pr_url | jq -r '.number' | head -n 1) - export issue_merge_commit_sha=$(curl -sSL $issue_pr_url | jq -r '.merge_commit_sha' | head -n 1) - git submodule update --init --recursive - git checkout wasmtime/pull/${issue_number}/merge -b pull/${issue_number}/merge/${issue_merge_commit_sha} - git config user.name $(curl -sSL $issue_commits_url | jq -r '.[].commit.committer.name' | tail -n 1) - git config user.email $(curl -sSL $issue_commits_url | jq -r '.[].commit.committer.email' | tail -n 1) - git log -n 1 - git commit --allow-empty -m "${issue_commits_url}" - git push origin --force pull/${issue_number}/merge/${issue_merge_commit_sha} - git log -n 1 - - Performance_Repo_On_Push: - name: Benchmark x64 on push Performance repo - runs-on: [self-hosted, linux, x64] - if: (github.event_name == 'push') && (github.repository == 'bytecodealliance/wasmtime-sightglass-benchmarking') - steps: - - run: echo "$GITHUB_CONTEXT" - - run: echo "${{ github.event.head_commit.message }}" - - name: "Build sightglass commit '${{ env.SG_COMMIT }}'" - run: | - cd ../ && ls -l && rm -rf ./sightglass - git clone https://github.com/bytecodealliance/sightglass.git && cd ./sightglass - git checkout ${{env.SG_COMMIT}} - cargo build --release - - - name: Checkout patch from bytecodealliance/wasmtime (pushed and triggering on this perf repo) - uses: actions/checkout@v3 - with: - submodules: true - path: wasmtime_commit - - - run: rustup update nightly && rustup default nightly - - - name: Build patch from bytecodealliance/wasmtime (pushed and triggering on this perf repo) - working-directory: ./wasmtime_commit - run: | - cargo --version - cargo build --release -p wasmtime-bench-api - cp target/release/libwasmtime_bench_api.so /tmp/wasmtime_commit.so - - - name: Checkout main from bytecodealliance/wasmtime - uses: actions/checkout@v3 - with: - ref: 'main' - repository: 'bytecodealliance/wasmtime' - submodules: true - path: wasmtime_main - - - name: Build main from bytecodealliance/wasmtime - working-directory: ./wasmtime_main - run: | - cargo build --release -p wasmtime-bench-api - cp target/release/libwasmtime_bench_api.so /tmp/wasmtime_main.so - - - name: Run performance tests - working-directory: ../sightglass - run: | - cargo run -- \ - benchmark \ - --processes 5 \ - --iterations-per-process 5 \ - --engine /tmp/wasmtime_main.so \ - --engine /tmp/wasmtime_commit.so \ - --output-file /tmp/results.txt - - - name: Print Results - run: cat /tmp/results.txt - - - id: get-comment-body - name: Create Results Body - run: | - body="$(cat /tmp/results.txt)" - body="${body//'%'/'%25'}" - body="${body//$'\n'/'%0A'}" - body="${body//$'\r'/'%0D'}" - echo "::set-output name=body::$body" - - - name: Publish Results - run: | - curl -X POST -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${{ secrets.WASMTIME_PUBLISHING_TOKEN }}" \ - ${{ github.event.head_commit.message }} \ - -d '{"body": ${{ toJSON(steps.get-comment-body.outputs.body) }}}' diff --git a/.github/workflows/publish-artifacts.yml b/.github/workflows/publish-artifacts.yml deleted file mode 100644 index e036474d24eb..000000000000 --- a/.github/workflows/publish-artifacts.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Publish Artifacts -on: - push: - branches: [main] - tags-ignore: [dev] - -permissions: - contents: write - -jobs: - publish: - name: Publish artifacts of build - runs-on: ubuntu-latest - if: github.repository == 'bytecodealliance/wasmtime' - steps: - - uses: actions/checkout@v3 - - run: | - sha=${{ github.sha }} - run_id=$( - gh api -H 'Accept: application/vnd.github+json' \ - /repos/${{ github.repository }}/actions/workflows/main.yml/runs\?exclude_pull_requests=true \ - | jq '.workflow_runs' \ - | jq "map(select(.head_commit.id == \"$sha\"))[0].id" \ - ) - gh run download $run_id - ls - find bins-* - env: - GH_TOKEN: ${{ github.token }} - - # Deploy the `gh-pages.tar.gz` artifact to the `gh-pages` branch. - - run: tar xf gh-pages.tar.gz - working-directory: gh-pages - - name: Deploy to gh-pages - uses: JamesIves/github-pages-deploy-action@v4 - with: - folder: ./gh-pages/gh-pages - single-commit: true - clean: true - if: github.ref == 'refs/heads/main' - - - run: npm install --production - working-directory: .github/actions/github-release - - run: | - mkdir dist - mv -t dist bins-*/*.tar.* - mv -t dist bins-*/*.{zip,msi,wasm} - - name: Publish Release - uses: ./.github/actions/github-release - with: - files: "dist/*" - token: ${{ github.token }} - continue-on-error: true diff --git a/.github/workflows/publish-to-cratesio.yml b/.github/workflows/publish-to-cratesio.yml deleted file mode 100644 index 6d05b66778a4..000000000000 --- a/.github/workflows/publish-to-cratesio.yml +++ /dev/null @@ -1,25 +0,0 @@ -# The purpose of this workflow is to publish the wasmtime workspace of crates -# whenever a wasmtime tag is created. This baiscally boils down to running -# `scripts/publish.rs` at the right time. - -name: "Publish to crates.io" - -on: - push: - tags: - - 'v*' - -jobs: - publish: - if: github.repository == 'bytecodealliance/wasmtime' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - run: rustup update stable && rustup default stable - - run: | - rustc scripts/publish.rs - ./publish publish - env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/release-process.yml b/.github/workflows/release-process.yml deleted file mode 100644 index e2003a456d92..000000000000 --- a/.github/workflows/release-process.yml +++ /dev/null @@ -1,252 +0,0 @@ -# The purpose of this workflow is to orchestrate Wasmtime's release process as -# much as possible. This specific workflow is responsible for a few timed parts -# of the process: -# -# * On the 5th of every month a new release branch is automatically created and -# the version number of the `main` branch is increased -# * On the 20th of every month the previous release branch is published. -# -# This automation is all done through PRs except for the creation of the release -# branch itself which is an write-action performed by this script. Otherwise -# humans are ideally reviewing and rubber-stamping the output of the script all -# other steps of the way. -# -# Note that this script also helps manage patch releases by sending a PR to the -# release branch with a bumped version number for all crates with a patch-bump. - -name: "Automated Release Process" -on: - schedule: - # “At 00:00 on day-of-month 5.” - # - # https://crontab.guru/#0_0_5_*_* - - cron: '0 0 5 * *' - - cron: '0 0 20 * *' - - # Allow manually triggering this request via the button at - # https://github.com/bytecodealliance/wasmtime/actions/workflows/release-process.yml - workflow_dispatch: - inputs: - action: - description: 'Publish script argument: "cut", "release-latest", or "release-patch"' - required: false - default: 'cut' - -jobs: - release_process: - if: "github.repository == 'bytecodealliance/wasmtime' || !github.event.schedule" - name: Run the release process - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - name: Setup - run: | - rustc scripts/publish.rs - git config user.name 'Wasmtime Publish' - git config user.email 'wasmtime-publish@users.noreply.github.com' - git remote set-url origin https://git:${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/${{ github.repository }} - - - uses: ./.github/actions/install-rust - - uses: ./.github/actions/install-cargo-vet - - - name: Bump major version number - run: | - set -ex - # Push the current contents of `main` to a new release branch - cur=$(./ci/print-current-version.sh) - git push origin HEAD:release-$cur - - # Update version numbers and make a commit indicating that. Note that - # on merge this will not trigger a publish. - ./publish bump - num=$(./ci/print-current-version.sh) - - # Add a new section to the release notes for the new version. - cp RELEASES.md backup-releases - sed "s/VERSION/$num/" ci/RELEASES-template.md > RELEASES.md - cat backup-releases >> RELEASES.md - rm backup-releases - - # Update `cargo vet` entries for all the new crate versions - cargo vet - - # Commit all of the above changes. - git commit -am "Bump Wasmtime to $num" - - # Push the result to a branch and setup metadata for the step below - # that creates a PR - git push origin HEAD:ci/bump-to-$num - echo "PR_HEAD=ci/bump-to-$num" >> $GITHUB_ENV - echo "PR_TITLE=Bump Wasmtime to $num" >> $GITHUB_ENV - echo "PR_BASE=main" >> $GITHUB_ENV - cat > pr-body <<-EOF - This is an [automated pull request][process] from CI which indicates - that the next [\`release-$cur\` branch][branch] has been created and - the \`main\` branch is getting its version number bumped from $cur to - $num. - - Maintainers should take a moment to review the [release - notes][RELEASES.md] for $cur, and if any changes are necessary send - PRs to the \`main\` branch to update [RELEASES.md] and then backport - these PRs to the [release branch][branch]. - - Maintainers should also review that aarch64-apple-darwin builds - are passing via [embark's CI](https://buildkite.com/embark-studios/wasmtime-aarch64-apple-darwin). - - Another automated PR will be made in roughly 2 weeks time when for - the actual release itself. - - If any issues arise on the \`main\` branch before the release is made - then the issue should first be fixed on \`main\` and then backported - to the \`release-$cur\` branch. - - [RELEASES.md]: https://github.com/${{ github.repository }}/blob/main/RELEASES.md - [branch]: https://github.com/${{ github.repository }}/tree/release-$cur - [process]: https://docs.wasmtime.dev/contributing-release-process.html - EOF - if: >- - github.event.schedule == '0 0 5 * *' || - github.event.inputs.action == 'cut' - - - name: Perform latest release - run: | - set -ex - git fetch origin - - # Determine the latest release branch - rustc ci/find-latest-release.rs -o /tmp/find-latest-release - cur=`/tmp/find-latest-release` - - # Update the release date of $cur in RELEASES.md - rustc ci/update-release-date.rs -o /tmp/update-release-date - /tmp/update-release-date $(date +'%Y-%m-%d') - - git commit --allow-empty -a -F-< pr-body <<-EOF - This is an [automated pull request][process] from CI which is updating - the release date of Wasmtime $cur to today. This PR's base branch - is \`main\` and a second PR will be coming to perform the actual - release which will be targeted at \`release-$cur\`. - - [process]: https://docs.wasmtime.dev/contributing-release-process.html - EOF - body=$(jq -sR < ./pr-body) - curl --include --request POST \ - https://api.github.com/repos/${{ github.repository }}/pulls \ - --header "Authorization: token ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \ - --data @- << EOF - { - "head": "ci/release-date-for-$cur", - "base": "main", - "title": "Update release date of Wasmtime $cur", - "body": $body, - "maintainer_can_modify": true - } - EOF - - # Move to the most recent release branch, update the release date and - # commit it, indicating that the commit is what will get tagged and - # released - git reset --hard origin/release-$cur - git submodule update --init --recursive - sed -i "s/^Unreleased/Released $(date +'%Y-%m-%d')/" RELEASES.md - git commit --allow-empty -a -F-<> $GITHUB_ENV - echo "PR_TITLE=Release Wasmtime $cur" >> $GITHUB_ENV - echo "PR_BASE=release-$cur" >> $GITHUB_ENV - cat > pr-body <<-EOF - This is an [automated pull request][process] from CI which is - intended to notify maintainers that it's time to release Wasmtime - $cur. The [release branch][branch] was created roughly two weeks ago - and it's now time for it to be published and released. - - It's recommended that maintainers double-check that [RELEASES.md] - is up-to-date and that there are no known issues before merging this - PR. When this PR is merged a release tag will automatically be - created, crates will be published, and CI artifacts will be produced. - - [RELEASES.md]: https://github.com/${{ github.repository }}/blob/main/RELEASES.md - [process]: https://docs.wasmtime.dev/contributing-release-process.html - [branch]: https://github.com/${{ github.repository }}/tree/release-$cur - EOF - if: >- - github.event.schedule == '0 0 20 * *' || - github.event.inputs.action == 'release-latest' - - - name: Bump and release patch version number - run: | - set -ex - # Update version numbers on a patch basis and update RELEASES.md if a - # patch release marker is already in there. Note that this commit - # message indicates that on-merge a release will be made. - ./publish bump-patch - # Update `cargo vet` entries for all the new crate versions - cargo vet - sed -i "s/^Unreleased/Released $(date +'%Y-%m-%d')/" RELEASES.md - num=$(./ci/print-current-version.sh) - git commit -a -F-<> $GITHUB_ENV - echo "PR_TITLE=Release Wasmtime $num" >> $GITHUB_ENV - echo "PR_BASE=${{ github.ref_name }}" >> $GITHUB_ENV - cat > pr-body <<-EOF - This is an [automated pull request][process] from CI to create a patch - release for Wasmtime $num, requested by @${{ github.actor }}. - - It's recommended that maintainers double-check that [RELEASES.md] - is up-to-date and that there are no known issues before merging this - PR. When this PR is merged a release tag will automatically be - created, crates will be published, and CI artifacts will be produced. - - [RELEASES.md]: https://github.com/${{ github.repository }}/blob/main/RELEASES.md - [process]: https://docs.wasmtime.dev/contributing-release-process.html - EOF - - if: github.event.inputs.action == 'release-patch' - - - name: Make a PR - # Note that the syntax here is kinda funky, and the general gist is that - # I couldn't figure out a good way to have a multiline string-literal - # become a json-encoded string literal to send to GitHub. This - # represents my best attempt. - run: | - set -ex - body=$(jq -sR < ./pr-body) - - curl --include --request POST \ - https://api.github.com/repos/${{ github.repository }}/pulls \ - --header "Authorization: token ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \ - --data @- << EOF - { - "head": "$PR_HEAD", - "base": "$PR_BASE", - "title": "$PR_TITLE", - "body": $body, - "maintainer_can_modify": true - - } - EOF diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml deleted file mode 100644 index c7bde9931c42..000000000000 --- a/.github/workflows/triage.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: "Issue / PR Triage" - -on: - issues: - types: ["labeled"] - schedule: - # Run pull request triage once an hour. Ideally, this would be on - # "labeled" types of pull request events, but that doesn't work if the pull - # request is from another fork. For example, see - # https://github.com/actions/labeler/issues/12 - - cron: '42 * * * *' - -concurrency: - group: issue-triage - cancel-in-progress: true - -jobs: - triage: - if: github.repository == 'bytecodealliance/wasmtime' - runs-on: ubuntu-latest - steps: - - # Automatically label PRs that touch certain directories with certain - # labels. - - uses: bytecodealliance/labeler@schedule-fork - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" - - # @-mention people who are subscribed to a label when an issue/PR is given - # that label. - - uses: bytecodealliance/subscribe-to-label-action@v1 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" - - # Leave pre-determined comments on issues/PRs that are given a certain label. - - uses: bytecodealliance/label-messager-action@v1 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.gitignore b/.gitignore index 7790b83f8725..c28c8df7f777 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ vendor examples/build examples/.cache *.coredump +crates/explorer/node_modules diff --git a/.gitmodules b/.gitmodules index 2f6c628a2f24..e1fa8b72c643 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,15 +1,6 @@ [submodule "tests/spec_testsuite"] path = tests/spec_testsuite url = https://github.com/WebAssembly/testsuite -[submodule "crates/c-api/wasm-c-api"] - path = crates/c-api/wasm-c-api - url = https://github.com/WebAssembly/wasm-c-api -[submodule "crates/wasi-common/WASI"] - path = crates/wasi-common/WASI - url = https://github.com/WebAssembly/WASI -[submodule "crates/wasi-nn/spec"] - path = crates/wasi-nn/spec - url = https://github.com/WebAssembly/wasi-nn [submodule "tests/wasi_testsuite/wasi-threads"] path = tests/wasi_testsuite/wasi-threads url = https://github.com/WebAssembly/wasi-threads diff --git a/ADOPTERS.md b/ADOPTERS.md index 581828946d9d..e211ab5378d6 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -14,5 +14,6 @@ Wasmtime is used in many different production use-cases. This list has grown sig | [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. | | [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. | | [Microsoft](https://microsoft.com/) | [@devigned](https://gist.github.com/devigned) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Microsoft has had Wasmtime in preview for its WebAssembly System Interface (WASI) node pools in Azure Kubernetes Service since October 2021. | +| [Redpanda](https://redpanda.com/) | [@rockwotj](https://github.com/rockwotj) | ![beta](https://img.shields.io/badge/-production-blue?style=flat) | Redpanda Data Transforms allow developers to transform data directly in the message broker. | | [Shopify](https://www.shopify.com/) | [@saulecabrera](https://github.com/saulecabrera) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Shopify Functions allow developers to customize the backend logic of Shopify. | -| [SingleStore](https://www.singlestore.com/) | [@Kylebrown9](https://github.com/Kylebrown9) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | SingleStoreDB Cloud embeds Wasmtime to bring developers' code to the data, safely and with speed and scale. | +| [SingleStore](https://www.singlestore.com/) | [@pvetere](https://github.com/pvetere) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | SingleStoreDB Cloud embeds Wasmtime to bring developers' code to the data, safely and with speed and scale. | diff --git a/CODEOWNERS b/CODEOWNERS index 61d8a372db8d..b4bbf31eff2f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -39,4 +39,5 @@ /winch/ @bytecodealliance/wasmtime-compiler-reviewers # Fuzz testing -/fuzz/ @bytecodealliance/wasmtime-fuzz-reviewers +/fuzz/ @bytecodealliance/wasmtime-fuzz-reviewers +/crates/fuzzing @bytecodealliance/wasmtime-fuzz-reviewers diff --git a/Cargo.lock b/Cargo.lock index ebde4e29ce0a..33fbe023c07b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -19,13 +19,14 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.2" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -43,6 +44,15 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anes" version = "0.1.6" @@ -85,7 +95,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -95,7 +105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -103,16 +113,25 @@ name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +dependencies = [ + "backtrace", +] [[package]] name = "arbitrary" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +checksum = "a2e1373abdaa212b704512ec2bd8b26bd0b7d5c3f70117411a5d9a451383c859" dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "async-trait" version = "0.1.71" @@ -121,18 +140,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", + "syn 2.0.60", ] [[package]] @@ -143,9 +151,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -163,13 +171,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] -name = "bincode" -version = "1.3.3" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bit-set" @@ -194,9 +199,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "block-buffer" @@ -220,13 +225,13 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byte-array-literals" -version = "14.0.0" +version = "25.0.0" [[package]] name = "byteorder" @@ -236,9 +241,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "bytesize" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" [[package]] name = "camino" @@ -251,50 +262,50 @@ dependencies = [ [[package]] name = "cap-fs-ext" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b779b2d0a001c125b4584ad586268fb4b92d957bff8d26d7fe0dd78283faa814" +checksum = "769f8cd02eb04d57f14e2e371ebb533f96817f9b2525d73a5c72b61ca7973747" dependencies = [ "cap-primitives", "cap-std", - "io-lifetimes 2.0.2", - "windows-sys", + "io-lifetimes 2.0.3", + "windows-sys 0.52.0", ] [[package]] name = "cap-net-ext" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffc30dee200c20b4dcb80572226f42658e1d9c4b668656d7cc59c33d50e396e" +checksum = "59ff6d3fb274292a9af283417e383afe6ded1fe66f6472d2c781216d3d80c218" dependencies = [ "cap-primitives", "cap-std", - "rustix 0.38.8", + "rustix 0.38.31", "smallvec", ] [[package]] name = "cap-primitives" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf30c373a3bee22c292b1b6a7a26736a38376840f1af3d2d806455edf8c3899" +checksum = "90a0b44fc796b1a84535a63753d50ba3972c4db55c7255c186f79140e63d56d0" dependencies = [ "ambient-authority", "fs-set-times", "io-extras", - "io-lifetimes 2.0.2", + "io-lifetimes 2.0.3", "ipnet", "maybe-owned", - "rustix 0.38.8", - "windows-sys", + "rustix 0.38.31", + "windows-sys 0.52.0", "winx", ] [[package]] name = "cap-rand" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "577de6cff7c2a47d6b13efe5dd28bf116bd7f8f7db164ea95b7cc2640711f522" +checksum = "4327f08daac33a99bb03c54ae18c8f32c3ba31c728a33ddf683c6c6a5043de68" dependencies = [ "ambient-authority", "rand", @@ -302,45 +313,35 @@ dependencies = [ [[package]] name = "cap-std" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84bade423fa6403efeebeafe568fdb230e8c590a275fba2ba978dd112efcf6e9" +checksum = "266626ce180cf9709f317d0bf9754e3a5006359d87f4bf792f06c9c5f1b63c0f" dependencies = [ "cap-primitives", "io-extras", - "io-lifetimes 2.0.2", - "rustix 0.38.8", -] - -[[package]] -name = "cap-tempfile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b9e3348a3510c4619b4c7a7bcdef09a71221da18f266bda3ed6b9aea2c509e2" -dependencies = [ - "cap-std", - "rand", - "rustix 0.38.8", - "uuid", + "io-lifetimes 2.0.3", + "rustix 0.38.31", ] [[package]] name = "cap-time-ext" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f52b3c8f4abfe3252fd0a071f3004aaa3b18936ec97bdbd8763ce03aff6247" +checksum = "e1353421ba83c19da60726e35db0a89abef984b3be183ff6f58c5b8084fcd0c5" dependencies = [ + "ambient-authority", "cap-primitives", + "iana-time-zone", "once_cell", - "rustix 0.38.8", + "rustix 0.38.31", "winx", ] [[package]] name = "capstone" -version = "0.9.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "178b3c80b4ce3939792d1dd6600575e012da806a10e81abe812dc29cbfe629ec" +checksum = "b08ca438d9585a2b216b0c2e88ea51e096286c5f197f7be2526bb515ef775b6c" dependencies = [ "capstone-sys", "libc", @@ -348,9 +349,9 @@ dependencies = [ [[package]] name = "capstone-sys" -version = "0.13.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2f3d97b88fa4a9a6464c7c08b8c4588aaea8c18d0651eca11f2ca15f50f1f6" +checksum = "fe7183271711ffb7c63a6480e4baf480e0140da59eeba9b18fcc8bf3478950e3" dependencies = [ "cc", "libc", @@ -367,9 +368,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.3" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", @@ -387,11 +388,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.73" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -448,6 +450,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", + "terminal_size 0.2.6", ] [[package]] @@ -456,10 +459,10 @@ version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ - "heck", + "heck 0.4.0", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", ] [[package]] @@ -468,6 +471,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -484,15 +493,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "command-tests" -version = "0.0.0" -dependencies = [ - "anyhow", - "getrandom", - "wit-bindgen", -] - [[package]] name = "component-fuzz-util" version = "0.0.0" @@ -510,7 +510,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", ] [[package]] @@ -527,7 +527,7 @@ version = "0.0.0" dependencies = [ "anyhow", "arbitrary", - "env_logger 0.10.0", + "env_logger", "wasmtime", ] @@ -541,16 +541,32 @@ dependencies = [ "libc", "once_cell", "regex", - "terminal_size", + "terminal_size 0.1.17", "unicode-width", "winapi", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpp_demangle" -version = "0.3.5" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" dependencies = [ "cfg-if", ] @@ -566,38 +582,56 @@ dependencies = [ [[package]] name = "cranelift" -version = "0.101.0" +version = "0.112.0" dependencies = [ "cranelift-codegen", "cranelift-frontend", + "cranelift-interpreter", + "cranelift-jit", + "cranelift-module", + "cranelift-native", + "cranelift-object", ] [[package]] name = "cranelift-bforest" -version = "0.101.0" +version = "0.112.0" dependencies = [ "cranelift-entity", ] +[[package]] +name = "cranelift-bitset" +version = "0.112.0" +dependencies = [ + "arbitrary", + "serde", + "serde_derive", +] + [[package]] name = "cranelift-codegen" -version = "0.101.0" +version = "0.112.0" dependencies = [ "anyhow", - "bincode", "bumpalo", "capstone", "cranelift-bforest", + "cranelift-bitset", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-control", "cranelift-entity", "cranelift-isle", "criterion", + "env_logger", "gimli", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "log", + "postcard", + "pulley-interpreter", "regalloc2", + "rustc-hash", "serde", "serde_derive", "sha2", @@ -609,26 +643,27 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.101.0" +version = "0.112.0" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.101.0" +version = "0.112.0" [[package]] name = "cranelift-control" -version = "0.101.0" +version = "0.112.0" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.101.0" +version = "0.112.0" dependencies = [ + "cranelift-bitset", "serde", "serde_derive", ] @@ -647,28 +682,29 @@ dependencies = [ "cranelift-module", "cranelift-native", "cranelift-reader", - "cranelift-wasm", "file-per-thread-logger", "filecheck", "gimli", "log", "num_cpus", + "pulley-interpreter", "serde", "serde_derive", "similar", + "smallvec", "target-lexicon", "thiserror", "toml", - "wasmparser", "wat", ] [[package]] name = "cranelift-frontend" -version = "0.101.0" +version = "0.112.0" dependencies = [ "cranelift-codegen", - "hashbrown 0.14.0", + "env_logger", + "hashbrown 0.14.3", "log", "similar", "smallvec", @@ -689,7 +725,7 @@ dependencies = [ [[package]] name = "cranelift-interpreter" -version = "0.101.0" +version = "0.112.0" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -703,16 +739,85 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.101.0" +version = "0.112.0" dependencies = [ "codespan-reporting", "log", + "pretty", + "tempfile", +] + +[[package]] +name = "cranelift-isle-veri" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "cranelift-codegen-meta", + "cranelift-isle", + "cranelift-isle-veri-test-macros", + "easy-smt", + "env_logger", + "log", + "num-bigint", + "num-traits", + "rayon", + "serde", + "serde_json", "tempfile", ] +[[package]] +name = "cranelift-isle-veri-aslp" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "cranelift-isle-veri-test-macros", + "enquote", + "pest", + "pest_derive", + "reqwest", + "serde", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "cranelift-isle-veri-isaspec" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "cranelift-codegen", + "cranelift-isle", + "cranelift-isle-veri-aslp", + "itertools 0.12.1", + "reqwest", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "cranelift-isle-veri-meta" +version = "0.1.0" +dependencies = [ + "wasmparser 0.216.0", +] + +[[package]] +name = "cranelift-isle-veri-test-macros" +version = "0.1.0" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "cranelift-jit" -version = "0.101.0" +version = "0.112.0" dependencies = [ "anyhow", "cranelift", @@ -728,24 +833,24 @@ dependencies = [ "region", "target-lexicon", "wasmtime-jit-icache-coherence", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "cranelift-module" -version = "0.101.0" +version = "0.112.0" dependencies = [ "anyhow", "cranelift-codegen", "cranelift-control", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "serde", "serde_derive", ] [[package]] name = "cranelift-native" -version = "0.101.0" +version = "0.112.0" dependencies = [ "cranelift-codegen", "libc", @@ -754,7 +859,7 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.101.0" +version = "0.112.0" dependencies = [ "anyhow", "cranelift-codegen", @@ -769,7 +874,7 @@ dependencies = [ [[package]] name = "cranelift-reader" -version = "0.101.0" +version = "0.112.0" dependencies = [ "anyhow", "cranelift-codegen", @@ -779,7 +884,7 @@ dependencies = [ [[package]] name = "cranelift-serde" -version = "0.101.0" +version = "0.112.0" dependencies = [ "clap", "cranelift-codegen", @@ -806,39 +911,36 @@ dependencies = [ "cranelift-native", "cranelift-object", "cranelift-reader", - "cranelift-wasm", "filecheck", - "fxhash", "indicatif", "log", - "pretty_env_logger 0.5.0", + "pretty_env_logger", "rayon", "regalloc2", + "rustc-hash", "serde", "similar", "target-lexicon", - "termcolor", "thiserror", "toml", "walkdir", - "wat", ] [[package]] name = "cranelift-wasm" -version = "0.101.0" +version = "0.112.0" dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "hashbrown 0.14.0", - "itertools", + "hashbrown 0.14.3", + "itertools 0.12.1", "log", "serde", "serde_derive", "smallvec", "target-lexicon", - "wasmparser", + "wasmparser 0.216.0", "wasmtime-types", "wat", ] @@ -864,11 +966,10 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "oorandom", - "plotters", "rayon", "regex", "serde", @@ -885,17 +986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", + "itertools 0.10.5", ] [[package]] @@ -965,7 +1056,7 @@ checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", ] [[package]] @@ -1009,12 +1100,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "dlmalloc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "203540e710bfadb90e5e29930baf5d10270cec1f43ab34f46f78b147b2de715a" +dependencies = [ + "libc", +] + [[package]] name = "downcast-rs" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "easy-smt" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47cdc2566d8faac2fc64d33fdce1441600f59dcaa45f59ea846c8e5b04191623" +dependencies = [ + "log", + "unicode-segmentation", +] + [[package]] name = "egg" version = "0.6.0" @@ -1035,6 +1145,21 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedding" +version = "25.0.0" +dependencies = [ + "anyhow", + "dlmalloc", + "wasmtime", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -1051,16 +1176,12 @@ dependencies = [ ] [[package]] -name = "env_logger" -version = "0.7.1" +name = "enquote" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "06c36cb11dbde389f4096111698d8b567c0720e3452fd5ac3e6b4e47e1939932" dependencies = [ - "atty", - "humantime 1.3.0", - "log", - "regex", - "termcolor", + "thiserror", ] [[package]] @@ -1069,7 +1190,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "humantime 2.1.0", + "humantime", "is-terminal", "log", "regex", @@ -1084,23 +1205,28 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "escape8259" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "ba4f4911e3666fcd7826997b4745c8224295a6f3072f1418c3067b97a67557ee" dependencies = [ - "cc", - "libc", + "rustversion", +] + +[[package]] +name = "example-component-wasm" +version = "0.0.0" +dependencies = [ + "wit-bindgen", ] [[package]] @@ -1123,22 +1249,19 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fd-lock" -version = "4.0.0" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0377f1edc77dbd1118507bc7a66e4ab64d2b90c66f90726dc801e73a8c68f9" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" dependencies = [ "cfg-if", - "rustix 0.38.8", - "windows-sys", + "rustix 0.38.31", + "windows-sys 0.52.0", ] [[package]] @@ -1147,7 +1270,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a3cc21c33af89af0930c8cae4ade5e6fdc17b5d2c97b3d2e2edb67a1cf683f3" dependencies = [ - "env_logger 0.10.0", + "env_logger", "log", ] @@ -1179,12 +1302,37 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499" +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.1.0" @@ -1196,20 +1344,20 @@ dependencies = [ [[package]] name = "fs-set-times" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd738b84894214045e8414eaded76359b4a5773f0a0a56b16575110739cdcf39" +checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" dependencies = [ - "io-lifetimes 2.0.2", - "rustix 0.38.8", - "windows-sys", + "io-lifetimes 2.0.3", + "rustix 0.38.31", + "windows-sys 0.52.0", ] [[package]] name = "fslock" -version = "0.1.8" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57eafdd0c16f57161105ae1b98a1238f97645f2f588438b2949c99a2af9616bf" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" dependencies = [ "libc", "winapi", @@ -1270,10 +1418,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" dependencies = [ "futures-core", + "futures-io", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -1291,7 +1442,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "debugid", "fxhash", "serde", @@ -1321,34 +1472,47 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" dependencies = [ "fallible-iterator", - "indexmap 2.0.0", + "indexmap 2.2.6", "stable_deref_trait", ] [[package]] -name = "glob" -version = "0.3.0" +name = "h2" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] [[package]] name = "h2" -version = "0.3.19" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 1.9.1", + "http 1.0.0", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1369,20 +1533,12 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", + "serde", ] [[package]] @@ -1390,39 +1546,44 @@ name = "heck" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" -dependencies = [ - "unicode-segmentation", -] [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", +checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", ] [[package]] -name = "hermit-abi" -version = "0.3.0" +name = "http" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] [[package]] name = "http" -version = "0.2.9" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" dependencies = [ "bytes", "fnv", @@ -1431,24 +1592,35 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0-rc.2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951dfc2e32ac02d67c90c0d65bd27009a635dc9b381a2cc7d284ab01e3a0150d" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.0.0", ] [[package]] name = "http-body-util" -version = "0.1.0-rc.2" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92445bc9cc14bfa0a3ce56817dc3b5bcc227a168781a356b702410789cec0d10" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -1464,15 +1636,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error 1.2.3", -] - [[package]] name = "humantime" version = "2.1.0" @@ -1481,26 +1644,84 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.0.0-rc.3" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75264b2003a3913f118d35c586e535293b3e22e41f074930762929d071e092" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", + "socket2", "tokio", + "tower-service", "tracing", "want", ] +[[package]] +name = "hyper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f9214f3e703236b221f1a9cd88ec8b4adfa5296de01ab96216361f4692f56" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.4", + "http 1.0.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.30", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "id-arena" version = "2.2.1" @@ -1529,12 +1750,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "serde", ] @@ -1567,30 +1788,30 @@ dependencies = [ [[package]] name = "io-extras" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d3c230ee517ee76b1cc593b52939ff68deda3fae9e41eca426c6b4993df51c4" +checksum = "c301e73fb90e8a29e600a9f402d095765f74310d582916a952f618836a1bd1ed" dependencies = [ - "io-lifetimes 2.0.2", - "windows-sys", + "io-lifetimes 2.0.3", + "windows-sys 0.52.0", ] [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.0", + "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "io-lifetimes" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb4def18c48926ccac55c1223e02865ce1a821751a95920448662696e7472c" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" [[package]] name = "ipnet" @@ -1600,14 +1821,13 @@ checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ - "hermit-abi 0.3.0", - "io-lifetimes 1.0.10", - "rustix 0.37.13", - "windows-sys", + "hermit-abi", + "rustix 0.38.31", + "windows-sys 0.52.0", ] [[package]] @@ -1615,7 +1835,7 @@ name = "isle-fuzz" version = "0.0.0" dependencies = [ "cranelift-isle", - "env_logger 0.10.0", + "env_logger", "libfuzzer-sys", "log", ] @@ -1626,14 +1846,23 @@ version = "0.0.0" dependencies = [ "clap", "cranelift-isle", - "env_logger 0.10.0", + "env_logger", ] [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -1646,9 +1875,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "ittapi" -version = "0.3.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e0d0b7b3b53d92a7e8b80ede3400112a6b8b4c98d1f5b8b16bb787c780582c" +checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" dependencies = [ "anyhow", "ittapi-sys", @@ -1657,18 +1886,18 @@ dependencies = [ [[package]] name = "ittapi-sys" -version = "0.3.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f8763c96e54e6d6a0dccc2990d8b5e33e3313aaeae6185921a3f4c1614a77c" +checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" dependencies = [ "cc", ] [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -1696,9 +1925,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libfuzzer-sys" @@ -1713,12 +1942,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.7.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1727,17 +1956,29 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +[[package]] +name = "libtest-mimic" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f0f4c6f44ecfd52e8b443f2ad18f2b996540135771561283c2352ce56a1c70b" +dependencies = [ + "clap", + "escape8259", + "termcolor", + "threadpool", +] + [[package]] name = "linux-raw-sys" -version = "0.3.3" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b085a4f2cde5781fc4b1717f2e86c62f5cda49de7ba99a7c2eae02b61c9064c" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "listenfd" @@ -1768,6 +2009,15 @@ dependencies = [ "libc", ] +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "matchers" version = "0.1.0" @@ -1791,11 +2041,11 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memfd" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.37.13", + "rustix 0.38.31", ] [[package]] @@ -1816,6 +2066,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "min-platform-host" +version = "25.0.0" +dependencies = [ + "anyhow", + "libloading", + "object", + "wasmtime", +] + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1827,31 +2093,77 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -1863,13 +2175,13 @@ checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" [[package]] name = "object" -version = "0.32.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "crc32fast", - "hashbrown 0.14.0", - "indexmap 2.0.0", + "hashbrown 0.14.3", + "indexmap 2.2.6", "memchr", ] @@ -1904,9 +2216,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -1914,11 +2226,55 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "openvino" -version = "0.5.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc731d9a7805dd533b69de3ee33062d5ea1dfa9fca1c19f8fd165b62e2cdde7" +checksum = "aee013796927eec6012a344f10ecdc06bf26de79c626a2395e3f115464907ef6" dependencies = [ "openvino-finder", "openvino-sys", @@ -1927,9 +2283,9 @@ dependencies = [ [[package]] name = "openvino-finder" -version = "0.5.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bbd80eea06c2b9ec3dce85900ff3ae596c01105b759b38a005af69bbeb4d07" +checksum = "af4c6841df4cd60fef743015f3348f81b6b225bd255ed0c4cab6e8c479e45eaa" dependencies = [ "cfg-if", "log", @@ -1937,16 +2293,47 @@ dependencies = [ [[package]] name = "openvino-sys" -version = "0.5.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "318ed662bdf05a3f86486408159e806d53363171621a8000b81366fab5158713" +checksum = "f62fc2bd6882f2300a6b5017eaad292586d70995d333582aabcf1f1121cd147c" dependencies = [ + "env_logger", "libloading", "once_cell", "openvino-finder", - "pretty_env_logger 0.4.0", ] +[[package]] +name = "ort" +version = "2.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc80894094c6a875bfac64415ed456fa661081a278a035e22be661305c87e14" +dependencies = [ + "js-sys", + "ort-sys", + "thiserror", + "tracing", + "web-sys", +] + +[[package]] +name = "ort-sys" +version = "2.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d9c1373fc813d3f024d394f621f4c6dde0734c79b1c17113c3bb5bf0084bbe" +dependencies = [ + "flate2", + "sha2", + "tar", + "ureq", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "paste" version = "1.0.7" @@ -1960,43 +2347,77 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] -name = "pin-project-lite" -version = "0.2.9" +name = "pest" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] [[package]] -name = "pin-utils" -version = "0.1.0" +name = "pest_derive" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" +dependencies = [ + "pest", + "pest_generator", +] [[package]] -name = "plotters" -version = "0.3.1" +name = "pest_generator" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.60", ] [[package]] -name = "plotters-backend" -version = "0.3.2" +name = "pest_meta" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" +checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" +dependencies = [ + "once_cell", + "pest", + "sha2", +] [[package]] -name = "plotters-svg" -version = "0.3.1" +name = "pin-project-lite" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" + +[[package]] +name = "postcard" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" dependencies = [ - "plotters-backend", + "cobs", + "embedded-io", + "serde", ] [[package]] @@ -2006,13 +2427,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] -name = "pretty_env_logger" -version = "0.4.0" +name = "pretty" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +checksum = "b55c4d17d994b637e2f4daf6e5dc5d660d209d5642377d675d7a1c3ab69fa579" dependencies = [ - "env_logger 0.7.1", - "log", + "arrayvec", + "typed-arena", + "unicode-width", ] [[package]] @@ -2021,15 +2443,49 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" dependencies = [ - "env_logger 0.10.0", + "env_logger", "log", ] +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.60", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.92", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -2064,14 +2520,23 @@ dependencies = [ ] [[package]] -name = "pulldown-cmark" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +name = "pulley-interpreter" +version = "0.1.0" dependencies = [ - "bitflags 1.3.2", - "memchr", - "unicase", + "arbitrary", + "cranelift-bitset", + "env_logger", + "log", + "sptr", +] + +[[package]] +name = "pulley-interpreter-fuzz" +version = "0.0.0" +dependencies = [ + "env_logger", + "log", + "pulley-interpreter", ] [[package]] @@ -2088,9 +2553,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.29" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -2148,21 +2613,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "reactor-tests" -version = "0.1.0" -dependencies = [ - "wit-bindgen", ] [[package]] @@ -2196,11 +2652,11 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b4dcbd3a2ae7fb94b5813fa0e957c6ab51bf5d0a8ee1b69e0c2d0f1e6eb8485" +checksum = "7d3060e21243fead477032ee2824bd68dfc70596b3333317e5295f6afb1d779d" dependencies = [ - "hashbrown 0.13.2", + "hashbrown 0.14.3", "log", "rustc-hash", "serde", @@ -2264,26 +2720,65 @@ dependencies = [ "winapi", ] +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" -version = "0.16.20" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e" dependencies = [ "cc", + "getrandom", "libc", - "once_cell", - "spin 0.5.2", + "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -2293,55 +2788,94 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags 1.3.2", "errno", - "io-lifetimes 1.0.10", + "io-lifetimes 1.0.11", "libc", - "linux-raw-sys 0.3.3", - "windows-sys", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", ] [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "errno", "itoa", "libc", - "linux-raw-sys 0.4.3", + "linux-raw-sys 0.4.12", "once_cell", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.6" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] +[[package]] +name = "rustls" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebbbdb961df0ad3f2652da8f3fdc4b36122f568f968f45ad3316f26c025c677b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.0", +] + +[[package]] +name = "rustls-pki-types" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" + [[package]] name = "rustls-webpki" -version = "0.101.4" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "rusty-fork" version = "0.3.0" @@ -2369,6 +2903,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -2376,13 +2919,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "sct" -version = "0.7.0" +name = "security-framework" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ - "ring", - "untrusted", + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -2411,7 +2967,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", ] [[package]] @@ -2425,6 +2981,27 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.10.2" @@ -2507,12 +3084,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.9" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2533,12 +3110,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.4" @@ -2569,6 +3140,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "symbolic_expressions" version = "5.0.3" @@ -2588,56 +3165,93 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "system-interface" -version = "0.26.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ce32341b2c0b70c144bbf35627fdc1ef18c76ced5e5e7b3ee8b5ba6b2ab6a0" +checksum = "9aef1f9d4c1dbdd1cb3a63be9efd2f04d8ddbc919d46112982c76818ffc2f1a7" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "cap-fs-ext", "cap-std", "fd-lock", - "io-lifetimes 2.0.2", - "rustix 0.38.8", - "windows-sys", + "io-lifetimes 2.0.3", + "rustix 0.38.31", + "windows-sys 0.52.0", "winx", ] +[[package]] +name = "tar" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" -version = "0.12.3" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.6.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ - "autocfg", "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.13", - "windows-sys", + "rustix 0.38.31", + "windows-sys 0.48.0", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -2652,6 +3266,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "terminal_size" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +dependencies = [ + "rustix 0.37.27", + "windows-sys 0.48.0", +] + [[package]] name = "test-log" version = "0.2.11" @@ -2664,32 +3288,29 @@ dependencies = [ ] [[package]] -name = "test-programs" +name = "test-programs" +version = "0.0.0" +dependencies = [ + "anyhow", + "base64 0.21.0", + "futures", + "getrandom", + "libc", + "sha2", + "url", + "wasi", + "wasi-nn", + "wit-bindgen", +] + +[[package]] +name = "test-programs-artifacts" version = "0.0.0" dependencies = [ - "anyhow", - "bytes", - "cap-rand", - "cap-std", "cargo_metadata", - "cfg-if", - "heck", - "http", - "http-body", - "http-body-util", - "hyper", - "lazy_static", - "tempfile", - "test-log", - "tokio", - "tracing", - "tracing-subscriber", - "wasi-cap-std-sync", - "wasi-common", + "heck 0.4.0", "wasmtime", - "wasmtime-wasi", - "wasmtime-wasi-http", - "wit-component", + "wit-component 0.216.0", ] [[package]] @@ -2709,7 +3330,7 @@ checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", ] [[package]] @@ -2721,6 +3342,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -2748,11 +3378,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.29.1" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "2d3ce25f50619af8b0aec2eb23deebe84249e19e2ddd393a6e16e3300a6dadfd" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", @@ -2762,7 +3391,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2773,16 +3402,27 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", ] [[package]] name = "tokio-rustls" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls", + "rustls 0.22.4", + "rustls-pki-types", "tokio", ] @@ -2802,13 +3442,44 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951" dependencies = [ + "indexmap 2.2.6", "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.37" @@ -2830,7 +3501,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", ] [[package]] @@ -2840,6 +3511,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", ] [[package]] @@ -2849,12 +3532,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", + "nu-ansi-term", "once_cell", "regex", "sharded-slab", "thread_local", "tracing", "tracing-core", + "tracing-log", ] [[package]] @@ -2863,6 +3548,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.15.0" @@ -2870,13 +3561,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] -name = "unicase" -version = "2.6.0" +name = "ucd-trie" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-bidi" @@ -2901,9 +3589,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" @@ -2919,9 +3607,24 @@ checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea" +dependencies = [ + "base64 0.22.1", + "log", + "once_cell", + "rustls 0.23.7", + "rustls-pki-types", + "url", + "webpki-roots", +] [[package]] name = "url" @@ -2945,28 +3648,37 @@ name = "uuid" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0" -dependencies = [ - "getrandom", -] [[package]] name = "v8" -version = "0.74.1" +version = "0.86.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1202e0bd078112bf8d521491560645e1fd6955c4afd975c75b05596a7e7e4eea" +checksum = "0d30d72faeef07020ec4428dbfa2909801e5e36becf650c1b49c92b70f6771d8" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "fslock", "once_cell", "which", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "verify-component-adapter" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", - "wasmparser", + "wasmparser 0.216.0", "wat", ] @@ -3012,104 +3724,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasi-cap-std-sync" -version = "14.0.0" +name = "wasi-common" +version = "25.0.0" dependencies = [ "anyhow", - "async-trait", + "bitflags 2.4.1", "cap-fs-ext", "cap-rand", "cap-std", "cap-time-ext", "fs-set-times", "io-extras", - "io-lifetimes 2.0.2", + "io-lifetimes 2.0.3", + "libc", + "log", "once_cell", - "rustix 0.38.8", + "rustix 0.38.31", "system-interface", "tempfile", - "tracing", - "wasi-common", - "windows-sys", -] - -[[package]] -name = "wasi-common" -version = "14.0.0" -dependencies = [ - "anyhow", - "bitflags 2.3.3", - "cap-rand", - "cap-std", - "io-extras", - "log", - "rustix 0.38.8", + "test-log", + "test-programs-artifacts", "thiserror", + "tokio", "tracing", + "tracing-subscriber", + "wasi-common", "wasmtime", "wiggle", - "windows-sys", -] - -[[package]] -name = "wasi-http-proxy-tests" -version = "0.0.0" -dependencies = [ - "wit-bindgen", + "windows-sys 0.52.0", ] [[package]] -name = "wasi-http-tests" -version = "0.0.0" +name = "wasi-nn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7031683cc05a71515d9200fb159b28d717ded3c40dbb979d1602cf46f3a68f40" dependencies = [ - "anyhow", - "wit-bindgen", + "thiserror", ] [[package]] name = "wasi-preview1-component-adapter" -version = "14.0.0" +version = "25.0.0" dependencies = [ + "bitflags 2.4.1", "byte-array-literals", "object", "wasi", - "wasm-encoder", - "wit-bindgen", -] - -[[package]] -name = "wasi-sockets-tests" -version = "0.0.0" -dependencies = [ - "anyhow", - "wit-bindgen", -] - -[[package]] -name = "wasi-tests" -version = "0.0.0" -dependencies = [ - "libc", - "once_cell", - "wasi", - "wit-bindgen", -] - -[[package]] -name = "wasi-tokio" -version = "14.0.0" -dependencies = [ - "anyhow", - "cap-std", - "cap-tempfile", - "io-extras", - "io-lifetimes 2.0.2", - "rustix 0.38.8", - "tempfile", - "tokio", - "wasi-cap-std-sync", - "wasi-common", - "wiggle", + "wasm-encoder 0.216.0", + "wit-bindgen-rust-macro", ] [[package]] @@ -3133,10 +3796,22 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.87" @@ -3155,7 +3830,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3168,54 +3843,82 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-encoder" -version = "0.33.2" +version = "0.215.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb56df3e06b8e6b77e37d2969a50ba51281029a9aeb3855e76b7f49b6418847" +dependencies = [ + "leb128", + "wasmparser 0.215.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34180c89672b3e4825c3a8db4b61a674f1447afd5fe2445b2d22c3d8b6ea086c" +checksum = "04c23aebea22c8a75833ae08ed31ccc020835b12a41999e58c31464271b94a88" dependencies = [ "leb128", + "wasmparser 0.216.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.215.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6bb07c5576b608f7a2a9baa2294c1a3584a249965d695a9814a496cb6d232f" +dependencies = [ + "anyhow", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder 0.215.0", + "wasmparser 0.215.0", ] [[package]] name = "wasm-metadata" -version = "0.10.6" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "577508d8a45bc54ad97efe77c95ba57bb10e7e5c5bac9c31295ce88b8045cd7d" +checksum = "47c8154d703a6b0e45acf6bd172fa002fc3c7058a9f7615e517220aeca27c638" dependencies = [ "anyhow", - "indexmap 2.0.0", + "indexmap 2.2.6", "serde", + "serde_derive", "serde_json", "spdx", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.216.0", + "wasmparser 0.216.0", ] [[package]] name = "wasm-mutate" -version = "0.2.35" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22ff62504d0e55a4e4c32cdf4e65f9c925ba0bc9904e141394eb2ad2b8f319d" +checksum = "17002e0f291e0c330a81b9285cf0e4e316e10e75b00d4f5c625d325f14582d11" dependencies = [ "egg", "log", "rand", "thiserror", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.216.0", + "wasmparser 0.216.0", ] [[package]] name = "wasm-smith" -version = "0.12.18" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7499466e905b4e8d0cc1720c1bfddf1e5040f6fa42efd4f43edd1b88dbe9ce9b" +checksum = "b25e5a09d7934b471fb84ea864cc91ed1a4467ea8aaa32c09f60f3fb262e070e" dependencies = [ + "anyhow", "arbitrary", "flagset", - "indexmap 2.0.0", + "indexmap 2.2.6", "leb128", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.216.0", ] [[package]] @@ -3229,11 +3932,12 @@ dependencies = [ [[package]] name = "wasmi" -version = "0.20.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01bf50edb2ea9d922aa75a7bf3c15e26a6c9e2d18c56e862b49737a582901729" +checksum = "acfc1e384a36ca532d070a315925887247f3c7e23567e23e0ac9b1c5d6b8bf76" dependencies = [ - "spin 0.9.4", + "smallvec", + "spin", "wasmi_arena", "wasmi_core", "wasmparser-nostd", @@ -3241,126 +3945,171 @@ dependencies = [ [[package]] name = "wasmi_arena" -version = "0.1.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ea379cbb0b41f3a9f0bf7b47036d036aae7f43383d8cc487d4deccf40dee0a" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" [[package]] name = "wasmi_core" -version = "0.5.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5bf998ab792be85e20e771fe14182b4295571ad1d4f89d3da521c1bef5f597a" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" dependencies = [ "downcast-rs", "libm", "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.215.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e" +dependencies = [ + "ahash", + "bitflags 2.4.1", + "hashbrown 0.14.3", + "indexmap 2.2.6", + "semver", ] [[package]] name = "wasmparser" -version = "0.113.2" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0d44fab0bd78404e352f3399324eef76516a4580b52bc9031c60f064e98f3" +checksum = "bcdee6bea3619d311fb4b299721e89a986c3470f804b6d534340e412589028e3" dependencies = [ - "indexmap 2.0.0", + "ahash", + "bitflags 2.4.1", + "hashbrown 0.14.3", + "indexmap 2.2.6", "semver", + "serde", ] [[package]] name = "wasmparser-nostd" -version = "0.91.0" +version = "0.100.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c37f310b5a62bfd5ae7c0f1d8e6f98af16a5d6d84ba764e9c36439ec14e318b" +checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" dependencies = [ "indexmap-nostd", ] [[package]] name = "wasmprinter" -version = "0.2.67" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6615a5587149e753bf4b93f90fa3c3f41c88597a7a2da72879afcabeda9648f" +checksum = "8f82916f3892e53620639217d6ec78fe15c678352a3fbf3f3745b6417d0bd70f" dependencies = [ "anyhow", - "wasmparser", + "termcolor", + "wasmparser 0.216.0", ] [[package]] name = "wasmtime" -version = "14.0.0" +version = "25.0.0" dependencies = [ + "addr2line", "anyhow", "async-trait", - "bincode", + "bitflags 2.4.1", "bumpalo", + "cc", "cfg-if", "encoding_rs", + "env_logger", "fxprof-processed-profile", - "indexmap 2.0.0", + "gimli", + "hashbrown 0.14.3", + "indexmap 2.2.6", + "ittapi", "libc", + "libm", "log", + "mach2", + "memfd", "object", "once_cell", "paste", + "postcard", + "proptest", "psm", + "rand", "rayon", + "rustix 0.38.31", + "semver", "serde", "serde_derive", "serde_json", + "smallvec", + "sptr", "target-lexicon", "tempfile", - "wasi-cap-std-sync", - "wasm-encoder", - "wasmparser", + "wasi-common", + "wasm-encoder 0.216.0", + "wasmparser 0.216.0", + "wasmtime-asm-macros", "wasmtime-cache", "wasmtime-component-macro", "wasmtime-component-util", "wasmtime-cranelift", "wasmtime-environ", "wasmtime-fiber", - "wasmtime-jit", - "wasmtime-runtime", - "wasmtime-wasi", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-slab", + "wasmtime-versioned-export-macros", "wasmtime-winch", + "wasmtime-wmemcheck", "wat", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "wasmtime-asm-macros" -version = "14.0.0" +version = "25.0.0" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-bench-api" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "cap-std", "clap", "shuffling-allocator", "target-lexicon", - "wasi-cap-std-sync", + "wasi-common", "wasmtime", "wasmtime-cli-flags", - "wasmtime-wasi", "wasmtime-wasi-nn", "wat", ] [[package]] name = "wasmtime-c-api" -version = "14.0.0" +version = "25.0.0" +dependencies = [ + "wasmtime-c-api-impl", +] + +[[package]] +name = "wasmtime-c-api-impl" +version = "25.0.0" dependencies = [ "anyhow", "cap-std", - "env_logger 0.10.0", + "env_logger", "futures", + "log", "once_cell", - "wasi-cap-std-sync", - "wasi-common", + "tokio", + "tracing", "wasmtime", "wasmtime-c-api-macros", "wasmtime-wasi", @@ -3369,7 +4118,7 @@ dependencies = [ [[package]] name = "wasmtime-c-api-macros" -version = "0.0.0" +version = "25.0.0" dependencies = [ "proc-macro2", "quote", @@ -3377,60 +4126,73 @@ dependencies = [ [[package]] name = "wasmtime-cache" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", - "base64", - "bincode", + "base64 0.21.0", "directories-next", "filetime", "log", "once_cell", - "pretty_env_logger 0.5.0", - "rustix 0.38.8", + "postcard", + "pretty_env_logger", + "rustix 0.38.31", "serde", "serde_derive", "sha2", "tempfile", "toml", - "windows-sys", + "windows-sys 0.52.0", "zstd", ] [[package]] name = "wasmtime-cli" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "async-trait", "bstr", + "bytes", + "bytesize", + "capstone", + "cfg-if", "clap", "component-macro-test", "component-test-util", + "cranelift-codegen", + "cranelift-filetests", + "cranelift-reader", "criterion", - "env_logger 0.10.0", + "env_logger", "filecheck", - "http-body", + "http 1.0.0", "http-body-util", - "hyper", + "humantime", + "hyper 1.0.1", "libc", + "libtest-mimic", "listenfd", "log", "memchr", "num_cpus", + "object", "once_cell", "rayon", - "rustix 0.38.8", + "rustix 0.38.31", "serde", "serde_derive", "serde_json", + "similar", "target-lexicon", "tempfile", - "test-programs", + "test-programs-artifacts", "tokio", + "toml", + "tracing", "walkdir", - "wasm-encoder", - "wasmparser", + "wasi-common", + "wasmparser 0.216.0", "wasmtime", "wasmtime-cache", "wasmtime-cli-flags", @@ -3438,105 +4200,102 @@ dependencies = [ "wasmtime-cranelift", "wasmtime-environ", "wasmtime-explorer", - "wasmtime-runtime", + "wasmtime-test-macros", "wasmtime-wasi", "wasmtime-wasi-http", + "wasmtime-wasi-keyvalue", "wasmtime-wasi-nn", + "wasmtime-wasi-runtime-config", "wasmtime-wasi-threads", "wasmtime-wast", - "wast 65.0.2", + "wast 216.0.0", "wat", - "windows-sys", + "windows-sys 0.52.0", + "wit-component 0.216.0", ] [[package]] name = "wasmtime-cli-flags" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "clap", "file-per-thread-logger", - "humantime 2.1.0", - "pretty_env_logger 0.5.0", + "humantime", "rayon", + "tracing-subscriber", "wasmtime", ] [[package]] name = "wasmtime-component-macro" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "component-macro-test-helpers", + "prettyplease", "proc-macro2", "quote", - "syn 2.0.29", + "serde", + "serde_json", + "similar", + "syn 2.0.60", "tracing", "wasmtime", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser", + "wit-parser 0.216.0", ] [[package]] name = "wasmtime-component-util" -version = "14.0.0" +version = "25.0.0" [[package]] name = "wasmtime-cranelift" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "cfg-if", "cranelift-codegen", "cranelift-control", "cranelift-entity", - "cranelift-frontend", - "cranelift-native", - "cranelift-wasm", - "gimli", - "log", - "object", - "target-lexicon", - "thiserror", - "wasmparser", - "wasmtime-cranelift-shared", - "wasmtime-environ", - "wasmtime-versioned-export-macros", -] - -[[package]] -name = "wasmtime-cranelift-shared" -version = "14.0.0" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-control", + "cranelift-frontend", "cranelift-native", + "cranelift-wasm", "gimli", + "log", "object", + "smallvec", "target-lexicon", + "thiserror", + "wasmparser 0.216.0", "wasmtime-environ", + "wasmtime-versioned-export-macros", ] [[package]] name = "wasmtime-environ" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "clap", + "cpp_demangle", + "cranelift-bitset", "cranelift-entity", - "env_logger 0.10.0", + "env_logger", "gimli", - "indexmap 2.0.0", + "indexmap 2.2.6", "log", "object", + "postcard", + "rustc-demangle", + "semver", "serde", "serde_derive", "target-lexicon", - "thiserror", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.216.0", + "wasmparser 0.216.0", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -3549,9 +4308,9 @@ version = "0.0.0" dependencies = [ "arbitrary", "component-fuzz-util", - "env_logger 0.10.0", + "env_logger", "libfuzzer-sys", - "wasmparser", + "wasmparser 0.216.0", "wasmprinter", "wasmtime-environ", "wat", @@ -3559,7 +4318,7 @@ dependencies = [ [[package]] name = "wasmtime-explorer" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "capstone", @@ -3569,19 +4328,21 @@ dependencies = [ "target-lexicon", "wasmprinter", "wasmtime", + "wasmtime-environ", ] [[package]] name = "wasmtime-fiber" -version = "14.0.0" +version = "25.0.0" dependencies = [ + "anyhow", "backtrace", "cc", "cfg-if", - "rustix 0.38.8", + "rustix 0.38.31", "wasmtime-asm-macros", "wasmtime-versioned-export-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -3600,14 +4361,16 @@ dependencies = [ "cranelift-native", "cranelift-reader", "cranelift-wasm", + "env_logger", "libfuzzer-sys", "once_cell", "proc-macro2", + "pulley-interpreter-fuzz", "quote", "rand", "smallvec", "target-lexicon", - "wasmparser", + "wasmparser 0.216.0", "wasmtime", "wasmtime-fuzzing", ] @@ -3620,126 +4383,88 @@ dependencies = [ "arbitrary", "component-fuzz-util", "component-test-util", - "env_logger 0.10.0", + "env_logger", + "futures", "log", "rand", "rayon", "target-lexicon", "tempfile", "v8", - "wasm-encoder", + "wasm-encoder 0.216.0", "wasm-mutate", "wasm-smith", "wasm-spec-interpreter", "wasmi", - "wasmparser", + "wasmparser 0.216.0", "wasmprinter", "wasmtime", "wasmtime-wast", "wat", ] -[[package]] -name = "wasmtime-jit" -version = "14.0.0" -dependencies = [ - "addr2line", - "anyhow", - "bincode", - "cfg-if", - "cpp_demangle", - "gimli", - "ittapi", - "log", - "object", - "rustc-demangle", - "rustix 0.38.8", - "serde", - "serde_derive", - "target-lexicon", - "wasmtime-environ", - "wasmtime-jit-debug", - "wasmtime-jit-icache-coherence", - "wasmtime-runtime", - "windows-sys", -] - [[package]] name = "wasmtime-jit-debug" -version = "14.0.0" +version = "25.0.0" dependencies = [ "object", "once_cell", - "rustix 0.38.8", + "rustix 0.38.31", "wasmtime-versioned-export-macros", ] [[package]] name = "wasmtime-jit-icache-coherence" -version = "14.0.0" +version = "25.0.0" dependencies = [ + "anyhow", "cfg-if", "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] -name = "wasmtime-runtime" -version = "14.0.0" +name = "wasmtime-slab" +version = "25.0.0" + +[[package]] +name = "wasmtime-test-macros" +version = "0.0.0" dependencies = [ "anyhow", - "cc", - "cfg-if", - "encoding_rs", - "indexmap 2.0.0", - "libc", - "log", - "mach", - "memfd", - "memoffset", - "once_cell", - "paste", - "proptest", - "rand", - "rustix 0.38.8", - "sptr", - "wasm-encoder", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-fiber", - "wasmtime-jit-debug", - "wasmtime-versioned-export-macros", - "wasmtime-wmemcheck", - "windows-sys", + "proc-macro2", + "quote", + "syn 2.0.60", ] [[package]] name = "wasmtime-types" -version = "14.0.0" +version = "25.0.0" dependencies = [ + "anyhow", "cranelift-entity", "serde", "serde_derive", - "thiserror", - "wasmparser", + "smallvec", + "wasmparser 0.216.0", ] [[package]] name = "wasmtime-versioned-export-macros" -version = "14.0.0" +version = "25.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", ] [[package]] name = "wasmtime-wasi" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "async-trait", - "bitflags 2.3.3", + "bitflags 2.4.1", "bytes", "cap-fs-ext", "cap-net-ext", @@ -3749,63 +4474,94 @@ dependencies = [ "fs-set-times", "futures", "io-extras", - "io-lifetimes 2.0.2", - "libc", - "log", + "io-lifetimes 2.0.3", "once_cell", - "rustix 0.38.8", + "rustix 0.38.31", "system-interface", + "tempfile", "test-log", + "test-programs-artifacts", "thiserror", "tokio", "tracing", "tracing-subscriber", "url", - "wasi-cap-std-sync", - "wasi-common", - "wasi-tokio", "wasmtime", "wiggle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "wasmtime-wasi-http" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "async-trait", + "base64 0.21.0", "bytes", "futures", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", - "hyper", - "rustls", + "hyper 1.0.1", + "rustls 0.22.4", + "sha2", + "test-log", + "test-programs-artifacts", "tokio", "tokio-rustls", "tracing", + "tracing-subscriber", "wasmtime", "wasmtime-wasi", "webpki-roots", ] +[[package]] +name = "wasmtime-wasi-keyvalue" +version = "25.0.0" +dependencies = [ + "anyhow", + "test-programs-artifacts", + "tokio", + "wasmtime", + "wasmtime-wasi", +] + [[package]] name = "wasmtime-wasi-nn" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", + "cap-std", + "libtest-mimic", "openvino", + "ort", + "test-programs-artifacts", "thiserror", "tracing", + "tracing-subscriber", "walkdir", "wasmtime", + "wasmtime-wasi", "wiggle", + "windows", +] + +[[package]] +name = "wasmtime-wasi-runtime-config" +version = "25.0.0" +dependencies = [ + "anyhow", + "test-programs-artifacts", + "tokio", + "wasmtime", + "wasmtime-wasi", ] [[package]] name = "wasmtime-wasi-threads" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "log", @@ -3817,42 +4573,42 @@ dependencies = [ [[package]] name = "wasmtime-wast" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "log", "wasmtime", - "wast 65.0.2", + "wast 216.0.0", ] [[package]] name = "wasmtime-winch" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "cranelift-codegen", "gimli", "object", "target-lexicon", - "wasmparser", - "wasmtime-cranelift-shared", + "wasmparser 0.216.0", + "wasmtime-cranelift", "wasmtime-environ", "winch-codegen", ] [[package]] name = "wasmtime-wit-bindgen" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", - "heck", - "indexmap 2.0.0", - "wit-parser", + "heck 0.4.0", + "indexmap 2.2.6", + "wit-parser 0.216.0", ] [[package]] name = "wasmtime-wmemcheck" -version = "14.0.0" +version = "25.0.0" [[package]] name = "wast" @@ -3865,23 +4621,24 @@ dependencies = [ [[package]] name = "wast" -version = "65.0.2" +version = "216.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55a88724cf8c2c0ebbf32c8e8f4ac0d6aa7ba6d73a1cfd94b254aa8f894317e" +checksum = "f7eb1f2eecd913fdde0dc6c3439d0f24530a98ac6db6cb3d14d92a5328554a08" dependencies = [ + "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder", + "wasm-encoder 0.216.0", ] [[package]] name = "wat" -version = "1.0.74" +version = "1.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83e1a8d86d008adc7bafa5cf4332d448699a08fcf2a715a71fbb75e2c5ca188" +checksum = "ac0409090fb5154f95fb5ba3235675fd9e579e731524d63b6a2f653e1280c82a" dependencies = [ - "wast 65.0.2", + "wast 216.0.0", ] [[package]] @@ -3896,28 +4653,33 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "which" -version = "4.2.5" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" dependencies = [ "either", - "lazy_static", - "libc", + "home", + "once_cell", + "rustix 0.38.31", + "windows-sys 0.48.0", ] [[package]] name = "wiggle" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", "async-trait", - "bitflags 2.3.3", + "bitflags 2.4.1", "proptest", "thiserror", "tokio", @@ -3930,24 +4692,24 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "14.0.0" +version = "25.0.0" dependencies = [ "anyhow", - "heck", + "heck 0.4.0", "proc-macro2", "quote", "shellexpand", - "syn 2.0.29", + "syn 2.0.60", "witx", ] [[package]] name = "wiggle-macro" -version = "14.0.0" +version = "25.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", "wiggle", "wiggle-generate", ] @@ -3957,7 +4719,7 @@ name = "wiggle-test" version = "0.0.0" dependencies = [ "anyhow", - "env_logger 0.10.0", + "env_logger", "proptest", "thiserror", "tracing", @@ -3998,7 +4760,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "0.12.0" +version = "0.23.0" dependencies = [ "anyhow", "cranelift-codegen", @@ -4006,57 +4768,52 @@ dependencies = [ "regalloc2", "smallvec", "target-lexicon", - "wasmparser", + "wasmparser 0.216.0", + "wasmtime-cranelift", "wasmtime-environ", ] [[package]] -name = "winch-filetests" -version = "0.0.0" +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "anyhow", - "capstone", - "cranelift-codegen", - "serde", - "serde_derive", - "similar", - "target-lexicon", - "toml", - "wasmtime-environ", - "wat", - "winch-codegen", - "winch-test-macros", + "windows-core", + "windows-implement", + "windows-interface", + "windows-targets 0.52.0", ] [[package]] -name = "winch-test-macros" -version = "0.0.0" +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" dependencies = [ - "glob", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.60", ] [[package]] -name = "winch-tools" -version = "0.0.0" +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" dependencies = [ - "anyhow", - "capstone", - "clap", - "cranelift-codegen", - "glob", - "serde", - "similar", - "target-lexicon", - "toml", - "wasmparser", - "wasmtime-environ", - "wat", - "winch-codegen", - "winch-filetests", - "winch-test-macros", + "proc-macro2", + "quote", + "syn 2.0.60", ] [[package]] @@ -4065,208 +4822,369 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] [[package]] name = "winx" -version = "0.36.1" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4857cedf8371f690bb6782a3e2b065c54d1b6661be068aaf3eac8b45e813fdf8" +checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" dependencies = [ - "bitflags 2.3.3", - "windows-sys", + "bitflags 2.4.1", + "windows-sys 0.52.0", ] [[package]] name = "wit-bindgen" -version = "0.12.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f7c5d6f59ae013fc4c013c76eab667844a46e86b51987acb71b1e32953211a" +checksum = "7b4bac478334a647374ff24a74b66737a4cb586dc8288bc3080a93252cd1105c" dependencies = [ - "bitflags 2.3.3", + "wit-bindgen-rt", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.12.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f0371c47784e7559efb422f74473e395b49f7101725584e2673657e0b4fc104" +checksum = "bb7e3df01cd43cfa1cb52602e4fc05cb2b62217655f6705639b6953eb0a3fed2" dependencies = [ "anyhow", - "wit-component", - "wit-parser", + "heck 0.5.0", + "wit-parser 0.215.0", ] [[package]] -name = "wit-bindgen-rust" -version = "0.12.0" +name = "wit-bindgen-rt" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeab5a09a85b1641690922ce05d79d868a2f2e78e9415a5302f58b9846fab8f1" +checksum = "b2de7a3b06b9725d129b5cbd1beca968feed919c433305a23da46843185ecdd6" dependencies = [ - "anyhow", - "heck", - "wasm-metadata", - "wit-bindgen-core", - "wit-bindgen-rust-lib", - "wit-component", + "bitflags 2.4.1", ] [[package]] -name = "wit-bindgen-rust-lib" -version = "0.12.0" +name = "wit-bindgen-rust" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13c89c9c1a93e164318745841026f63f889376f38664f86a7f678930280e728" +checksum = "61a767d1a8eb4e908bfc53febc48b87ada545703b16fe0148ee7736a29a01417" dependencies = [ - "heck", + "anyhow", + "heck 0.5.0", + "indexmap 2.2.6", + "prettyplease", + "syn 2.0.60", + "wasm-metadata 0.215.0", "wit-bindgen-core", + "wit-component 0.215.0", ] [[package]] name = "wit-bindgen-rust-macro" -version = "0.12.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a70c97e09751a9a95a592bd8ef84e953e5cdce6ebbfdb35ceefa5cc511da3b71" +checksum = "9b185c342d0d27bd83d4080f5a66cf3b4f247fa49d679bceb66e11cc7eb58b99" dependencies = [ "anyhow", + "prettyplease", "proc-macro2", - "syn 2.0.29", + "quote", + "syn 2.0.60", "wit-bindgen-core", "wit-bindgen-rust", - "wit-bindgen-rust-lib", - "wit-component", ] [[package]] name = "wit-component" -version = "0.14.4" +version = "0.215.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f725e3885fc5890648be5c5cbc1353b755dc932aa5f1aa7de968b912a3280743" +dependencies = [ + "anyhow", + "bitflags 2.4.1", + "indexmap 2.2.6", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.215.0", + "wasm-metadata 0.215.0", + "wasmparser 0.215.0", + "wit-parser 0.215.0", +] + +[[package]] +name = "wit-component" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee23614740bf871dac9856e3062c7a308506eb3f0a2759939ab8d0aa8436a1c0" +checksum = "7e2ca3ece38ea2447a9069b43074ba73d96dde1944cba276c54e41371745f9dc" dependencies = [ "anyhow", - "bitflags 2.3.3", - "indexmap 2.0.0", + "bitflags 2.4.1", + "indexmap 2.2.6", "log", "serde", + "serde_derive", "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", + "wasm-encoder 0.216.0", + "wasm-metadata 0.216.0", + "wasmparser 0.216.0", + "wit-parser 0.216.0", ] [[package]] name = "wit-parser" -version = "0.11.3" +version = "0.215.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a39edca9abb16309def3843af73b58d47d243fe33a9ceee572446bcc57556b9a" +checksum = "935a97eaffd57c3b413aa510f8f0b550a4a9fe7d59e79cd8b89a83dcb860321f" dependencies = [ "anyhow", "id-arena", - "indexmap 2.0.0", + "indexmap 2.2.6", "log", - "pulldown-cmark", "semver", "serde", + "serde_derive", "serde_json", "unicode-xid", - "url", + "wasmparser 0.215.0", +] + +[[package]] +name = "wit-parser" +version = "0.216.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4d108165c1167a4ccc8a803dcf5c28e0a51d6739fd228cc7adce768632c764c" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.2.6", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.216.0", ] [[package]] name = "witx" version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" dependencies = [ "anyhow", "log", - "rayon", "thiserror", "wast 35.0.2", ] +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys 0.4.12", + "rustix 0.38.31", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + [[package]] name = "zstd" -version = "0.11.1+zstd.1.5.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a16b8414fde0414e90c612eba70985577451c4c504b99885ebed24762cb81a" +checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "5.0.1+zstd.1.5.2" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c12659121420dd6365c5c3de4901f97145b79651fb1d25814020ed2ed0585ae" +checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" dependencies = [ - "libc", "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.1+zstd.1.5.2" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", + "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index d4cc8fc06a95..e5a47c2366b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,27 @@ edition.workspace = true default-run = "wasmtime" rust-version.workspace = true +[package.metadata.binstall] +pkg-url = "{repo}/releases/download/v{version}/wasmtime-v{version}-{target-arch}-{target-family}{archive-suffix}" +bin-dir = "wasmtime-v{version}-{target-arch}-{target-family}/{bin}{binary-ext}" +pkg-fmt = "txz" +[package.metadata.binstall.overrides.x86_64-apple-darwin] +pkg-url = "{repo}/releases/download/v{version}/wasmtime-v{version}-{target-arch}-macos{archive-suffix}" +bin-dir = "wasmtime-v{version}-{target-arch}-macos/{bin}{binary-ext}" +[package.metadata.binstall.overrides.aarch64-apple-darwin] +pkg-url = "{repo}/releases/download/v{version}/wasmtime-v{version}-{target-arch}-macos{archive-suffix}" +bin-dir = "wasmtime-v{version}-{target-arch}-macos/{bin}{binary-ext}" +[package.metadata.binstall.overrides.x86_64-pc-windows-msvc] +pkg-fmt = "zip" +[package.metadata.binstall.overrides.x86_64-pc-windows-gnu] +pkg-fmt = "zip" +[package.metadata.binstall.overrides.x86_64-unknown-linux-musl] +pkg-url = "{repo}/releases/download/v{version}/wasmtime-v{version}-{target-arch}-musl{archive-suffix}" +bin-dir = "wasmtime-v{version}-{target-arch}-musl/{bin}{binary-ext}" + +[lints] +workspace = true + [lib] doctest = false @@ -22,53 +43,56 @@ path = "src/bin/wasmtime.rs" doc = false [dependencies] -wasmtime = { workspace = true, features = ['cache', 'cranelift'] } -wasmtime-cache = { workspace = true } +wasmtime = { workspace = true, features = ['std'] } +wasmtime-cache = { workspace = true, optional = true } wasmtime-cli-flags = { workspace = true } -wasmtime-cranelift = { workspace = true } +wasmtime-cranelift = { workspace = true, optional = true } wasmtime-environ = { workspace = true } -wasmtime-explorer = { workspace = true } -wasmtime-wast = { workspace = true } -wasmtime-wasi = { workspace = true, default-features = true, features = [ - "exit", -] } +wasmtime-explorer = { workspace = true, optional = true } +wasmtime-wast = { workspace = true, optional = true } +wasi-common = { workspace = true, default-features = true, features = ["exit", "tokio"], optional = true } +wasmtime-wasi = { workspace = true, default-features = true, optional = true } wasmtime-wasi-nn = { workspace = true, optional = true } +wasmtime-wasi-runtime-config = { workspace = true, optional = true } +wasmtime-wasi-keyvalue = { workspace = true, optional = true } wasmtime-wasi-threads = { workspace = true, optional = true } wasmtime-wasi-http = { workspace = true, optional = true } -wasmtime-runtime = { workspace = true } -clap = { workspace = true, features = ["color", "suggestions", "derive"] } -anyhow = { workspace = true } +clap = { workspace = true } +anyhow = { workspace = true, features = ['std'] } target-lexicon = { workspace = true } once_cell = { workspace = true } -listenfd = "1.0.0" -wat = { workspace = true } +listenfd = { version = "1.0.0", optional = true } +wat = { workspace = true, optional = true } serde = { workspace = true } serde_derive = { workspace = true } serde_json = { workspace = true } wasmparser = { workspace = true } -wasm-encoder = { workspace = true } +tracing = { workspace = true } +log = { workspace = true } +humantime = { workspace = true } +tempfile = { workspace = true, optional = true } async-trait = { workspace = true } +bytes = { workspace = true } +cfg-if = { workspace = true } tokio = { workspace = true, optional = true, features = [ "signal", "macros" ] } hyper = { workspace = true, optional = true } -http-body = { workspace = true } -http-body-util = { workspace = true } +http = { workspace = true, optional = true } +http-body-util = { workspace = true, optional = true } [target.'cfg(unix)'.dependencies] -rustix = { workspace = true, features = ["mm", "param"] } +rustix = { workspace = true, features = ["mm", "param", "process"] } [dev-dependencies] # depend again on wasmtime to activate its default features for tests -wasmtime = { workspace = true, features = ['component-model', 'async', 'default', 'winch'] } +wasmtime = { workspace = true, features = ['default', 'winch', 'all-arch', 'call-hook', 'memory-protection-keys'] } env_logger = { workspace = true } log = { workspace = true } filecheck = { workspace = true } tempfile = { workspace = true } -test-programs = { path = "crates/test-programs" } -wasmtime-runtime = { workspace = true } tokio = { workspace = true, features = ["rt", "time", "macros", "rt-multi-thread"] } wast = { workspace = true } -criterion = "0.5.0" +criterion = { workspace = true } num_cpus = "1.13.0" memchr = "2.4" async-trait = { workspace = true } @@ -83,12 +107,24 @@ libc = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } walkdir = { workspace = true } +test-programs-artifacts = { workspace = true } +bytesize = "1.3.0" +wit-component = { workspace = true } +cranelift-filetests = { workspace = true } +cranelift-codegen = { workspace = true, features = ["disas", "trace-log", "timing"] } +cranelift-reader = { workspace = true } +toml = { workspace = true } +similar = { workspace = true } +libtest-mimic = { workspace = true } +capstone = { workspace = true } +object = { workspace = true, features = ['std'] } +wasmtime-test-macros = { path = "crates/test-macros" } [target.'cfg(windows)'.dev-dependencies] windows-sys = { workspace = true, features = ["Win32_System_Memory"] } [build-dependencies] -anyhow = { workspace = true } +anyhow = { workspace = true, features = ['std'] } [profile.release.build-override] opt-level = 0 @@ -99,145 +135,166 @@ members = [ "cranelift", "cranelift/isle/fuzz", "cranelift/isle/islec", + "cranelift/isle/veri/aslp", + "cranelift/isle/veri/isaspec", + "cranelift/isle/veri/meta", + "cranelift/isle/veri/test-macros", + "cranelift/isle/veri/veri", "cranelift/serde", "crates/bench-api", - "crates/c-api", - "crates/cli-flags", + "crates/c-api/artifact", "crates/environ/fuzz", - "crates/jit-icache-coherence", - "crates/test-programs/wasi-tests", - "crates/test-programs/wasi-http-tests", - "crates/test-programs/wasi-http-proxy-tests", - "crates/test-programs/wasi-sockets-tests", - "crates/test-programs/command-tests", - "crates/test-programs/reactor-tests", - "crates/wmemcheck", + "crates/test-programs", "crates/wasi-preview1-component-adapter", "crates/wasi-preview1-component-adapter/verify", - "crates/winch", "examples/fib-debug/wasm", "examples/wasi/wasm", "examples/tokio/wasm", + "examples/component/wasm", + "examples/min-platform", + "examples/min-platform/embedding", "fuzz", - "winch", "winch/codegen", ] exclude = [ - 'crates/wasi-common/WASI/tools/witx-cli', 'docs/rust_wasi_markdown_parser', ] [workspace.package] -version = "14.0.0" +version = "25.0.0" authors = ["The Wasmtime Project Developers"] edition = "2021" # Wasmtime's current policy is that this number can be no larger than the # current stable release of Rust minus 2. -rust-version = "1.70.0" +rust-version = "1.78.0" + +[workspace.lints.rust] +# Turn on some lints which are otherwise allow-by-default in rustc. +unused_extern_crates = 'warn' +trivial_numeric_casts = 'warn' +unstable_features = 'warn' +unused_import_braces = 'warn' +unused-lifetimes = 'warn' + +[workspace.lints.clippy] +# The default set of lints in Clippy is viewed as "too noisy" right now so +# they're all turned off by default. Selective lints are then enabled below as +# necessary. +all = { level = 'allow', priority = -1 } +clone_on_copy = 'warn' +map_clone = 'warn' +uninlined_format_args = 'warn' +unnecessary_to_owned = 'warn' +manual_strip = 'warn' [workspace.dependencies] -wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=14.0.0" } -wasmtime = { path = "crates/wasmtime", version = "14.0.0", default-features = false } -wasmtime-cache = { path = "crates/cache", version = "=14.0.0" } -wasmtime-cli-flags = { path = "crates/cli-flags", version = "=14.0.0" } -wasmtime-cranelift = { path = "crates/cranelift", version = "=14.0.0" } -wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=14.0.0" } -wasmtime-winch = { path = "crates/winch", version = "=14.0.0" } -wasmtime-environ = { path = "crates/environ", version = "=14.0.0" } -wasmtime-explorer = { path = "crates/explorer", version = "=14.0.0" } -wasmtime-fiber = { path = "crates/fiber", version = "=14.0.0" } -wasmtime-types = { path = "crates/types", version = "14.0.0" } -wasmtime-jit = { path = "crates/jit", version = "=14.0.0" } -wasmtime-jit-debug = { path = "crates/jit-debug", version = "=14.0.0" } -wasmtime-runtime = { path = "crates/runtime", version = "=14.0.0" } -wasmtime-wast = { path = "crates/wast", version = "=14.0.0" } -wasmtime-wasi = { path = "crates/wasi", version = "14.0.0", default-features = false } -wasmtime-wasi-http = { path = "crates/wasi-http", version = "=14.0.0", default-features = false } -wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "14.0.0" } -wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "14.0.0" } -wasmtime-component-util = { path = "crates/component-util", version = "=14.0.0" } -wasmtime-component-macro = { path = "crates/component-macro", version = "=14.0.0" } -wasmtime-asm-macros = { path = "crates/asm-macros", version = "=14.0.0" } -wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=14.0.0" } +arbitrary = { version = "1.3.1" } +wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=25.0.0" } +wasmtime = { path = "crates/wasmtime", version = "25.0.0", default-features = false } +wasmtime-c-api-macros = { path = "crates/c-api-macros", version = "=25.0.0" } +wasmtime-cache = { path = "crates/cache", version = "=25.0.0" } +wasmtime-cli-flags = { path = "crates/cli-flags", version = "=25.0.0" } +wasmtime-cranelift = { path = "crates/cranelift", version = "=25.0.0" } +wasmtime-winch = { path = "crates/winch", version = "=25.0.0" } +wasmtime-environ = { path = "crates/environ", version = "=25.0.0" } +wasmtime-explorer = { path = "crates/explorer", version = "=25.0.0" } +wasmtime-fiber = { path = "crates/fiber", version = "=25.0.0" } +wasmtime-types = { path = "crates/types", version = "25.0.0" } +wasmtime-jit-debug = { path = "crates/jit-debug", version = "=25.0.0" } +wasmtime-wast = { path = "crates/wast", version = "=25.0.0" } +wasmtime-wasi = { path = "crates/wasi", version = "25.0.0", default-features = false } +wasmtime-wasi-http = { path = "crates/wasi-http", version = "=25.0.0", default-features = false } +wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "25.0.0" } +wasmtime-wasi-runtime-config = { path = "crates/wasi-runtime-config", version = "25.0.0" } +wasmtime-wasi-keyvalue = { path = "crates/wasi-keyvalue", version = "25.0.0" } +wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "25.0.0" } +wasmtime-component-util = { path = "crates/component-util", version = "=25.0.0" } +wasmtime-component-macro = { path = "crates/component-macro", version = "=25.0.0" } +wasmtime-asm-macros = { path = "crates/asm-macros", version = "=25.0.0" } +wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=25.0.0" } +wasmtime-slab = { path = "crates/slab", version = "=25.0.0" } component-test-util = { path = "crates/misc/component-test-util" } component-fuzz-util = { path = "crates/misc/component-fuzz-util" } -wiggle = { path = "crates/wiggle", version = "=14.0.0", default-features = false } -wiggle-macro = { path = "crates/wiggle/macro", version = "=14.0.0" } -wiggle-generate = { path = "crates/wiggle/generate", version = "=14.0.0" } -wasi-common = { path = "crates/wasi-common", version = "=14.0.0" } -wasi-tokio = { path = "crates/wasi-common/tokio", version = "=14.0.0" } -wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=14.0.0" } +wiggle = { path = "crates/wiggle", version = "=25.0.0", default-features = false } +wiggle-macro = { path = "crates/wiggle/macro", version = "=25.0.0" } +wiggle-generate = { path = "crates/wiggle/generate", version = "=25.0.0" } +wasi-common = { path = "crates/wasi-common", version = "=25.0.0", default-features = false } wasmtime-fuzzing = { path = "crates/fuzzing" } -wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=14.0.0" } -wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=14.0.0" } - -cranelift-wasm = { path = "cranelift/wasm", version = "0.101.0" } -cranelift-codegen = { path = "cranelift/codegen", version = "0.101.0" } -cranelift-frontend = { path = "cranelift/frontend", version = "0.101.0" } -cranelift-entity = { path = "cranelift/entity", version = "0.101.0" } -cranelift-native = { path = "cranelift/native", version = "0.101.0" } -cranelift-module = { path = "cranelift/module", version = "0.101.0" } -cranelift-interpreter = { path = "cranelift/interpreter", version = "0.101.0" } -cranelift-reader = { path = "cranelift/reader", version = "0.101.0" } +wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=25.0.0" } +wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=25.0.0" } +test-programs-artifacts = { path = 'crates/test-programs/artifacts' } + +pulley-interpreter = { path = 'pulley', version = "=0.1.0" } +pulley-interpreter-fuzz = { path = 'pulley/fuzz' } + +cranelift-wasm = { path = "cranelift/wasm", version = "0.112.0" } +cranelift-codegen = { path = "cranelift/codegen", version = "0.112.0", default-features = false, features = ["std", "unwind"] } +cranelift-frontend = { path = "cranelift/frontend", version = "0.112.0" } +cranelift-entity = { path = "cranelift/entity", version = "0.112.0" } +cranelift-native = { path = "cranelift/native", version = "0.112.0" } +cranelift-module = { path = "cranelift/module", version = "0.112.0" } +cranelift-interpreter = { path = "cranelift/interpreter", version = "0.112.0" } +cranelift-reader = { path = "cranelift/reader", version = "0.112.0" } cranelift-filetests = { path = "cranelift/filetests" } -cranelift-object = { path = "cranelift/object", version = "0.101.0" } -cranelift-jit = { path = "cranelift/jit", version = "0.101.0" } +cranelift-object = { path = "cranelift/object", version = "0.112.0" } +cranelift-jit = { path = "cranelift/jit", version = "0.112.0" } cranelift-fuzzgen = { path = "cranelift/fuzzgen" } -cranelift-bforest = { path = "cranelift/bforest", version = "0.101.0" } -cranelift-control = { path = "cranelift/control", version = "0.101.0" } -cranelift = { path = "cranelift/umbrella", version = "0.101.0" } +cranelift-bforest = { path = "cranelift/bforest", version = "0.112.0" } +cranelift-bitset = { path = "cranelift/bitset", version = "0.112.0" } +cranelift-control = { path = "cranelift/control", version = "0.112.0" } +cranelift = { path = "cranelift/umbrella", version = "0.112.0" } -winch-codegen = { path = "winch/codegen", version = "=0.12.0" } -winch-filetests = { path = "winch/filetests" } -winch-test-macros = { path = "winch/test-macros" } +winch-codegen = { path = "winch/codegen", version = "=0.23.0" } wasi-preview1-component-adapter = { path = "crates/wasi-preview1-component-adapter" } byte-array-literals = { path = "crates/wasi-preview1-component-adapter/byte-array-literals" } # Bytecode Alliance maintained dependencies: # --------------------------- -regalloc2 = "0.9.2" +regalloc2 = "0.9.4" # cap-std family: -target-lexicon = { version = "0.12.3", default-features = false, features = ["std"] } -cap-std = "2.0.0" -cap-rand = { version = "2.0.0", features = ["small_rng"] } -cap-fs-ext = "2.0.0" -cap-net-ext = "2.0.0" -cap-time-ext = "2.0.0" -cap-tempfile = "2.0.0" -fs-set-times = "0.20.0" -system-interface = { version = "0.26.0", features = ["cap_std_impls"] } -io-lifetimes = { version = "2.0.2", default-features = false } -io-extras = "0.18.0" -rustix = "0.38.8" -is-terminal = "0.4.0" +target-lexicon = "0.12.16" +cap-std = "3.0.0" +cap-rand = { version = "3.0.0", features = ["small_rng"] } +cap-fs-ext = "3.0.0" +cap-net-ext = "3.0.0" +cap-time-ext = "3.0.0" +cap-tempfile = "3.0.0" +fs-set-times = "0.20.1" +system-interface = { version = "0.27.1", features = ["cap_std_impls"] } +io-lifetimes = { version = "2.0.3", default-features = false } +io-extras = "0.18.1" +rustix = "0.38.31" # wit-bindgen: -wit-bindgen = { version = "0.12.0", default-features = false } +wit-bindgen = { version = "0.30.0", default-features = false } +wit-bindgen-rust-macro = { version = "0.30.0", default-features = false } # wasm-tools family: -wasmparser = "0.113.2" -wat = "1.0.74" -wast = "65.0.2" -wasmprinter = "0.2.67" -wasm-encoder = "0.33.2" -wasm-smith = "0.12.18" -wasm-mutate = "0.2.35" -wit-parser = "0.11.3" -wit-component = "0.14.4" +wasmparser = { version = "0.216.0", default-features = false } +wat = "1.216.0" +wast = "216.0.0" +wasmprinter = "0.216.0" +wasm-encoder = "0.216.0" +wasm-smith = "0.216.0" +wasm-mutate = "0.216.0" +wit-parser = "0.216.0" +wit-component = "0.216.0" # Non-Bytecode Alliance maintained dependencies: # -------------------------- -object = { version = "0.32", default-features = false, features = ['read_core', 'elf', 'std'] } -gimli = { version = "0.28.0", default-features = false, features = ['read', 'std'] } -anyhow = "1.0.22" -windows-sys = "0.48.0" +cc = "1.0" +object = { version = "0.36", default-features = false, features = ['read_core', 'elf'] } +gimli = { version = "0.29.0", default-features = false, features = ['read'] } +addr2line = { version = "0.22.0", default-features = false } +anyhow = { version = "1.0.22", default-features = false } +windows-sys = "0.52.0" env_logger = "0.10" log = { version = "0.4.8", default-features = false } -clap = { version = "4.3.12", features = ["color", "suggestions", "derive"] } +clap = { version = "4.3.12", default-features = false, features = ["std", "derive"] } hashbrown = { version = "0.14", default-features = false } -capstone = "0.9.0" -once_cell = "1.12.0" +capstone = "0.12.0" +once_cell = { version = "1.12.0", default-features = false } smallvec = { version = "1.6.1", features = ["union"] } tracing = "0.1.26" bitflags = "2.0" @@ -245,9 +302,15 @@ thiserror = "1.0.43" async-trait = "0.1.71" heck = "0.4" similar = "2.1.0" -toml = "0.5.9" +toml = "0.8.10" +mach2 = "0.4.2" +memfd = "0.6.2" +psm = "0.1.11" +proptest = "1.0.0" +rand = { version = "0.8.3", features = ["small_rng"] } +sptr = "0.3.2" # serde and serde_derive must have the same version -serde = "1.0.188" +serde = { version = "1.0.188", default-features = false, features = ['alloc'] } serde_derive = "1.0.188" serde_json = "1.0.80" glob = "0.3.0" @@ -256,58 +319,150 @@ walkdir = "2.3.3" cfg-if = "1.0" tempfile = "3.1.0" filecheck = "0.5.0" -libc = "0.2.60" +libc = { version = "0.2.112", default-features = true } file-per-thread-logger = "0.2.0" -tokio = { version = "1.26.0", features = [ "rt", "time" ] } -hyper = "=1.0.0-rc.3" -http = "0.2.9" -http-body = "=1.0.0-rc.2" -http-body-util = "=0.1.0-rc.2" +tokio = { version = "1.30.0", features = [ "rt", "time" ] } +hyper = "1.0.1" +http = "1.0.0" +http-body = "1.0.0" +http-body-util = "0.1.0" +reqwest = "0.11" bytes = "1.4" futures = { version = "0.3.27", default-features = false } -indexmap = "2.0.0" +indexmap = { version = "2.0.0", default-features = false } pretty_env_logger = "0.5.0" syn = "2.0.25" test-log = { version = "0.2", default-features = false, features = ["trace"] } -tracing-subscriber = { version = "0.3.1", default-features = false, features = ['fmt', 'env-filter'] } +tracing-subscriber = { version = "0.3.1", default-features = false, features = ['fmt', 'env-filter', 'ansi', 'tracing-log'] } url = "2.3.1" +humantime = "2.0.0" +postcard = { version = "1.0.8", default-features = false, features = ['alloc'] } +criterion = { version = "0.5.0", default-features = false, features = ["html_reports", "rayon"] } +rustc-hash = "1.1.0" +libtest-mimic = "0.7.0" +semver = { version = "1.0.17", default-features = false } +# ============================================================================= +# +# Features for the Wasmtime CLI executable +# +# +# Note that many of these features are inherited from Wasmtime itself or +# otherwise configure the `wasmtime` crate's execution. Features are provided as +# compile-time switches to disable functionality primarily if one is interested +# in configuring binary size and or exploring the binary size implications of +# various features. Most features are enabled by default but most embeddings +# likely won't need all features. +# +# When adding or removing a feature, make sure to kepe the C API in sync by +# modifying locations marked WASMTIME_FEATURE_LIST [features] default = [ - "jitdump", - "wasmtime/wat", - "wasmtime/parallel-compilation", - "vtune", + # All subcommands are included by default. + "run", + "compile", + "explore", + "serve", + "wast", + "config", + + # On-by-default WASI features "wasi-nn", "wasi-threads", "wasi-http", + "wasi-runtime-config", + "wasi-keyvalue", + + # Most features of Wasmtime are enabled by default. + "wat", + "parallel-compilation", "pooling-allocator", + "cache", + "logging", + "demangle", + "cranelift", + "profiling", + "coredump", + "addr2line", + "debug-builtins", + "component-model", + "threads", + "gc", + "winch", + + # Enable some nice features of clap by default, but they come at a binary size + # cost, so allow disabling this through disabling of our own `default` + # feature. + "clap/default", + "clap/wrap_help", ] -jitdump = ["wasmtime/jitdump"] -vtune = ["wasmtime/vtune"] + +# ======================================== +# Off-by-default features +# +# These features are off-by-default but may optionally be enabled. +all-arch = ["wasmtime/all-arch"] +winch = ["wasmtime/winch"] +wmemcheck = ["wasmtime/wmemcheck"] +memory-protection-keys = ["wasmtime-cli-flags/memory-protection-keys"] + +# 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"] + +# ======================================== +# On-by-default features +# +# These features are all included in the `default` set above and this is +# the internal mapping for what they enable in Wasmtime itself. wasi-nn = ["dep:wasmtime-wasi-nn"] -wasi-threads = ["dep:wasmtime-wasi-threads"] -wasi-http = ["component-model", "dep:wasmtime-wasi-http", "dep:tokio", "dep:hyper", "wasmtime-wasi-http?/sync"] +wasi-threads = ["dep:wasmtime-wasi-threads", "threads"] +wasi-http = ["component-model", "dep:wasmtime-wasi-http", "dep:tokio", "dep:hyper"] +wasi-runtime-config = ["dep:wasmtime-wasi-runtime-config"] +wasi-keyvalue = ["dep:wasmtime-wasi-keyvalue"] pooling-allocator = ["wasmtime/pooling-allocator", "wasmtime-cli-flags/pooling-allocator"] -all-arch = ["wasmtime/all-arch"] component-model = [ "wasmtime/component-model", - "wasmtime-wast/component-model", + "wasmtime-wast?/component-model", "wasmtime-cli-flags/component-model" ] -winch = ["wasmtime/winch"] -wmemcheck = ["wasmtime/wmemcheck"] +wat = ["dep:wat", "wasmtime/wat"] +cache = ["dep:wasmtime-cache", "wasmtime-cli-flags/cache"] +parallel-compilation = ["wasmtime-cli-flags/parallel-compilation"] +logging = ["wasmtime-cli-flags/logging"] +demangle = ["wasmtime/demangle"] +cranelift = ["wasmtime-cli-flags/cranelift", "dep:wasmtime-cranelift"] +profiling = ["wasmtime/profiling", "wasmtime/call-hook"] +coredump = ["wasmtime-cli-flags/coredump"] +addr2line = ["wasmtime/addr2line"] +debug-builtins = ["wasmtime/debug-builtins"] +threads = ["wasmtime-cli-flags/threads"] +gc = ["wasmtime-cli-flags/gc"] -# Enable the `wasmtime serve` command -serve = ["wasi-http", "component-model"] +# CLI subcommands for the `wasmtime` executable. See `wasmtime $cmd --help` +# for more information on each subcommand. +serve = ["wasi-http", "component-model", "dep:http-body-util", "dep:http"] +explore = ["dep:wasmtime-explorer", "dep:tempfile"] +wast = ["dep:wasmtime-wast"] +config = ["cache"] +compile = ["cranelift"] +run = ["dep:wasmtime-wasi", "wasmtime/runtime", "dep:listenfd", "dep:wasi-common", "dep:tokio"] [[test]] name = "host_segfault" harness = false +[[test]] +name = "disas" +harness = false + +[[test]] +name = "wast" +harness = false + [[example]] name = "tokio" -required-features = ["wasmtime-wasi/tokio"] +required-features = ["wasi-common/tokio"] [[bench]] name = "instantiation" @@ -351,3 +506,8 @@ incremental = false debug-assertions = false overflow-checks = false opt-level = 's' + +[profile.fastest-runtime] +inherits = "release" +codegen-units = 1 +lto = true diff --git a/README.md b/README.md index 3b52b9d9a0af..add5f9622e5f 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ fn main() { and compile/run it with: ```sh -$ rustup target add wasm32-wasi -$ rustc hello.rs --target wasm32-wasi +$ rustup target add wasm32-wasip1 +$ rustc hello.rs --target wasm32-wasip1 $ wasmtime hello.wasm Hello, world! ``` @@ -108,7 +108,7 @@ command may not install the target for the correct copy of Rust.) [Secure]: https://docs.wasmtime.dev/security.html [Configurable]: https://docs.rs/wasmtime/latest/wasmtime/struct.Config.html [WASI]: https://docs.rs/wasmtime-wasi/latest/wasmtime_wasi/ -[Standards Compliant]: https://docs.wasmtime.dev/stability-wasm-proposals-support.html +[Standards Compliant]: https://docs.wasmtime.dev/stability-tiers.html ## Language Support @@ -131,7 +131,7 @@ Languages supported by the community: * **Perl** - the [`Wasm` Perl package's `Wasm::Wasmtime`] [Rust]: https://bytecodealliance.github.io/wasmtime/lang-rust.html -[C]: https://bytecodealliance.github.io/wasmtime/examples-c-embed.html +[C]: https://bytecodealliance.github.io/wasmtime/lang-c.html [`wasmtime` crate]: https://crates.io/crates/wasmtime [c-headers]: https://bytecodealliance.github.io/wasmtime/c-api/ [Python]: https://bytecodealliance.github.io/wasmtime/lang-python.html diff --git a/RELEASES.md b/RELEASES.md index cc89c3f2231c..b8b5e96fe415 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3045 +1,47 @@ --------------------------------------------------------------------------------- - -## 14.0.0 +## 25.0.0 Unreleased. ### Added -* Added the `wasmtime::FrameInfo::module` method, which returns the - `wasmtime::Module` associated with the stack frame. - -### Changed - -* The `wasmtime::FrameInfo::module_name` has been removed, however you can now - get identical results by chaining `wasmtime::FrameInfo::module` and - `wasmtime::Module::name`: `my_frame.module().name()`. - --------------------------------------------------------------------------------- - -## 13.0.0 - -Released 2023-09-20 - -### Added - -* Configuration of mach ports vs signals on macOS is now done through a `Config` - instead of at compile time. - [#6807](https://github.com/bytecodealliance/wasmtime/pull/6807) - -* `Engine::detect_precompiled{,_file}` can be used to to determine whether some - bytes or a file look like a precompiled module or a component. - [#6832](https://github.com/bytecodealliance/wasmtime/pull/6832) - [#6937](https://github.com/bytecodealliance/wasmtime/pull/6937) - -* A new feature "wmemcheck" has been added to enable Valgrind-like detection of - use-after-free within a WebAssembly guest module. - [#6820](https://github.com/bytecodealliance/wasmtime/pull/6820) - [#6856](https://github.com/bytecodealliance/wasmtime/pull/6856) - -* The `wasmtime` CLI now supports executing components. - [#6836](https://github.com/bytecodealliance/wasmtime/pull/6836) - -* Support for WASI preview2's TCP sockets interface has been added. - [#6837](https://github.com/bytecodealliance/wasmtime/pull/6837) - -* Wasmtime's implementation of the wasi-nn proposal now supports named models. - [#6854](https://github.com/bytecodealliance/wasmtime/pull/6854) - -* The C API now supports configuring `native_unwind_info`, - `dynamic_memory_reserved_for_growth`, `target`, and Cranelift settings. - [#6896](https://github.com/bytecodealliance/wasmtime/pull/6896) - [#6934](https://github.com/bytecodealliance/wasmtime/pull/6934) - -* The `wasmtime` crate now has initial support for component model bindings - generation for the WIT `resource` type. - [#6886](https://github.com/bytecodealliance/wasmtime/pull/6886) - -* Cranelift's RISC-V backend now has a complete implementation of the - WebAssembly SIMD proposal. Many thanks to Afonso Bordado for all their - contributions! - [#6920](https://github.com/bytecodealliance/wasmtime/pull/6920) - [#6924](https://github.com/bytecodealliance/wasmtime/pull/6924) - -* The `bindgen!` macro in the `wasmtime` crate now supports conditional - configuration for which imports should be `async` and which should be - synchronous. - [#6942](https://github.com/bytecodealliance/wasmtime/pull/6942) - -### Changed - -* The pooling allocator was significantly refactored and the - `PoolingAllocationConfig` has some minor breaking API changes that reflect - those changes. - - Previously, the pooling allocator had `count` slots, and each slot had `N` - memories and `M` tables. Every allocated instance would reserve those `N` - memories and `M` tables regardless whether it actually needed them all or - not. This could lead to some waste and over-allocation when a module used less - memories and tables than the pooling allocator's configured maximums. - - After the refactors in this release, the pooling allocator doesn't have - one-size-fits-all slots anymore. Instead, memories and tables are in separate - pools that can be allocated from independently, and we allocate exactly as - many memories and tables as are necessary for the instance being allocated. - - To preserve your old configuration with the new methods you can do the following: - - ```rust - let mut config = PoolingAllocationConfig::default(); - - // If you used to have this old, no-longer-compiling configuration: - config.count(count); - config.instance_memories(n); - config.instance_tables(m); - - // You can use these equivalent settings for the new config methods: - config.total_core_instances(count); - config.total_stacks(count); // If using the `async` feature. - config.total_memories(count * n); - config.max_memories_per_module(n); - config.total_tables(count * m); - config.max_tables_per_module(m); - ``` - - There are additionally a variety of methods to limit the maximum amount of - resources a single core Wasm or component instance can take from the pool: - - * `PoolingAllocationConfig::max_memories_per_module` - * `PoolingAllocationConfig::max_tables_per_module` - * `PoolingAllocationConfig::max_memories_per_component` - * `PoolingAllocationConfig::max_tables_per_component` - * `PoolingAllocationConfig::max_core_instances_per_component` - - These methods do not affect the size of the pre-allocated pool. - [#6835](https://github.com/bytecodealliance/wasmtime/pull/6835) - -* Builder methods for WASI contexts now use `&mut self` instead of `self`. - [#6770](https://github.com/bytecodealliance/wasmtime/pull/6770) - -* Native unwinding information is now properly disabled when it is configured to - be turned off. - [#6547](https://github.com/bytecodealliance/wasmtime/pull/6547) - -* Wasmtime's minimum supported Rust version (MSRV) is now 1.70.0. Wasmtime's - MSRV policy of supporting the last three releases of Rust (N-2) is now - additionally documented. More discussion can additionally be found on the PR - itself. - [#6900](https://github.com/bytecodealliance/wasmtime/pull/6900) - -* Wasmtime's support for DWARF debugging information has seen some fixes for - previously reported crashes. - [#6931](https://github.com/bytecodealliance/wasmtime/pull/6931) - -### Removed - -* Wasmtime's experimental implementation of wasi-crypto has been removed. More - discussion of this change can be found on - [#6732](https://github.com/bytecodealliance/wasmtime/pull/6732) - and - [#6816](https://github.com/bytecodealliance/wasmtime/pull/6816) - -* Support for `union` types in the component model has been removed. - [#6913](https://github.com/bytecodealliance/wasmtime/pull/6913) - --------------------------------------------------------------------------------- - -## 12.0.2 - -Released 2023-09-14. - -### Fixed - -* [CVE-2023-41880] - Miscompilation of wasm `i64x2.shr_s` instruction with - constant input on x86\_64 - -[CVE-2023-41880]: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-gw5p-q8mj-p7gh - --------------------------------------------------------------------------------- - -## 12.0.1 - -Released 2023-08-24 - -### Fixed - -* Optimized the cranelift compilation on aarch64 for large wasm modules. - [#6804](https://github.com/bytecodealliance/wasmtime/pull/6804) - --------------------------------------------------------------------------------- - -## 12.0.0 - -Released 2023-08-21 - -### Added - -* Wasmtime now supports having multiple different versions of itself being - linked into the same final executable by mangling some C symbols used by - Wasmtime. - [#6673](https://github.com/bytecodealliance/wasmtime/pull/6673) - -* The `perfmap` profiling option is now supported on any Unix platform instead - of just Linux. - [#6701](https://github.com/bytecodealliance/wasmtime/pull/6701) - -* The `wasmtime` CLI now supports `--env FOO` to inherit the value of the - environment variable `FOO` which avoids needing to do `--env FOO=$FOO` for - example. - [#6746](https://github.com/bytecodealliance/wasmtime/pull/6746) - -* Wasmtime now supports component model resources, although support has not yet - been added to `bindgen!`. - [#6691](https://github.com/bytecodealliance/wasmtime/pull/6691) - -* Wasmtime now supports configuration to enable the tail calls proposal. - Platform support now also includes AArch64 and RISC-V in addition to the - previous x86\_64 support. - [#6723](https://github.com/bytecodealliance/wasmtime/pull/6723) - [#6749](https://github.com/bytecodealliance/wasmtime/pull/6749) - [#6774](https://github.com/bytecodealliance/wasmtime/pull/6774) - -* Wasmtime's implementation of WASI Preview 2 now supports streams/pollables - with host objects that are all backed by Rust `async`. - [#6556](https://github.com/bytecodealliance/wasmtime/pull/6556) - -* Support for core dumps has now been added to the `wasmtime` crate. - [#6513](https://github.com/bytecodealliance/wasmtime/pull/6513) - -* New `{Module,Component}::resources_required` APIs allow inspecting what will - be required when instantiating the module or component. - [#6789](https://github.com/bytecodealliance/wasmtime/pull/6789) - -### Fixed - -* Functions on instances defined through `component::Linker::func_new` are now - defined correctly. - [#6637](https://github.com/bytecodealliance/wasmtime/pull/6637) - -* The `async_stack_size` configuration option is no longer inspected when - `async_support` is disabled at runtime. - [#6771](https://github.com/bytecodealliance/wasmtime/pull/6771) - -* WASI Preview 1 APIs will now trap on misaligned or out-of-bounds pointers - instead of returning an error. - [#6776](https://github.com/bytecodealliance/wasmtime/pull/6776) - -### Changed - -* Empty types are no longer allowed in the component model. - [#6777](https://github.com/bytecodealliance/wasmtime/pull/6777) - --------------------------------------------------------------------------------- - -## 11.0.2 - -Released 2023-09-14. - -### Fixed - -* [CVE-2023-41880] - Miscompilation of wasm `i64x2.shr_s` instruction with - constant input on x86\_64 - -[CVE-2023-41880]: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-gw5p-q8mj-p7gh - --------------------------------------------------------------------------------- - -## 11.0.1 - -Released 2023-07-24. - -### Fixed - -* Update some minimum version requirements for Wasmtime's dependencies to fix - building Wasmtime with historical versions of these dependencies. - [#6758](https://github.com/bytecodealliance/wasmtime/pull/6758) - --------------------------------------------------------------------------------- - -## 11.0.0 - -Released 2023-07-20 - -### Changed - -* The WASI Preview 2 `WasiCtxBuilder` type has been refactored, and `WasiCtx` now has private - fields. - [#6652](https://github.com/bytecodealliance/wasmtime/pull/6652) - -* Component `bindgen!` now generates owned types by default instead of based on - how they're used - [#6648](https://github.com/bytecodealliance/wasmtime/pull/6648) - -* Wasmtime/Cranelift on x86-64 can now execute Wasm-SIMD on baseline SSE2, which - all x86-64 processors support (as part of the base x86-64 spec). Previously, - SSE4.2 extensions were required. This new work allows Wasm with SIMD - extensions to execute on processors produced back to 2003. - [#6625](https://github.com/bytecodealliance/wasmtime/pull/6625) - - -### Fixed - -* Only export the top-level preview2 module from wasmtime-wasi when the - `preview2` feature is enabled. - [#6615](https://github.com/bytecodealliance/wasmtime/pull/6615) - - -### Cranelift changes - -* Tail call implementation has begun in Cranelift - [#6641](https://github.com/bytecodealliance/wasmtime/pull/6641) - [#6666](https://github.com/bytecodealliance/wasmtime/pull/6666) - [#6650](https://github.com/bytecodealliance/wasmtime/pull/6650) - [#6635](https://github.com/bytecodealliance/wasmtime/pull/6635) - [#6608](https://github.com/bytecodealliance/wasmtime/pull/6608) - [#6586](https://github.com/bytecodealliance/wasmtime/pull/6586) - -* Work continues on SIMD support for the riscv64 backend - [#6657](https://github.com/bytecodealliance/wasmtime/pull/6657) - [#6643](https://github.com/bytecodealliance/wasmtime/pull/6643) - [#6601](https://github.com/bytecodealliance/wasmtime/pull/6601) - [#6609](https://github.com/bytecodealliance/wasmtime/pull/6609) - [#6602](https://github.com/bytecodealliance/wasmtime/pull/6602) - [#6598](https://github.com/bytecodealliance/wasmtime/pull/6598) - [#6599](https://github.com/bytecodealliance/wasmtime/pull/6599) - [#6587](https://github.com/bytecodealliance/wasmtime/pull/6587) - [#6568](https://github.com/bytecodealliance/wasmtime/pull/6568) - [#6515](https://github.com/bytecodealliance/wasmtime/pull/6515) - -* Fix `AuthenticatedRet` when stack bytes are popped in the aarch64 backend - [#6634](https://github.com/bytecodealliance/wasmtime/pull/6634) - -* The `fcvt_low_from_sint` instruction has been removed, as it its current - behavior can be recovered through a combination of `swiden_low` and - `fcvt_from_sint` - [#6565](https://github.com/bytecodealliance/wasmtime/pull/6565) - --------------------------------------------------------------------------------- - -## 10.0.2 - -Released 2023-09-14. - -### Fixed - -* [CVE-2023-41880] - Miscompilation of wasm `i64x2.shr_s` instruction with - constant input on x86\_64 - -[CVE-2023-41880]: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-gw5p-q8mj-p7gh - --------------------------------------------------------------------------------- - -## 10.0.1 - -Released 2023-06-21 - -### Fixed - -* Only export the top-level preview2 module from wasmtime-wasi when the - `preview2` feature is enabled. - [#6615](https://github.com/bytecodealliance/wasmtime/pull/6615) - --------------------------------------------------------------------------------- - -## 10.0.0 - -Released 2023-06-20 - -### Added - -* Expose the `Config::static_memory_forced` option through the C api - [#6413](https://github.com/bytecodealliance/wasmtime/pull/6413) - -* Basic guest-profiler documentation for the book - [#6394](https://github.com/bytecodealliance/wasmtime/pull/6394) - -* Merge the initial wasi-preview2 implementation - [#6391](https://github.com/bytecodealliance/wasmtime/pull/6391) - -* The wasi-preview2 component adapter has been pulled into the main wasmtime - repository. It is available for the first time as part of this release, but should be - treated as as a beta at this time. Patch releases will not be made for bug fixes. - [#6374](https://github.com/bytecodealliance/wasmtime/pull/6374) - -* A callback invoked when an epoch deadline is reached can now be configured via - the C API. - [#6359](https://github.com/bytecodealliance/wasmtime/pull/6359) - -* PR auto-assignment policies have been documented, to clarify the expectations of - reviewers. - [#6346](https://github.com/bytecodealliance/wasmtime/pull/6346) - -* Support for the function references has been added - [#5288](https://github.com/bytecodealliance/wasmtime/pull/5288) - -### Changed - -* An `epoch_deadline_callback` now returns an `UpdateDeadline` enum to allow - optionally yielding to the async executor after the callback runs. - [#6464](https://github.com/bytecodealliance/wasmtime/pull/6464) - -* The `--profile-guest` flag has now been folded into `--profile=guest` - [#6352](https://github.com/bytecodealliance/wasmtime/pull/6352) - -* Initializers are no longer tracked in the type information for globals, and - instead are provided when creating the global. - [#6349](https://github.com/bytecodealliance/wasmtime/pull/6349) - -* The "raw" representation of `funcref` and `externref` in the embedding API has - been updated from a `usize` to a `*mut u8` to be compatible with Rust's - proposed strict provenance rules. This change is additionally reflected into - the C API as well. - [#6338](https://github.com/bytecodealliance/wasmtime/pull/6338) - -### Fixed - -* Fixed a soundness issue with the component model and async - [#6509](https://github.com/bytecodealliance/wasmtime/pull/6509) - -* Opening directories with WASI on Windows with `NONBLOCK` in flags has been - fixed. - [#6348](https://github.com/bytecodealliance/wasmtime/pull/6348) - -### Cranelift changes - -* Performance improvements in regalloc2 have landed, and compilation time has - improved - [#6483](https://github.com/bytecodealliance/wasmtime/pull/6483) - [#6398](https://github.com/bytecodealliance/wasmtime/pull/6398) - -* Renamed `abi::Caller` to `abi::CallSite` - [#6414](https://github.com/bytecodealliance/wasmtime/pull/6414) - -* Work has begun on SIMD support for the riscv64 backend - [#6324](https://github.com/bytecodealliance/wasmtime/pull/6324) - [#6366](https://github.com/bytecodealliance/wasmtime/pull/6366) - [#6367](https://github.com/bytecodealliance/wasmtime/pull/6367) - [#6392](https://github.com/bytecodealliance/wasmtime/pull/6392) - [#6397](https://github.com/bytecodealliance/wasmtime/pull/6397) - [#6403](https://github.com/bytecodealliance/wasmtime/pull/6403) - [#6408](https://github.com/bytecodealliance/wasmtime/pull/6408) - [#6419](https://github.com/bytecodealliance/wasmtime/pull/6419) - [#6430](https://github.com/bytecodealliance/wasmtime/pull/6430) - [#6507](https://github.com/bytecodealliance/wasmtime/pull/6507) - --------------------------------------------------------------------------------- - -## 9.0.3 - -Released 2023-05-31. - -### Fixed - -* Fix Wasi rights system to work with wasi-testsuite, which exposed a corner case - that was missed by the fixes in the 9.0.2 release. - [#6479](https://github.com/bytecodealliance/wasmtime/pull/6479) - --------------------------------------------------------------------------------- - -## 9.0.2 - -Released 2023-05-26. - -### Fixed - -* Fix Wasi rights system to work with wasi-libc. This regression was - introduced in the 9.0.0 release. - [#6462](https://github.com/bytecodealliance/wasmtime/pull/6462) - [#6471](https://github.com/bytecodealliance/wasmtime/pull/6471) - --------------------------------------------------------------------------------- - -## 9.0.1 - -Released 2023-05-22. - -### Fixed - -* A panic which happened when enabling support for native platform profilers was - fixed. - [#6435](https://github.com/bytecodealliance/wasmtime/pull/6435) - --------------------------------------------------------------------------------- - -## 9.0.0 - -Released 2023-05-22. - -### Added - -* Initial integration of the Winch baseline compiler into Wasmtime is - implemented. Note that Winch still does not support much of WebAssembly, but - intrepid explorers may have an easier time playing around with it now. - [#6119](https://github.com/bytecodealliance/wasmtime/pull/6119) - -* The `wasmtime` CLI now has flags to limit memory, instances, and tables. For - example `--max-memory-size` or `--max-tables`. Additionally it has a new - `--trap-on-grow-failure` option to force a trap whenever a `memory.grow` would - otherwise fail which can be useful for debugging modules which may be - encountering OOM. - [#6149](https://github.com/bytecodealliance/wasmtime/pull/6149) - -* An initial implementation of the wasi-http proposal was added to Wasmtime in - the shape of a new `wasmtime-wasi-http` crate and a - `--wasi-modules=experimental-wasi-http` CLI flag. Note that this is not - on-by-default and still in an experimental status at this time. - [#5929](https://github.com/bytecodealliance/wasmtime/pull/5929) - -* Wasmtime's `bindgen!` macro for components now has `interfaces` and - `with` options to configure use of interfaces defined externally in separate - crates. - [#6160](https://github.com/bytecodealliance/wasmtime/pull/6160) - [#6210](https://github.com/bytecodealliance/wasmtime/pull/6210) - -* Wasmtime's `bindgen!` macro emits trace events for arguments and results - when enabled. - [#6209](https://github.com/bytecodealliance/wasmtime/pull/6209) - -* A new `Engine::precompile_compatibility_hash` method has been added to assist - with hashing artifacts to be compatible with versions of Wasmtime. - [#5826](https://github.com/bytecodealliance/wasmtime/pull/5826) - -* Wasmtime's C API now has functions for enabling the WebAssembly relaxed-simd - proposal. - [#6292](https://github.com/bytecodealliance/wasmtime/pull/6292) - -* A new `--emit-clif` flag has been added to `wasmtime compile` to see the CLIF - corresponding to a WebAssembly module to be used for debugging. - [#6307](https://github.com/bytecodealliance/wasmtime/pull/6307) - -* Support for an in-process sampling-based profiler has been added to Wasmtime. - This is intended to be used in conjunction with epochs to enable relatively - simple implementations of profiling a guest module. - [#6282](https://github.com/bytecodealliance/wasmtime/pull/6282) - -### Changed - -* Overhauled the way that Wasmtime calls into Wasm and Wasm calls back out to - the host. Instead of chaining together trampolines to convert between calling - conventions, we now represent `funcref`s with multiple function pointers, one - per calling convention. This paves the way for supporting Wasm tail calls and - also results in ~10% speed ups to a variety of function call benchmarks, - however there are some slight compiled Wasm module code size regressions - (which can be alleviated by disabling optional `.eh_frame` - generation). Additionally, in the C API the `wasmtime_func_call_unchecked` - function gained one more parameter, which is the capacity of the - args-and-results - buffer. - [#6262](https://github.com/bytecodealliance/wasmtime/pull/6262) - -* The `wasmtime compile` command will now default to producing executables for - the native host and its CPU features instead of the baseline feature set of - the host's architecture. - [#6152](https://github.com/bytecodealliance/wasmtime/pull/6152) - -* The `ResourceLimiter` trait and its `async` equivalent now support returning - errors from growth to force a trap in the wasm module rather than reporting - -1 to the wasm module. Note that this is primarily intended for debugging. - [#6149](https://github.com/bytecodealliance/wasmtime/pull/6149) - -* The non-egraph-based optimization pipeline has been removed from Cranelift, - and the corresponding `Config::use_egraphs` option is also removed. - [#6167](https://github.com/bytecodealliance/wasmtime/pull/6167) - -* Generated types for WIT files now always generates owned types by default. - [#6189](https://github.com/bytecodealliance/wasmtime/pull/6189) - -* Wasmtime's baseline x86\_64 CPU features required for SIMD support has been - lowered from SSE 4.2 to SSE 4.1. - [#6206](https://github.com/bytecodealliance/wasmtime/pull/6206) - -* The `fd_allocate` implementation in Wasmtime will now always fail with - `ENOTSUP`. - [#6217](https://github.com/bytecodealliance/wasmtime/pull/6217) - -* The "rights" system in WASI has been removed and rights are no longer - inspected in the implementation of any WASI functions. - [#6265](https://github.com/bytecodealliance/wasmtime/pull/6265) - -### Fixed - -* WASI can now open directories without `O_DIRECTORY`. - [#6163](https://github.com/bytecodealliance/wasmtime/pull/6163) - -* The `poll_oneoff` function has been fixed when handling non-regular files. - [#6258](https://github.com/bytecodealliance/wasmtime/pull/6258) - -* The behavior of `path_readlink` on too-small buffers has been fixed to - truncate. - [#6225](https://github.com/bytecodealliance/wasmtime/pull/6225) - -### Cranelift changes - -> Note: this section documents changes to Cranelift, a code generator backend -> that Wasmtime uses. These changes are not always applicable to Wasmtime as a -> WebAssembly runtime but may be interesting to other projects which embed or -> use Cranelift. - -* New `{u,s}{add,sub,mul}_overflow` instructions have been added. - [#5784](https://github.com/bytecodealliance/wasmtime/pull/5784) - -* The `iadd_cout` and `isub_bout` instructions have been removed. - [#6198](https://github.com/bytecodealliance/wasmtime/pull/6198) - -* ISLE now supports binary and octal integer literals. - [#6234](https://github.com/bytecodealliance/wasmtime/pull/6234) - -* An implementation of SIMD for RISC-V has started. - [#6240](https://github.com/bytecodealliance/wasmtime/pull/6240) - [#6266](https://github.com/bytecodealliance/wasmtime/pull/6266) - [#6268](https://github.com/bytecodealliance/wasmtime/pull/6268) - --------------------------------------------------------------------------------- - -## 8.0.1 - -Released 2023-04-27. - -### Changed - -* Breaking: Files opened using Wasmtime's implementation of WASI on Windows now - cannot be deleted until the file handle is closed. This was already true for - open directories. The change was necessary for the bug fix in - [#6163](https://github.com/bytecodealliance/wasmtime/pull/6163). - -### Fixed - -* Fixed wasi-common's implementation of the `O_DIRECTORY` flag to match POSIX. - [#6163](https://github.com/bytecodealliance/wasmtime/pull/6163) - -* Undefined Behavior in Rust runtime functions - [GHSA-ch89-5g45-qwc7](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ch89-5g45-qwc7) - --------------------------------------------------------------------------------- - -## 8.0.0 - -Released 2023-04-20 - -### Added - -* Allow the MPL-2.0 and OpenSSL licenses in dependencies of wasmtime. - [#6136](https://github.com/bytecodealliance/wasmtime/pull/6136) - -* Add a bounds-checking optimization for dynamic memories and guard pages. - [#6031](https://github.com/bytecodealliance/wasmtime/pull/6031) - -* Add support for generating perf maps for simple perf profiling. Additionally, - the `--jitdump` and `--vtune` flags have been replaced with a single - `--profile` flags that accepts `perfmap`, `jitdump`, and `vtune` arguments. - [#6030](https://github.com/bytecodealliance/wasmtime/pull/6030) - -* Validate faulting addresses are valid to fault on. As a mitigation to CVEs - like `GHSA-ff4p-7xrq-q5r8`, check that the address involved in a fault is one - that could be contained in a `Store`, or print a scary message and abort - immediately. - [#6028](https://github.com/bytecodealliance/wasmtime/pull/6028) - -* Add the `--default-values-unknown-imports` option to define unknown function - imports as functions that return the default value for their result type. - [#6010](https://github.com/bytecodealliance/wasmtime/pull/6010) - -* Add `Clone` for `component::InstancePre`. - [#5996](https://github.com/bytecodealliance/wasmtime/issues/5996) - -* Add `--dynamic-memory-reserved-for-growth` cli flag. - [#5980](https://github.com/bytecodealliance/wasmtime/issues/5980) - -* Introduce the `wasmtime-explorer` crate for investigating the compilation of - wasm modules. This functionality is also exposed via the `wasmtime explore` - command. - [#5975](https://github.com/bytecodealliance/wasmtime/pull/5975) - -* Added support for the Relaxed SIMD proposal. - [#5892](https://github.com/bytecodealliance/wasmtime/pull/5892) - -* Cranelift gained many new machine-independent optimizations. - [#5909](https://github.com/bytecodealliance/wasmtime/pull/5909) - [#6032](https://github.com/bytecodealliance/wasmtime/pull/6032) - [#6033](https://github.com/bytecodealliance/wasmtime/pull/6033) - [#6034](https://github.com/bytecodealliance/wasmtime/pull/6034) - [#6037](https://github.com/bytecodealliance/wasmtime/pull/6037) - [#6052](https://github.com/bytecodealliance/wasmtime/pull/6052) - [#6053](https://github.com/bytecodealliance/wasmtime/pull/6053) - [#6072](https://github.com/bytecodealliance/wasmtime/pull/6072) - [#6095](https://github.com/bytecodealliance/wasmtime/pull/6095) - [#6130](https://github.com/bytecodealliance/wasmtime/pull/6130) - ### Changed -* Derive `Copy` on `wasmtime::ValType`. - [#6138](https://github.com/bytecodealliance/wasmtime/pull/6138) - -* Make `StoreContextMut` accessible in the epoch deadline callback. - [#6075](https://github.com/bytecodealliance/wasmtime/pull/6075) - -* Take SIGFPE signals for divide traps on `x86_64`. - [#6026](https://github.com/bytecodealliance/wasmtime/pull/6026) - -* Use more specialized AVX instructions in the `x86_64` backend. - [#5924](https://github.com/bytecodealliance/wasmtime/pull/5924) - [#5930](https://github.com/bytecodealliance/wasmtime/pull/5930) - [#5931](https://github.com/bytecodealliance/wasmtime/pull/5931) - [#5982](https://github.com/bytecodealliance/wasmtime/pull/5982) - [#5986](https://github.com/bytecodealliance/wasmtime/pull/5986) - [#5999](https://github.com/bytecodealliance/wasmtime/pull/5999) - [#6023](https://github.com/bytecodealliance/wasmtime/pull/6023) - [#6025](https://github.com/bytecodealliance/wasmtime/pull/6025) - [#6060](https://github.com/bytecodealliance/wasmtime/pull/6060) - [#6086](https://github.com/bytecodealliance/wasmtime/pull/6086) - [#6092](https://github.com/bytecodealliance/wasmtime/pull/6092) - -* Generate more cache-friendly code for traps. - [#6011](https://github.com/bytecodealliance/wasmtime/pull/6011) - -### Fixed - -* Fixed suboptimal code generation in the `aarch64` backend. - [#5976](https://github.com/bytecodealliance/wasmtime/pull/5976) - [#5977](https://github.com/bytecodealliance/wasmtime/pull/5977) - [#5987](https://github.com/bytecodealliance/wasmtime/pull/5987) - [#5997](https://github.com/bytecodealliance/wasmtime/pull/5997) - [#6078](https://github.com/bytecodealliance/wasmtime/pull/6078) - -* Fixed suboptimal code generation in the `riscv64` backend. - [#5854](https://github.com/bytecodealliance/wasmtime/pull/5854) - [#5857](https://github.com/bytecodealliance/wasmtime/pull/5857) - [#5919](https://github.com/bytecodealliance/wasmtime/pull/5919) - [#5951](https://github.com/bytecodealliance/wasmtime/pull/5951) - [#5964](https://github.com/bytecodealliance/wasmtime/pull/5964) - [#6087](https://github.com/bytecodealliance/wasmtime/pull/6087) - - --------------------------------------------------------------------------------- - -## 7.0.1 - -Released 2023-04-27. - -### Fixed - -* Undefined Behavior in Rust runtime functions - [GHSA-ch89-5g45-qwc7](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ch89-5g45-qwc7) - -------------------------------------------------------------------------------- -## 7.0.0 - -Released 2023-03-20 - -### Added - -* An initial implementation of the wasi-threads proposal has been implemented - and landed in the Wasmtime CLI. This is available behind a - `--wasi-modules experimental-wasi-threads` flag. - [#5484](https://github.com/bytecodealliance/wasmtime/pull/5484) - -* Support for WASI sockets has been added to the C API. - [#5624](https://github.com/bytecodealliance/wasmtime/pull/5624) - -* Support for limiting `Store`-based resource usage, such as memory, tables, - etc, has been added to the C API. - [#5761](https://github.com/bytecodealliance/wasmtime/pull/5761) - -* A top level alias of `anyhow::Result` as `wasmtime::Result` has been added to - avoid the need to explicitly depend on `anyhow`. - [#5853](https://github.com/bytecodealliance/wasmtime/pull/5853) - -* Initial support for the WebAssembly core dump format has been added to the CLI - with a `--coredump-on-trap` flag. - [#5868](https://github.com/bytecodealliance/wasmtime/pull/5868) - -### Changed - -* The `S` type parameter on component-related methods has been removed. - [#5722](https://github.com/bytecodealliance/wasmtime/pull/5722) - -* Selection of a `world` to bindgen has been updated to select any `default - world` in a WIT package if there is only one. - [#5779](https://github.com/bytecodealliance/wasmtime/pull/5779) - -* WASI preopened file descriptors can now be closed. - [#5828](https://github.com/bytecodealliance/wasmtime/pull/5828) - -* The host traits generated by the `bindgen!` macro are now always named `Host`, - but are still scoped to each individual module. - [#5890](https://github.com/bytecodealliance/wasmtime/pull/5890) - -### Fixed - -* Components which have `type` imports are now supported better and error/panic - in fewer cases. - [#5777](https://github.com/bytecodealliance/wasmtime/pull/5777) - -* Types referred to by `wasmtime::component::Val` are now reexported under - `wasmtime::component`. - [#5790](https://github.com/bytecodealliance/wasmtime/pull/5790) - -* A panic due to a race between `memory.atomic.{wait32,wait64,notify}` - instructions has been fixed. - [#5871](https://github.com/bytecodealliance/wasmtime/pull/5871) - -* Guest-controlled out-of-bounds read/write on x86\_64 - [GHSA-ff4p-7xrq-q5r8](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ff4p-7xrq-q5r8) - -* Miscompilation of `i8x16.select` with the same inputs on x86\_64 - [GHSA-xm67-587q-r2vw](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-xm67-587q-r2vw) - --------------------------------------------------------------------------------- - -## 6.0.2 - -Released 2023-04-27. - -### Fixed - -* Undefined Behavior in Rust runtime functions - [GHSA-ch89-5g45-qwc7](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ch89-5g45-qwc7) - --------------------------------------------------------------------------------- - -## 6.0.1 - -Released 2023-03-08. - -### Fixed - -* Guest-controlled out-of-bounds read/write on x86\_64 - [GHSA-ff4p-7xrq-q5r8](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ff4p-7xrq-q5r8) - -* Miscompilation of `i8x16.select` with the same inputs on x86\_64 - [GHSA-xm67-587q-r2vw](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-xm67-587q-r2vw) - --------------------------------------------------------------------------------- - -## 6.0.0 - -Released 2023-02-20 - -### Added - -* Wasmtime's built-in cache can now be disabled after being enabled previously. - [#5542](https://github.com/bytecodealliance/wasmtime/pull/5542) - -* Older x86\_64 CPUs, without SSE4.1 for example, are now supported when the - wasm SIMD proposal is disabled. - [#5567](https://github.com/bytecodealliance/wasmtime/pull/5567) - -* The Wasmtime C API now has `WASMTIME_VERSION_*` macros defined in its header - files. - [#5651](https://github.com/bytecodealliance/wasmtime/pull/5651) - -* The `wasmtime` CLI executable as part of Wasmtime's precompiled release - artifacts now has the `all-arch` feature enabled. - [#5657](https://github.com/bytecodealliance/wasmtime/pull/5657) - -### Changed - -* Equality of `wasmtime::component::Val::Float{32,64}` now considers NaNs as - equal for assistance when fuzzing. - [#5535](https://github.com/bytecodealliance/wasmtime/pull/5535) - -* WIT syntax supported by `wasmtime::component::bindgen!` has been updated in - addition to the generated code being updated. - [#5565](https://github.com/bytecodealliance/wasmtime/pull/5565) - [#5692](https://github.com/bytecodealliance/wasmtime/pull/5692) - [#5694](https://github.com/bytecodealliance/wasmtime/pull/5694) - -* Cranelift's egraph-based optimization framework is now enabled by default. - [#5587](https://github.com/bytecodealliance/wasmtime/pull/5587) - -* The old `PoolingAllocationStrategy` type has been removed in favor of a more - flexible configuration via a new option - `PoolingAllocationConfig::max_unused_warm_slots` which is more flexible and - subsumes the previous use cases for each strategy. - [#5661](https://github.com/bytecodealliance/wasmtime/pull/5661) - -* Creation of `InstancePre` through `Linker::instantiate_pre` no longer requires - a `Store` to be provided. Instead a `Store`-related argument is now required - on `Linker::define`-style APIs instead. - [#5683](https://github.com/bytecodealliance/wasmtime/pull/5683) - -### Fixed - -* Compilation for FreeBSD on x86\_64 and AArch64 has been fixed. - [#5606](https://github.com/bytecodealliance/wasmtime/pull/5606) - --------------------------------------------------------------------------------- - -## 5.0.1 - -Released 2023-03-08. - -### Fixed - -* Guest-controlled out-of-bounds read/write on x86\_64 - [GHSA-ff4p-7xrq-q5r8](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ff4p-7xrq-q5r8) - -* Miscompilation of `i8x16.select` with the same inputs on x86\_64 - [GHSA-xm67-587q-r2vw](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-xm67-587q-r2vw) - --------------------------------------------------------------------------------- - -## 5.0.0 - -Released 2023-01-20 - -### Added - -* A `wasmtime::component::bingen!` macro has been added for generating bindings - from `*.wit` files. Note that WIT is still heavily in development so this is - more of a preview of what will be as opposed to a finished feature. - [#5317](https://github.com/bytecodealliance/wasmtime/pull/5317) - [#5397](https://github.com/bytecodealliance/wasmtime/pull/5397) - -* The `wasmtime settings` CLI command now has a `--json` option for - machine-readable output. - [#5411](https://github.com/bytecodealliance/wasmtime/pull/5411) - -* Wiggle-generated bindings can now generate the trait for either `&mut self` or - `&self`. - [#5428](https://github.com/bytecodealliance/wasmtime/pull/5428) - -* The `wiggle` crate has more convenience APIs for working with guest data - that resides in shared memory. - [#5471](https://github.com/bytecodealliance/wasmtime/pull/5471) - [#5475](https://github.com/bytecodealliance/wasmtime/pull/5475) - -### Changed - -* Cranelift's egraph support has been rewritten and updated. This functionality - is still gated behind a flag and may become the default in the next release. - [#5382](https://github.com/bytecodealliance/wasmtime/pull/5382) - -* The implementation of codegen for WebAssembly linear memory has changed - significantly internally in Cranelift, moving more responsibility to the - Wasmtime embedding rather than Cranelift itself. This should have no - user-visible change, however. - [#5386](https://github.com/bytecodealliance/wasmtime/pull/5386) - -* The `Val::Float32` and `Val::Float64` variants for components now store `f32` - and `f64` instead of the bit representation. - [#5510](https://github.com/bytecodealliance/wasmtime/pull/5510) - -### Fixed - -* Handling of DWARF debugging information in components with multiple modules - has been fixed to ensure the right info is used for each module. - [#5358](https://github.com/bytecodealliance/wasmtime/pull/5358) - --------------------------------------------------------------------------------- - -## 4.0.1 - -Released 2023-03-08. - -### Fixed - -* Guest-controlled out-of-bounds read/write on x86\_64 - [GHSA-ff4p-7xrq-q5r8](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ff4p-7xrq-q5r8) - -* Miscompilation of `i8x16.select` with the same inputs on x86\_64 - [GHSA-xm67-587q-r2vw](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-xm67-587q-r2vw) - --------------------------------------------------------------------------------- - -## 4.0.0 - -Released 2022-12-20 - -### Added - -* Dynamic memories are now supported with the pooling instance allocator which - can possibly reduce the number of page faults throughout execution at the cost - of slower to run code. Page faults are primarily reduced by avoiding - releasing memory back to the system, relying on bounds checks to keep the - memory inaccessible. - [#5208](https://github.com/bytecodealliance/wasmtime/pull/5208) - -* The `wiggle` generator now supports function-level control over `tracing` - calls. - [#5194](https://github.com/bytecodealliance/wasmtime/pull/5194) - -* Support has been added to `wiggle` to be compatible with shared memories. - [#5225](https://github.com/bytecodealliance/wasmtime/pull/5225) - [#5229](https://github.com/bytecodealliance/wasmtime/pull/5229) - [#5264](https://github.com/bytecodealliance/wasmtime/pull/5264) - [#5268](https://github.com/bytecodealliance/wasmtime/pull/5268) - [#5054](https://github.com/bytecodealliance/wasmtime/pull/5054) - -* The `wiggle` generator now supports a "trappable error" configuration to - improve error conversions to guest errors and ensure that no host errors are - forgotten or accidentally become traps. The `wasi-common` crate has been - updated to use this. - [#5276](https://github.com/bytecodealliance/wasmtime/pull/5276) - [#5279](https://github.com/bytecodealliance/wasmtime/pull/5279) - -* The `memory.atomic.{notify,wait32,wait64}` instructions are now all - implemented in Wasmtime. - [#5255](https://github.com/bytecodealliance/wasmtime/pull/5255) - [#5311](https://github.com/bytecodealliance/wasmtime/pull/5311) - -* A `wasm_config_parallel_compilation_set` configuration function has been added - to the C API. - [#5298](https://github.com/bytecodealliance/wasmtime/pull/5298) - -* The `wasmtime` CLI can have its input module piped into it from stdin now. - [#5342](https://github.com/bytecodealliance/wasmtime/pull/5342) - -* `WasmBacktrace::{capture,force_capture}` methods have been added to - programmatically capture a backtrace outside of a trapping context. - [#5341](https://github.com/bytecodealliance/wasmtime/pull/5341) - -### Changed - -* The `S` type parameter on `Func::typed` and `Instance::get_typed_func` has - been removed and no longer needs to be specified. - [#5275](https://github.com/bytecodealliance/wasmtime/pull/5275) - -* The `SharedMemory::data` method now returns `&[UnsafeCell]` instead of the - prior raw slice return. - [#5240](https://github.com/bytecodealliance/wasmtime/pull/5240) - -* Creation of a `WasiCtx` will no longer unconditionally acquire randomness from - the OS, instead using the `rand::thread_rng()` function in Rust which is only - periodically reseeded with randomness from the OS. - [#5244](https://github.com/bytecodealliance/wasmtime/pull/5244) - -* Codegen of dynamically-bounds-checked wasm memory accesses has been improved. - [#5190](https://github.com/bytecodealliance/wasmtime/pull/5190) - -* Wasmtime will now emit inline stack probes in generated functions for x86\_64, - aarch64, and riscv64 architectures. This guarantees a process abort if an - engine was misconfigured to give wasm too much stack instead of optionally - allowing wasm to skip the guard page. - [#5350](https://github.com/bytecodealliance/wasmtime/pull/5350) - [#5353](https://github.com/bytecodealliance/wasmtime/pull/5353) - -### Fixed - -* Dropping a `Module` will now release kernel resources in-use by the pooling - allocator when enabled instead of waiting for a new instance to be - re-instantiated into prior slots. - [#5321](https://github.com/bytecodealliance/wasmtime/pull/5321) - --------------------------------------------------------------------------------- - -## 3.0.1 - -Released 2022-12-01. - -### Fixed - -* The instruction cache is now flushed for AArch64 Android. - [#5331](https://github.com/bytecodealliance/wasmtime/pull/5331) - -* Building for FreeBSD and Android has been fixed. - [#5323](https://github.com/bytecodealliance/wasmtime/pull/5323) - --------------------------------------------------------------------------------- - -## 3.0.0 - -Released 2022-11-21 - -### Added - -* New `WasiCtx::{push_file, push_dir}` methods exist for embedders to add their - own objects. - [#5027](https://github.com/bytecodealliance/wasmtime/pull/5027) - -* Wasmtime's `component-model` support now supports `async` host functions and - embedding in the same manner as core wasm. - [#5055](https://github.com/bytecodealliance/wasmtime/pull/5055) - -* The `wasmtime` CLI executable now supports a `--max-wasm-stack` flag. - [#5156](https://github.com/bytecodealliance/wasmtime/pull/5156) - -* AOT compilation support has been implemented for components (aka the - `component-model` feature of the Wasmtime crate). - [#5160](https://github.com/bytecodealliance/wasmtime/pull/5160) - -* A new `wasi_config_set_stdin_bytes` function is available in the C API to set - the stdin of a WASI-using module from an in-memory slice. - [#5179](https://github.com/bytecodealliance/wasmtime/pull/5179) - -* When using the pooling allocator there are now options to reset memory with - `memset` instead of `madvisev` on Linux to keep pages resident in memory to - reduce page faults when reusing linear memory slots. - [#5207](https://github.com/bytecodealliance/wasmtime/pull/5207) - -### Changed - -* Consuming 0 fuel with 0 fuel left is now considered to succeed. Additionally a - store may not consume its last unit of fuel. - [#5013](https://github.com/bytecodealliance/wasmtime/pull/5013) - -* A number of variants in the `wasi_common::ErrorKind` enum have been removed. - [#5015](https://github.com/bytecodealliance/wasmtime/pull/5015) - -* Methods on `WasiDir` now error-by-default instead of requiring a definition by - default. - [#5019](https://github.com/bytecodealliance/wasmtime/pull/5019) - -* Bindings generated by the `wiggle` crate now always depend on the `wasmtime` - crate meaning crates like `wasi-common` no longer compile for platforms such - as `wasm32-unknown-emscripten`. - [#5137](https://github.com/bytecodealliance/wasmtime/pull/5137) - -* Error handling in the `wasmtime` crate's API has been changed to primarily - work with `anyhow::Error` for custom errors. The `Trap` type has been replaced - with a simple `enum Trap { ... }` and backtrace information is now stored as a - `WasmBacktrace` type inserted as context into an `anyhow::Error`. - Host-functions are expected to return `anyhow::Result` instead of the prior - `Trap` error return from before. Additionally the old `Trap::i32_exit` - constructor is now a concrete `wasi_commont::I32Exit` type which can be tested - for with a `downcast_ref` on the error returned from Wasmtime. - [#5149](https://github.com/bytecodealliance/wasmtime/pull/5149) - -* Configuration of the pooling allocator is now done through a builder-style - `PoolingAllocationConfig` API instead of the prior enum-variant API. - [#5205](https://github.com/bytecodealliance/wasmtime/pull/5205) - -### Fixed - -* The instruction cache is now properly flushed for AArch64 on Windows. - [#4997](https://github.com/bytecodealliance/wasmtime/pull/4997) - -* Backtrace capturing with many sequences of wasm->host calls on the stack no - longer exhibit quadratic capturing behavior. - [#5049](https://github.com/bytecodealliance/wasmtime/pull/5049) - --------------------------------------------------------------------------------- - -## 2.0.2 - -Released 2022-11-10. - -### Fixed - -* [CVE-2022-39392] - modules may perform out-of-bounds reads/writes when the - pooling allocator was configured with `memory_pages: 0`. - -* [CVE-2022-39393] - data can be leaked between instances when using the pooling - allocator. - -* [CVE-2022-39394] - An incorrect Rust signature for the C API - `wasmtime_trap_code` function could lead to an out-of-bounds write of three - zero bytes. - -[CVE-2022-39392]: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-44mr-8vmm-wjhg -[CVE-2022-39393]: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-wh6w-3828-g9qf -[CVE-2022-39394]: https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-h84q-m8rr-3v9q - --------------------------------------------------------------------------------- - -## 2.0.1 - -Released 2022-10-27. - -### Fixed - -* A compilation error when building only the `wasmtime` crate on Windows with - only the default features enabled has been fixed. - [#5134](https://github.com/bytecodealliance/wasmtime/pull/5134) - -### Changed - -* The `rayon` dependency added to `cranelift-isle` in 2.0.0 has been removed to - improve the compile time of the `cranelift-codegen` crate. - [#5101](https://github.com/bytecodealliance/wasmtime/pull/5101) - --------------------------------------------------------------------------------- - -## 2.0.0 - -Released 2022-10-20 - -### Added - -* Cranelift has gained support for forward-edge CFI on the AArch64 backend. - [#3693](https://github.com/bytecodealliance/wasmtime/pull/3693) - -* A `--disable-parallel-compilation` CLI flag is now implemented for `wasmtime`. - [#4911](https://github.com/bytecodealliance/wasmtime/pull/4911) - -* [Tier 3] support has been added for for RISC-V 64 with a new backend in - Cranelift for this architecture. - [#4271](https://github.com/bytecodealliance/wasmtime/pull/4271) - -* Basic [tier 3] support for Windows ARM64 has been added but features such as - traps don't work at this time. - [#4990](https://github.com/bytecodealliance/wasmtime/pull/4990) - -### Changed - -* The implementation of the `random_get` function in `wasi-common` is now faster - by using a userspace CSPRNG rather than the OS for randomness. - [#4917](https://github.com/bytecodealliance/wasmtime/pull/4917) - -* The AArch64 backend has completed its transition to ISLE. - [#4851](https://github.com/bytecodealliance/wasmtime/pull/4851) - [#4866](https://github.com/bytecodealliance/wasmtime/pull/4866) - [#4898](https://github.com/bytecodealliance/wasmtime/pull/4898) - [#4884](https://github.com/bytecodealliance/wasmtime/pull/4884) - [#4820](https://github.com/bytecodealliance/wasmtime/pull/4820) - [#4913](https://github.com/bytecodealliance/wasmtime/pull/4913) - [#4942](https://github.com/bytecodealliance/wasmtime/pull/4942) - [#4943](https://github.com/bytecodealliance/wasmtime/pull/4943) - -* The size of the `sigaltstack` allocated per-thread for signal handling has - been increased from 16k to 64k. - [#4964](https://github.com/bytecodealliance/wasmtime/pull/4964) - - -[Tier 3]: https://docs.wasmtime.dev/stability-tiers.html - --------------------------------------------------------------------------------- - -## 1.0.2 - -Released 2022-11-10. - -### Fixed - -* [CVE-2022-39392] - modules may perform out-of-bounds reads/writes when the - pooling allocator was configured with `memory_pages: 0`. - -* [CVE-2022-39393] - data can be leaked between instances when using the pooling - allocator. - -* [CVE-2022-39394] - An incorrect Rust signature for the C API - `wasmtime_trap_code` function could lead to an out-of-bounds write of three - zero bytes. - --------------------------------------------------------------------------------- - -## 1.0.1 - -Released 2022-09-26 - -This is a patch release that incorporates a fix for a miscompilation of an -atomic-CAS operator on aarch64. The instruction is not usable from Wasmtime -with default settings, but may be used if the Wasm atomics extension is -enabled. The bug may also be reachable via other uses of Cranelift. Thanks to -@bjorn3 for reporting and debugging this issue! - -### Fixed - -* Fixed a miscompilation of `atomic_cas` on aarch64. The output register was - swapped with a temporary register in the register-allocator constraints. - [#4959](https://github.com/bytecodealliance/wasmtime/pull/4959) - [#4960](https://github.com/bytecodealliance/wasmtime/pull/4960) - --------------------------------------------------------------------------------- - -## 1.0.0 - -Released 2022-09-20 - -This release marks the official 1.0 release of Wasmtime and represents the -culmination of the work amongst over 300 contributors. Wasmtime has been -battle-tested in production through multiple embeddings for quite some time now -and we're confident in releasing a 1.0 version to signify the stability and -quality of the Wasmtime engine. - -More information about Wasmtime's 1.0 release is on the [Bytecode Alliance's -blog][ba-blog] with separate posts on [Wasmtime's performance -features][ba-perf], [Wasmtime's security story][ba-security], and [the 1.0 -release announcement][ba-1.0]. - -As a reminder the 2.0 release of Wasmtime is scheduled for one month from now on -October 20th. For more information see the [RFC on Wasmtime's 1.0 -release][rfc-1.0]. - -[ba-blog]: https://bytecodealliance.org/articles/ -[ba-perf]: https://bytecodealliance.org/articles/wasmtime-10-performance -[ba-security]: https://bytecodealliance.org/articles/security-and-correctness-in-wasmtime -[ba-1.0]: https://bytecodealliance.org/articles/wasmtime-1-0-fast-safe-and-now-production-ready.md -[rfc-1.0]: https://github.com/bytecodealliance/rfcs/blob/main/accepted/wasmtime-one-dot-oh.md - -### Added - -* An incremental compilation cache for Cranelift has been added which can be - enabled with `Config::enable_incremental_compilation`, and this option is - disabled by default for now. The incremental compilation cache has been - measured to improve compile times for cold uncached modules as well due to - some wasm modules having similar-enough functions internally. - [#4551](https://github.com/bytecodealliance/wasmtime/pull/4551) - -* Source tarballs are now available as part of Wasmtime's release artifacts. - [#4294](https://github.com/bytecodealliance/wasmtime/pull/4294) - -* WASI APIs that specify the REALTIME clock are now supported. - [#4777](https://github.com/bytecodealliance/wasmtime/pull/4777) - -* WASI's socket functions are now fully implemented. - [#4776](https://github.com/bytecodealliance/wasmtime/pull/4776) - -* The native call stack for async-executed wasm functions are no longer - automatically reset to zero after the stack is returned to the pool when using - the pooling allocator. A `Config::async_stack_zeroing` option has been added - to restore the old behavior of zero-on-return-to-pool. - [#4813](https://github.com/bytecodealliance/wasmtime/pull/4813) - -* Inline stack probing has been implemented for the Cranelift x64 backend. - [#4747](https://github.com/bytecodealliance/wasmtime/pull/4747) - -### Changed - -* Generating of native unwind information has moved from a - `Config::wasm_backtrace` option to a new `Config::native_unwind_info` option - and is enabled by default. - [#4643](https://github.com/bytecodealliance/wasmtime/pull/4643) - -* The `memory-init-cow` feature is now enabled by default in the C API. - [#4690](https://github.com/bytecodealliance/wasmtime/pull/4690) - -* Back-edge CFI is now enabled by default on AArch64 macOS. - [#4720](https://github.com/bytecodealliance/wasmtime/pull/4720) - -* WASI calls will no longer return NOTCAPABLE in preparation for the removal of - the rights system from WASI. - [#4666](https://github.com/bytecodealliance/wasmtime/pull/4666) - -### Internal - -This section of the release notes shouldn't affect external users since no -public-facing APIs are affected, but serves as a place to document larger -changes internally within Wasmtime. - -* Differential fuzzing has been refactored and improved into one fuzzing target - which can execute against any of Wasmtime itself (configured differently), - wasmi, V8, or the spec interpreter. Fuzzing now executes each exported - function with fuzz-generated inputs and the contents of all of memory and each - exported global is compared after each execution. Additionally more - interesting shapes of modules are also possible to generate. - [#4515](https://github.com/bytecodealliance/wasmtime/pull/4515) - [#4735](https://github.com/bytecodealliance/wasmtime/pull/4735) - [#4737](https://github.com/bytecodealliance/wasmtime/pull/4737) - [#4739](https://github.com/bytecodealliance/wasmtime/pull/4739) - [#4774](https://github.com/bytecodealliance/wasmtime/pull/4774) - [#4773](https://github.com/bytecodealliance/wasmtime/pull/4773) - [#4845](https://github.com/bytecodealliance/wasmtime/pull/4845) - [#4672](https://github.com/bytecodealliance/wasmtime/pull/4672) - [#4674](https://github.com/bytecodealliance/wasmtime/pull/4674) - -* The x64 backend for Cranelift has been fully migrated to ISLE. - [#4619](https://github.com/bytecodealliance/wasmtime/pull/4619) - [#4625](https://github.com/bytecodealliance/wasmtime/pull/4625) - [#4645](https://github.com/bytecodealliance/wasmtime/pull/4645) - [#4650](https://github.com/bytecodealliance/wasmtime/pull/4650) - [#4684](https://github.com/bytecodealliance/wasmtime/pull/4684) - [#4704](https://github.com/bytecodealliance/wasmtime/pull/4704) - [#4718](https://github.com/bytecodealliance/wasmtime/pull/4718) - [#4726](https://github.com/bytecodealliance/wasmtime/pull/4726) - [#4722](https://github.com/bytecodealliance/wasmtime/pull/4722) - [#4729](https://github.com/bytecodealliance/wasmtime/pull/4729) - [#4730](https://github.com/bytecodealliance/wasmtime/pull/4730) - [#4741](https://github.com/bytecodealliance/wasmtime/pull/4741) - [#4763](https://github.com/bytecodealliance/wasmtime/pull/4763) - [#4772](https://github.com/bytecodealliance/wasmtime/pull/4772) - [#4780](https://github.com/bytecodealliance/wasmtime/pull/4780) - [#4787](https://github.com/bytecodealliance/wasmtime/pull/4787) - [#4793](https://github.com/bytecodealliance/wasmtime/pull/4793) - [#4809](https://github.com/bytecodealliance/wasmtime/pull/4809) - -* The AArch64 backend for Cranelift has seen significant progress in being - ported to ISLE. - [#4608](https://github.com/bytecodealliance/wasmtime/pull/4608) - [#4639](https://github.com/bytecodealliance/wasmtime/pull/4639) - [#4634](https://github.com/bytecodealliance/wasmtime/pull/4634) - [#4748](https://github.com/bytecodealliance/wasmtime/pull/4748) - [#4750](https://github.com/bytecodealliance/wasmtime/pull/4750) - [#4751](https://github.com/bytecodealliance/wasmtime/pull/4751) - [#4753](https://github.com/bytecodealliance/wasmtime/pull/4753) - [#4788](https://github.com/bytecodealliance/wasmtime/pull/4788) - [#4796](https://github.com/bytecodealliance/wasmtime/pull/4796) - [#4785](https://github.com/bytecodealliance/wasmtime/pull/4785) - [#4819](https://github.com/bytecodealliance/wasmtime/pull/4819) - [#4821](https://github.com/bytecodealliance/wasmtime/pull/4821) - [#4832](https://github.com/bytecodealliance/wasmtime/pull/4832) - -* The s390x backend has seen improvements and additions to fully support the - Cranelift backend for rustc. - [#4682](https://github.com/bytecodealliance/wasmtime/pull/4682) - [#4702](https://github.com/bytecodealliance/wasmtime/pull/4702) - [#4616](https://github.com/bytecodealliance/wasmtime/pull/4616) - [#4680](https://github.com/bytecodealliance/wasmtime/pull/4680) - -* Significant improvements have been made to Cranelift-based fuzzing with more - supported features and more instructions being fuzzed. - [#4589](https://github.com/bytecodealliance/wasmtime/pull/4589) - [#4591](https://github.com/bytecodealliance/wasmtime/pull/4591) - [#4665](https://github.com/bytecodealliance/wasmtime/pull/4665) - [#4670](https://github.com/bytecodealliance/wasmtime/pull/4670) - [#4590](https://github.com/bytecodealliance/wasmtime/pull/4590) - [#4375](https://github.com/bytecodealliance/wasmtime/pull/4375) - [#4519](https://github.com/bytecodealliance/wasmtime/pull/4519) - [#4696](https://github.com/bytecodealliance/wasmtime/pull/4696) - [#4700](https://github.com/bytecodealliance/wasmtime/pull/4700) - [#4703](https://github.com/bytecodealliance/wasmtime/pull/4703) - [#4602](https://github.com/bytecodealliance/wasmtime/pull/4602) - [#4713](https://github.com/bytecodealliance/wasmtime/pull/4713) - [#4738](https://github.com/bytecodealliance/wasmtime/pull/4738) - [#4667](https://github.com/bytecodealliance/wasmtime/pull/4667) - [#4782](https://github.com/bytecodealliance/wasmtime/pull/4782) - [#4783](https://github.com/bytecodealliance/wasmtime/pull/4783) - [#4800](https://github.com/bytecodealliance/wasmtime/pull/4800) - -* Optimization work on cranelift has continued across various dimensions for - some modest compile-time improvements. - [#4621](https://github.com/bytecodealliance/wasmtime/pull/4621) - [#4701](https://github.com/bytecodealliance/wasmtime/pull/4701) - [#4697](https://github.com/bytecodealliance/wasmtime/pull/4697) - [#4711](https://github.com/bytecodealliance/wasmtime/pull/4711) - [#4710](https://github.com/bytecodealliance/wasmtime/pull/4710) - [#4829](https://github.com/bytecodealliance/wasmtime/pull/4829) - --------------------------------------------------------------------------------- - -## 0.40.0 - -Released 2022-08-20 - -This was a relatively quiet release in terms of user-facing features where most -of the work was around the internals of Wasmtime and Cranelift. Improvements -internally have been made along the lines of: - -* Many more instructions are now implemented with ISLE instead of handwritten - lowerings. -* Many improvements to the cranelift-based fuzzing. -* Many platform improvements for s390x including full SIMD support, running - `rustc_codegen_cranelift` with features like `i128`, supporting more - ABIs, etc. -* Much more of the component model has been implemented and is now fuzzed. - -Finally this release is currently scheduled to be the last `0.*` release of -Wasmtime. The upcoming release of Wasmtime on September 20 is planned to be -Wasmtime's 1.0 release. More information about what 1.0 means for Wasmtime is -available in the [1.0 RFC] - -[1.0 RFC]: https://github.com/bytecodealliance/rfcs/blob/main/accepted/wasmtime-one-dot-oh.md - -### Added - -* Stack walking has been reimplemented with frame pointers rather than with - native unwind information. This means that backtraces are feasible to capture - in performance-critical environments and in general stack walking is much - faster than before. - [#4431](https://github.com/bytecodealliance/wasmtime/pull/4431) - -* The WebAssembly `simd` proposal is now fully implemented for the s390x - backend. - [#4427](https://github.com/bytecodealliance/wasmtime/pull/4427) - -* Support for AArch64 has been added in the experimental native debuginfo - support that Wasmtime has. - [#4468](https://github.com/bytecodealliance/wasmtime/pull/4468) - -* Support building the C API of Wasmtime with CMake has been added. - [#4369](https://github.com/bytecodealliance/wasmtime/pull/4369) - -* Clarification was added to Wasmtime's documentation about "tiers of support" - for various features. - [#4479](https://github.com/bytecodealliance/wasmtime/pull/4479) - -### Fixed - -* Support for `filestat_get` has been improved for stdio streams in WASI. - [#4531](https://github.com/bytecodealliance/wasmtime/pull/4531) - -* Enabling the `vtune` feature no longer breaks builds on AArch64. - [#4533](https://github.com/bytecodealliance/wasmtime/pull/4533) - --------------------------------------------------------------------------------- - -## 0.39.1 - -Released 2022-07-20. - -### Fixed - -* An s390x-specific codegen bug in addition to a mistake introduced in the fix - of CVE-2022-31146 were fixed. - [#4490](https://github.com/bytecodealliance/wasmtime/pull/4490) - --------------------------------------------------------------------------------- - -## 0.39.0 - -Released 2022-07-20 - -### Added - -* Initial support for shared memories and the `threads` WebAssembly proposal - has been added. Note that this feature is still experimental and not ready - for production use yet. - [#4187](https://github.com/bytecodealliance/wasmtime/pull/4187) - -* A new `Linker::define_unknown_imports_as_traps` method and - `--trap-unknown-imports` CLI flag have been added to conveniently support - running modules with imports that aren't dynamically called at runtime. - [#4312](https://github.com/bytecodealliance/wasmtime/pull/4312) - -* The VTune profiling strategy can now be selected through the C API. - [#4316](https://github.com/bytecodealliance/wasmtime/pull/4316) - -### Changed - -* Some methods on the `Config` structure now return `&mut Self` instead of - `Result<&mut Self>` since the validation is deferred until `Engine::new`: - `profiler`, `cranelift_flag_enable`, `cranelift_flag_set`, `max_wasm_stack`, - `async_stack_size`, and `strategy`. - [#4252](https://github.com/bytecodealliance/wasmtime/pull/4252) - [#4262](https://github.com/bytecodealliance/wasmtime/pull/4262) - -* Parallel compilation of WebAssembly modules is now enabled in the C API by - default. - [#4270](https://github.com/bytecodealliance/wasmtime/pull/4270) - -* Implicit Cargo features of the `wasmtime` introduced through `optional` - dependencies may have been removed since namespaced features are now used. - It's recommended to only used the set of named `[features]` for Wasmtime. - [#4293](https://github.com/bytecodealliance/wasmtime/pull/4293) - -* Register allocation has fixed a few issues related to excessive memory usage - at compile time. - [#4324](https://github.com/bytecodealliance/wasmtime/pull/4324) - -### Fixed - -* A refactor of `Config` was made to fix an issue that the order of calls to `Config` - matters now, which may lead to unexpected behavior. - [#4252](https://github.com/bytecodealliance/wasmtime/pull/4252) - [#4262](https://github.com/bytecodealliance/wasmtime/pull/4262) - -* Wasmtime has been fixed to work on SSE2-only x86\_64 platforms when the - `simd` feature is disabled in `Config`. - [#4231](https://github.com/bytecodealliance/wasmtime/pull/4231) - -* Generation of platform-specific unwinding information is disabled if - `wasm_backtrace` and `wasm_reference_types` are both disabled. - [#4351](https://github.com/bytecodealliance/wasmtime/pull/4351) - --------------------------------------------------------------------------------- - -## 0.38.3 - -Released 2022-07-20. - -### Fixed. - -* An s390x-specific codegen bug in addition to a mistake introduced in the fix - of CVE-2022-31146 were fixed. - [#4491](https://github.com/bytecodealliance/wasmtime/pull/4491) - --------------------------------------------------------------------------------- - -## 0.38.2 - -Released 2022-07-20. - -### Fixed. - -* A miscompilation when handling constant divisors on AArch64 has been fixed. - [CVE-2022-31169](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-7f6x-jwh5-m9r4) - -* A use-after-free possible with accidentally missing stack maps has been fixed. - [CVE-2022-31146](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-5fhj-g3p3-pq9g) - --------------------------------------------------------------------------------- - -## 0.38.1 - -Released 2022-06-27. - -### Fixed. - -* A register allocator bug was fixed that could affect direct users of - Cranelift who use struct-return (`sret`) arguments. The bug had to do with - the handling of physical register constraints in the function prologue. No - impact should be possible for users of Cranelift via the Wasm frontend, - including Wasmtime. - [regalloc2#60](https://github.com/bytecodealliance/regalloc2/pull/60) - [#4333](https://github.com/bytecodealliance/wasmtime/pull/4333) - -* Lowering bugs for the `i8x16.swizzle` and `select`-with-`v128`-inputs - instructions were fixed for the x86\_64 code generator. Note that aarch64 and - s390x are unaffected. - [#4334](https://github.com/bytecodealliance/wasmtime/pull/4334) - -* A bug in the 8-bit lowering of integer division on x86-64 was fixed in - Cranelift that could cause a register allocator panic due to an undefined - value in a register. (The divide instruction does not take a register `rdx` - as a source when 8 bits but the metadata incorrectly claimed it did.) No - impact on Wasm/Wasmtime users, and impact on direct Cranelift embedders - limited to compilation panics. - [#4332](https://github.com/bytecodealliance/wasmtime/pull/4332) - --------------------------------------------------------------------------------- - -## 0.38.0 - -Released 2022-06-21 - -### Added - -* Enabling or disabling NaN canonicalization in generated code is now exposed - through the C API. - [#4154](https://github.com/bytecodealliance/wasmtime/pull/4154) - -* A user-defined callback can now be invoked when an epoch interruption happens - via the `Store::epoch_deadline_callback` API. - [#4152](https://github.com/bytecodealliance/wasmtime/pull/4152) - -* Basic alias analysis with redundant-load elimintation and store-to-load - forwarding optimizations has been added to Cranelift. - [#4163](https://github.com/bytecodealliance/wasmtime/pull/4163) - -### Changed - -* Traps originating from epoch-based interruption are now exposed as - `TrapCode::Interrupt`. - [#4105](https://github.com/bytecodealliance/wasmtime/pull/4105) - -* Binary builds for AArch64 now require glibc 2.17 and for s390x require glibc - 2.16. Previously glibc 2.28 was required. - [#4171](https://github.com/bytecodealliance/wasmtime/pull/4171) - -* The `wasmtime::ValRaw` now has all of its fields listed as private and instead - constructors/accessors are provided for getting at the internal data. - [#4186](https://github.com/bytecodealliance/wasmtime/pull/4186) - -* The `wasm-backtrace` Cargo feature has been removed in favor of a - `Config::wasm_backtrace` runtime configuration option. Additionally backtraces - are now only captured when an embedder-generated trap actually reaches a - WebAssembly call stack. - [#4183](https://github.com/bytecodealliance/wasmtime/pull/4183) - -* Usage of `*_unchecked` APIs for `Func` in the `wasmtime` crate and C API now - take a `usize` parameter indicating the number of `ValRaw` values behind - the associated pointer. - [#4192](https://github.com/bytecodealliance/wasmtime/pull/4192) - -### Fixed - -* An improvement was made to the spill-slot allocation in code generation to fix - an issue where some stack slots accidentally weren't reused. This issue was - introduced with the landing of regalloc2 in 0.37.0 and may have resulted in - larger-than-intended increases in stack frame sizes. - [#4222](https://github.com/bytecodealliance/wasmtime/pull/4222) - --------------------------------------------------------------------------------- - -## 0.37.0 - -Released 2022-05-20 - -### Added - -* Updated Cranelift to use regalloc2, a new register allocator. This should - result in ~20% faster compile times, and for programs that suffered from - register-allocation pressure before, up to ~20% faster generated code. - [#3989](https://github.com/bytecodealliance/wasmtime/pull/3989) - -* Pre-built binaries for macOS M1 machines are now available as release - artifacts. - [#3983](https://github.com/bytecodealliance/wasmtime/pull/3983) - -* Copy-on-write images of memory can now be manually initialized for a `Module` - with an explicit method call, but it is still not required to call this method - and will automatically otherwise happen on the first instantiation. - [#3964](https://github.com/bytecodealliance/wasmtime/pull/3964) - -### Fixed - -* Using `InstancePre::instantiate` or `Linker::instantiate` will now panic as - intended when used with an async-configured `Store`. - [#3972](https://github.com/bytecodealliance/wasmtime/pull/3972) - -### Changed - -* The unsafe `ValRaw` type in the `wasmtime` crate now always stores its values - in little-endian format instead of the prior native-endian format. Users of - `ValRaw` are recommended to audit their existing code for usage to continue - working on big-endian platforms. - [#4035](https://github.com/bytecodealliance/wasmtime/pull/4035) - -### Removed - -* Support for `Config::paged_memory_initialization` and the `uffd` crate feature - have been removed from the `wasmtime` crate. Users should migrate to using - `Config::memory_init_cow` which is more portable and faster at this point. - [#4040](https://github.com/bytecodealliance/wasmtime/pull/4040) - --------------------------------------------------------------------------------- - -## 0.36.0 - -Released 2022-04-20 - -### Added - -* Support for epoch-based interruption has been added to the C API. - [#3925](https://github.com/bytecodealliance/wasmtime/pull/3925) - -* Support for disabling libunwind-based backtraces of WebAssembly code at - compile time has been added. - [#3932](https://github.com/bytecodealliance/wasmtime/pull/3932) - -* Async support for call hooks has been added to optionally execute "blocking" - work whenever a wasm module is entered or exited relative to the host. - [#3876](https://github.com/bytecodealliance/wasmtime/pull/3876) - -### Fixed - -* Loading a `Module` will now check, at runtime, that the compilation settings - enabled in a `Config` are compatible with the native host. For example this - ensures that if avx2 is enabled that the host actually has avx2 support. - [#3899](https://github.com/bytecodealliance/wasmtime/pull/3899) - -### Removed - -* Support for `Config::interruptable` and `InterruptHandle` has been removed - from the `wasmtime` crate. Users should migrate to using epoch-based - interruption instead. - [#3925](https://github.com/bytecodealliance/wasmtime/pull/3925) - -* The module linking implementation of Wasmtime has been removed to make room - for the upcoming support for the component model. - [#3958](https://github.com/bytecodealliance/wasmtime/pull/3958) - --------------------------------------------------------------------------------- - -## 0.35.3 - -Released 2022-04-11. - -### Fixed - -* Backported a bugfix for an instruction lowering issue that could cause a - regalloc panic due to an undefined register in some cases. No miscompilation - was ever possible, but panics would result in a compilation failure. - [#4012](https://github.com/bytecodealliance/wasmtime/pull/4012) - --------------------------------------------------------------------------------- - -## 0.35.2 - -Released 2022-03-31. - -### Security Fixes - -* [CVE-2022-24791](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-gwc9-348x-qwv2): - Fixed a use after free with `externref`s and epoch interruption. - -## 0.35.1 - -Released 2022-03-09. - -### Fixed - -* Fixed a bug in the x86-64 lowering of the `uextend` opcode for narrow (`i8`, - `i16`) integer sources when the value is produced by one of several - arithmetic instructions. - [#3906](https://github.com/bytecodealliance/wasmtime/pull/3906) - -## 0.35.0 - -Released 2022-03-07. - -### Added - -* The `wasmtime_wasi::add_to_linker` function now allows providing - a context object of a custom type instead of `wasmtime_wasi::WasiCtx`, - as long as that type implements the required WASI snapshot traits. - This allows, for example, wrapping `WasiCtx` into a struct and providing - custom implementations for those traits to override the default behaviour. - -### Changed - -* WebAssembly tables of `funcref` values are now lazily initialized which can, - in some cases, greatly speed up instantiation of a module. - [#3733](https://github.com/bytecodealliance/wasmtime/pull/3733) - -* The `memfd` feature in 0.34.0, now renamed to `memory-init-cow`, has been - enabled by default. This means that, where applicable, WebAssembly linear - memories are now initialized with copy-on-write mappings. Support from this - has been expanded from Linux-only to include macOS and other Unix systems when - modules are loaded from precompiled `*.cwasm` files on disk. - [#3777](https://github.com/bytecodealliance/wasmtime/pull/3777) - [#3778](https://github.com/bytecodealliance/wasmtime/pull/3778) - [#3787](https://github.com/bytecodealliance/wasmtime/pull/3787) - [#3819](https://github.com/bytecodealliance/wasmtime/pull/3819) - [#3831](https://github.com/bytecodealliance/wasmtime/pull/3831) - -* Clarify that SSE 4.2 (and prior) is required for running WebAssembly code with - simd support enabled on x86\_64. - [#3816](https://github.com/bytecodealliance/wasmtime/pull/3816) - [#3817](https://github.com/bytecodealliance/wasmtime/pull/3817) - [#3833](https://github.com/bytecodealliance/wasmtime/pull/3833) - [#3825](https://github.com/bytecodealliance/wasmtime/pull/3825) - -* Support for profiling with VTune is now enabled at compile time by default, - but it remains disabled at runtime by default. - [#3821](https://github.com/bytecodealliance/wasmtime/pull/3821) - -* The `ModuleLimits` type has been removed from the configuration of the pooling - allocator in favor of configuring the total size of an instance allocation - rather than each individual field. - [#3837](https://github.com/bytecodealliance/wasmtime/pull/3837) - -* The native stack size allowed for WebAssembly has been decreased from 1 MiB to - 512 KiB on all platforms to better accomodate running wasm on the main thread - on Windows. - [#3861](https://github.com/bytecodealliance/wasmtime/pull/3861) - -* The `wasi-common` crate now supports doing polls for both read and write - interest on a file descriptor at the same time. - [#3866](https://github.com/bytecodealliance/wasmtime/pull/3866) - -### Fixed - -* The `Store::call_hook` callback is now invoked when entering host functions - defined with `*_unchecked` variants. - [#3881](https://github.com/bytecodealliance/wasmtime/pull/3881) - -### Removed - -* The incomplete and unmaintained ARM32 backend has been removed from Cranelift. - [#3799](https://github.com/bytecodealliance/wasmtime/pull/3799) - --------------------------------------------------------------------------------- - -## 0.34.2 - -Released 2022-03-31. - -### Security Fixes - -* [CVE-2022-24791](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-gwc9-348x-qwv2): - Fixed a use after free with `externref`s and epoch interruption. - -## 0.34.1 - -Released 2022-02-16. - -### Security Fixes - -* [CVE-2022-23636](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-88xq-w8cq-xfg7): - Fixed an invalid drop of a partially-initialized instance in the pooling instance - allocator. - -## 0.33.1 - -Released 2022-02-16. - -### Security Fixes - -* [CVE-2022-23636](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-88xq-w8cq-xfg7): - Fixed an invalid drop of a partially-initialized instance in the pooling instance - allocator. - -## 0.34.0 - -Released 2022-02-07. - -### Fixed - -* The `wasi-common` default implementation of some attributes of files has been - updated to ensure that `wasi-libc`'s `isatty` function works as intended. - [#3696](https://github.com/bytecodealliance/wasmtime/pull/3696) - -* A benign debug assertion related to `externref` and garbage-collection has - been fixed. - [#3734](https://github.com/bytecodealliance/wasmtime/pull/3734) - -### Added - -* Function names are now automatically demangled when informing profilers of - regions of JIT code to apply Rust-specific demangling rules if applicable. - [#3683](https://github.com/bytecodealliance/wasmtime/pull/3683) - -* Support for profiling JIT-generated trampolines with VTune has been added. - [#3687](https://github.com/bytecodealliance/wasmtime/pull/3687) - -* Wasmtime now supports a new method of async preemption dubbed "epoch-based - interruption" which is intended to be much more efficient than the current - fuel-based method of preemption. - [#3699](https://github.com/bytecodealliance/wasmtime/pull/3699) - -* On Linux Wasmtime will now by default use copy-on-write mappings to initialize - memories of wasm modules where possible, accelerating instantiation by - avoiding costly memory copies. When combined with the pooling allocator this - can also be used to speed up instance-reuse cases due to fewer syscalls to - change memory mappings being necessary. - [#3697](https://github.com/bytecodealliance/wasmtime/pull/3697) - [#3738](https://github.com/bytecodealliance/wasmtime/pull/3738) - [#3760](https://github.com/bytecodealliance/wasmtime/pull/3760) - -* Wasmtime now supports the recently-added `sock_accept` WASI function. - [#3711](https://github.com/bytecodealliance/wasmtime/pull/3711) - -* Cranelift now has support for specifying blocks as cold. - [#3698](https://github.com/bytecodealliance/wasmtime/pull/3698) - -### Changed - -* Many more instructions for the x64 backend have been migrated to ISLE, - additionally with refactorings to make incorrect lowerings harder to - accidentally write. - [#3653](https://github.com/bytecodealliance/wasmtime/pull/3653) - [#3659](https://github.com/bytecodealliance/wasmtime/pull/3659) - [#3681](https://github.com/bytecodealliance/wasmtime/pull/3681) - [#3686](https://github.com/bytecodealliance/wasmtime/pull/3686) - [#3688](https://github.com/bytecodealliance/wasmtime/pull/3688) - [#3690](https://github.com/bytecodealliance/wasmtime/pull/3690) - [#3752](https://github.com/bytecodealliance/wasmtime/pull/3752) - -* More instructions in the aarch64 backend are now lowered with ISLE. - [#3658](https://github.com/bytecodealliance/wasmtime/pull/3658) - [#3662](https://github.com/bytecodealliance/wasmtime/pull/3662) - -* The s390x backend's lowering rules are now almost entirely defined with ISLE. - [#3702](https://github.com/bytecodealliance/wasmtime/pull/3702) - [#3703](https://github.com/bytecodealliance/wasmtime/pull/3703) - [#3706](https://github.com/bytecodealliance/wasmtime/pull/3706) - [#3717](https://github.com/bytecodealliance/wasmtime/pull/3717) - [#3723](https://github.com/bytecodealliance/wasmtime/pull/3723) - [#3724](https://github.com/bytecodealliance/wasmtime/pull/3724) - -* Instantiation of modules in Wasmtime has been further optimized now that the - copy-on-write memory initialization removed the previously most-expensive part - of instantiating a module. - [#3727](https://github.com/bytecodealliance/wasmtime/pull/3727) - [#3739](https://github.com/bytecodealliance/wasmtime/pull/3739) - [#3741](https://github.com/bytecodealliance/wasmtime/pull/3741) - [#3742](https://github.com/bytecodealliance/wasmtime/pull/3742) - --------------------------------------------------------------------------------- - -## 0.33.0 - -Released 2022-01-05. - -### Added - -* Compiled wasm modules may now optionally omit debugging information about - mapping addresses to source locations, resulting in smaller binaries. - [#3598](https://github.com/bytecodealliance/wasmtime/pull/3598) - -* The WebAssembly SIMD proposal is now enabled by default. - [#3601](https://github.com/bytecodealliance/wasmtime/pull/3601) - --------------------------------------------------------------------------------- - -## 0.32.1 - -Released 2022-01-04. - -### Fixed - -* Cranelift: remove recently-added build dependency on `sha2` to allow usage in - some dependency-sensitive environments, by computing ISLE manifest hashes - with a different hash function. - [#3619](https://github.com/bytecodealliance/wasmtime/pull/3619) - -* Cranelift: fixed 8- and 16-bit behavior of popcount (bit population count) - instruction. Does not affect Wasm frontend. - [#3617](https://github.com/bytecodealliance/wasmtime/pull/3617) - -* Cranelift: fixed miscompilation of 8- and 16-bit bit-rotate instructions. - Does not affect Wasm frontend. - [#3610](https://github.com/bytecodealliance/wasmtime/pull/3610) - --------------------------------------------------------------------------------- - -## 0.32.0 - -Released 2021-12-13. - -### Added - -* A new configuration option has been added to force using a "static" memory - style to automatically limit growth of memories in some configurations. - [#3503](https://github.com/bytecodealliance/wasmtime/pull/3503) - -* The `InstancePre` type now implements `Clone`. - [#3510](https://github.com/bytecodealliance/wasmtime/pull/3510) - -* Cranelift's instruction selection process has begun to be migrated towards the - ISLE compiler and definition language. - [#3506](https://github.com/bytecodealliance/wasmtime/pull/3506) - -* A `pooling-allocator` feature has been added, which is on-by-default, to - disable the pooling allocator at compile time. - [#3514](https://github.com/bytecodealliance/wasmtime/pull/3514) - -### Fixed - -* A possible panic when parsing a WebAssembly `name` section has been fixed. - [#3509](https://github.com/bytecodealliance/wasmtime/pull/3509) - -* Generating native DWARF information for some C-produced modules has been - fixed, notably those where there may be DWARF about dead code. - [#3498](https://github.com/bytecodealliance/wasmtime/pull/3498) - -* A number of SIMD code generation bugs have been fixed in the x64 backend - by migrating their lowerings to ISLE. - --------------------------------------------------------------------------------- - -## 0.31.0 - -Released 2021-10-29. - -### Added - -* New `Func::new_unchecked` and `Func::call_unchecked` APIs have been added with - accompanying functions in the C API to improve the performance of calls into - wasm and the host in the C API. - [#3350](https://github.com/bytecodealliance/wasmtime/pull/3350) - -* Release binaries are now available for the s390x-unknown-linux-gnu - architecture. - [#3372](https://github.com/bytecodealliance/wasmtime/pull/3372) - -* A new `ResourceLimiterAsync` trait is added which allows asynchronous blocking - of WebAssembly on instructions such as `memory.grow`. - [#3393](https://github.com/bytecodealliance/wasmtime/pull/3393) - -### Changed - -* The `Func::call` method now takes a slice to write the results into rather - than returning a boxed slice. - [#3319](https://github.com/bytecodealliance/wasmtime/pull/3319) - -* Trampolines are now covered when jitdump profiling is enabled. - [#3344](https://github.com/bytecodealliance/wasmtime/pull/3344) - -### Fixed - -* Debugging with GDB has been fixed on Windows. - [#3373](https://github.com/bytecodealliance/wasmtime/pull/3373) - -* Some quadradic behavior in Wasmtime's compilation of modules has been fixed. - [#3469](https://github.com/bytecodealliance/wasmtime/pull/3469) - [#3466](https://github.com/bytecodealliance/wasmtime/pull/3466) - -* Bounds-checks for wasm memory accesses in certain non-default configurations - have been fixed to correctly allow loads at the end of the address space. - [#3462](https://github.com/bytecodealliance/wasmtime/pull/3462) - -* When type-checking memories and tables for satisfying instance imports the - runtime size of the table/memory is now consulted instead of the object's - original type. - [#3450](https://github.com/bytecodealliance/wasmtime/pull/3450) - -### Removed - -* The Lightbeam backend has been removed, as per [RFC 14]. - [#3390](https://github.com/bytecodealliance/wasmtime/pull/3390) - -[RFC 14]: https://github.com/bytecodealliance/rfcs/pull/14 - -* Cranelift's old x86 backend has been removed, as per [RFC 12]. - [#3309](https://github.com/bytecodealliance/wasmtime/pull/3009) - -[RFC 12]: https://github.com/bytecodealliance/rfcs/pull/12 - -## 0.30.0 - -Released 2021-09-17. - -### Security Fixes - -* [CVE-2021-39216](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-v4cp-h94r-m7xf): - Fixed a use after free passing `externref`s to Wasm in Wasmtime. - -* [CVE-2021-39218](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-4873-36h9-wv49): - Fixed an out-of-bounds read/write and invalid free with `externref`s and GC - safepoints in Wasmtime. - -* [CVE-2021-39219](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-q879-9g95-56mx): - Fixed a bug where using two different `Engine`s with the same `Linker`-define - functions caused unsafety without `unsafe` blocks. - -### Added - -* Added experimental support for the in-progress 64-bit memories Wasm proposal. - -* Added support to build Wasmtime without the compiler. This lets you run - pre-compiled Wasm modules, without the ability (or potential attack surface) - of compiling new Wasm modules. The compilation functionality is gated by the - on-by-default `cranelift` cargo feature. - -* Added support for NaN canonicalization with SIMD vectors. - -* Added support for differential fuzzing against V8's Wasm engine. - -* Added support for fuzzing against the Wasm spec interpreter. - -* Enabled SIMD fuzzing on oss-fuzz. - -### Changed - -* A variety of performance improvements to loading pre-compiled modules. - -* A variety of performance improvements to function calls, both through Rust and - the C API. - -* Leaf functions that do not use the stack no longer bump the frame pointer on - aarch64 and s390x. - -* Many updates and expanded instruction support to the in-progress CLIF - interpreter. - -* Expanded fuzzing of reference types and GC. - -### Fixed - -* A number of fixes to both aarch64 and x86_64 support for the Wasm SIMD - proposal and the underlying CLIF vector instructions. - -* Fixed a potential infinite loop in the SSA computation for - `cranelift-frontend`. This was not reachable from `cranelift-wasm` or - Wasmtime, but might have affected general Cranelift users. - -### Removed - -* The `wasmtime wasm2obj` subcommand has been removed. Generating raw object - files for linking natively is no longer supported. Use the `wasmtime compile` - subcommand to pre-compile a Wasm module and `wasmtime run` to run pre-compiled - Wasm modules. - -## 0.29.0 - -Released 2021-08-02. - -### Changed - -* Instance exports are now loaded lazily from instances instead of eagerly as - they were before. This is an internal-only change and is not a breaking - change. - [#2984](https://github.com/bytecodealliance/wasmtime/pull/2984) - -* All linear memories created by Wasmtime will now, by default, have guard pages - in front of them in addition to after them. This is intended to help mitigate - future bugs in Cranelift, should they arise. - [#2977](https://github.com/bytecodealliance/wasmtime/pull/2977) - -* Linear memories now correctly support a maximum size of 4GB. Previously, the - limit field was 32 bits, which did not properly support a full 4GB memory. - This update is also a necessary change in preparation for future memory64 - support. - [#3013](https://github.com/bytecodealliance/wasmtime/pull/3013) - [#3134](https://github.com/bytecodealliance/wasmtime/pull/3134) - -* Injection counts of fuel into a `wasmtime::Store` now uses a u64 instead of a - u32. - [#3048](https://github.com/bytecodealliance/wasmtime/pull/3048) - -### Added - -* Support for `i128` has improved in the AArch64 backend. - [#2959](https://github.com/bytecodealliance/wasmtime/pull/2959) - [#2975](https://github.com/bytecodealliance/wasmtime/pull/2975) - [#2985](https://github.com/bytecodealliance/wasmtime/pull/2985) - [#2990](https://github.com/bytecodealliance/wasmtime/pull/2990) - [#3002](https://github.com/bytecodealliance/wasmtime/pull/3002) - [#3004](https://github.com/bytecodealliance/wasmtime/pull/3004) - [#3005](https://github.com/bytecodealliance/wasmtime/pull/3005) - [#3008](https://github.com/bytecodealliance/wasmtime/pull/3008) - [#3027](https://github.com/bytecodealliance/wasmtime/pull/3027) - -* The s390x backend now supports z14 and atomics. - [#2988](https://github.com/bytecodealliance/wasmtime/pull/2988) - [#2991](https://github.com/bytecodealliance/wasmtime/pull/2991) - -* The `wasmtime::Linker` type now implements `Clone`. - [#2993](https://github.com/bytecodealliance/wasmtime/pull/2993) - -* Support for the SIMD proposal on both x86\_64 and AArch64 has improved. On - x86\_64, all SIMD opcodes are now supported. - [#2997](https://github.com/bytecodealliance/wasmtime/pull/2997) - [#3035](https://github.com/bytecodealliance/wasmtime/pull/3035) - [#2982](https://github.com/bytecodealliance/wasmtime/pull/2982) - [#3084](https://github.com/bytecodealliance/wasmtime/pull/3084) - [#3082](https://github.com/bytecodealliance/wasmtime/pull/3082) - [#3107](https://github.com/bytecodealliance/wasmtime/pull/3107) - [#3105](https://github.com/bytecodealliance/wasmtime/pull/3105) - [#3114](https://github.com/bytecodealliance/wasmtime/pull/3114) - [#3070](https://github.com/bytecodealliance/wasmtime/pull/3070) - [#3126](https://github.com/bytecodealliance/wasmtime/pull/3126) - -* A `Trap` can now display its reason without also displaying the backtrace. - [#3033](https://github.com/bytecodealliance/wasmtime/pull/3033) - -* An initiall fuzzer for CLIF has been added. - [#3038](https://github.com/bytecodealliance/wasmtime/pull/3038) - -* High-level architecture documentation has been added for Wasmtime. - [#3019](https://github.com/bytecodealliance/wasmtime/pull/3019) - -* Support for multi-memory can now be configured in Wasmtime's C API. - [#3071](https://github.com/bytecodealliance/wasmtime/pull/3071) - -* The `wasmtime` crate now supports a `posix-signals-on-macos` feature to force - the usage of signals instead of mach ports to handle traps on macOS. - [#3063](https://github.com/bytecodealliance/wasmtime/pull/3063) - -* Wasmtime's C API now has a `wasmtime_trap_code` function to get the raw trap - code, if present, for a trap. - [#3086](https://github.com/bytecodealliance/wasmtime/pull/3086) - -* Wasmtime's C API now has a `wasmtime_linker_define_func` function to define a - store-independent function within a linker. - [#3122](https://github.com/bytecodealliance/wasmtime/pull/3122) - -* A `wasmtime::Linker::module_async` function was added as the asynchronous - counterpart to `wasmtime::Linker::module`. - [#3121](https://github.com/bytecodealliance/wasmtime/pull/3121) - -### Fixed - -* Compiling the `wasmtime` crate into a `dylib` crate type has been fixed. - [#3010](https://github.com/bytecodealliance/wasmtime/pull/3010) - -* The enter/exit hooks for WebAssembly are now executed for an instance's - `start` function, if present. - [#3001](https://github.com/bytecodealliance/wasmtime/pull/3001) - -* Some WASI functions in `wasi-common` have been fixed for big-endian platforms. - [#3016](https://github.com/bytecodealliance/wasmtime/pull/3016) - -* Wasmtime no longer erroneously assumes that all custom sections may contain - DWARF information, reducing instances of `Trap`'s `Display` implementation - providing misleading information to set an env var to get more information. - [#3083](https://github.com/bytecodealliance/wasmtime/pull/3083) - -* Some issues with parsing DWARF debug information have been fixed. - [#3116](https://github.com/bytecodealliance/wasmtime/pull/3116) - -## 0.28.0 - -Released 2021-06-09. - -### Changed - -* Breaking: Wasmtime's embedding API has been redesigned, as specified in [RFC - 11]. Rust users can now enjoy easier times with `Send` and `Sync`, and all - users can now more clearly manage memory, especially in the C API. Language - embeddings have been updated to the new API as well. - [#2897](https://github.com/bytecodealliance/wasmtime/pull/2897) - -[RFC 11]: https://github.com/bytecodealliance/rfcs/pull/11 - -### Added - -* A new `InstancePre` type, created with `Linker::instantiate_pre`, has been - added to perform type-checking of an instance once and reduce the work done - for each instantiation of a module: - [#2962](https://github.com/bytecodealliance/wasmtime/pull/2962) - -* Deserialization of a module can now optionally skip checking the wasmtime - version string: - [#2945](https://github.com/bytecodealliance/wasmtime/pull/2945) - -* A method has been exposed to frontload per-thread initialization costs if the - latency of every last wasm call is important: - [#2946](https://github.com/bytecodealliance/wasmtime/pull/2946) - -* Hooks have been added for entry/exit into wasm code to allow embeddings to - track time and other properties about execution in a wasm environment: - [#2952](https://github.com/bytecodealliance/wasmtime/pull/2952) - -* A [C++ embedding of Wasmtime has been written][cpp]. - -[RFC 11]: https://github.com/bytecodealliance/rfcs/pull/11 -[cpp]: https://github.com/bytecodealliance/wasmtime-cpp - -### Fixed - -* Multiple returns on macOS AArch64 have been fixed: - [#2956](https://github.com/bytecodealliance/wasmtime/pull/2956) - -## 0.27.0 - -Released 2021-05-21. - -### Security Fixes - -* Fixed a security issue in Cranelift's x64 backend that could result in a heap - sandbox escape due to an incorrect sign-extension: - [#2913](https://github.com/bytecodealliance/wasmtime/issues/2913). - -### Added - -* Support for IBM z/Archiecture (`s390x`) machines in Cranelift and Wasmtime: - [#2836](https://github.com/bytecodealliance/wasmtime/pull/2836), - [#2837](https://github.com/bytecodealliance/wasmtime/pull/2837), - [#2838](https://github.com/bytecodealliance/wasmtime/pull/2838), - [#2843](https://github.com/bytecodealliance/wasmtime/pull/2843), - [#2854](https://github.com/bytecodealliance/wasmtime/pull/2854), - [#2870](https://github.com/bytecodealliance/wasmtime/pull/2870), - [#2871](https://github.com/bytecodealliance/wasmtime/pull/2871), - [#2872](https://github.com/bytecodealliance/wasmtime/pull/2872), - [#2874](https://github.com/bytecodealliance/wasmtime/pull/2874). - -* Improved async support in wasi-common runtime: - [#2832](https://github.com/bytecodealliance/wasmtime/pull/2832). - -* Added `Store::with_limits`, `StoreLimits`, and `ResourceLimiter` to the - Wasmtime API to help with enforcing resource limits at runtime. The - `ResourceLimiter` trait can be implemented by custom resource limiters to - decide if linear memories or tables can be grown. - -* Added `allow-unknown-exports` option for the run command: - [#2879](https://github.com/bytecodealliance/wasmtime/pull/2879). - -* Added API to notify that a `Store` has moved to a new thread: - [#2822](https://github.com/bytecodealliance/wasmtime/pull/2822). - -* Documented guidance around using Wasmtime in multithreaded contexts: - [#2812](https://github.com/bytecodealliance/wasmtime/pull/2812). - In the future, the Wasmtime API will change to allow some of its core types - to be Send/Sync; see the in-progress - [#2897](https://github.com/bytecodealliance/wasmtime/pull/2897) for details. - -* Support calls from native code to multiple-return-value functions: - [#2806](https://github.com/bytecodealliance/wasmtime/pull/2806). - -### Changed - -* Breaking: `Memory::new` has been changed to return `Result` as creating a - host memory object is now a fallible operation when the initial size of - the memory exceeds the store limits. - -### Fixed - -* Many instruction selection improvements on x64 and aarch64: - [#2819](https://github.com/bytecodealliance/wasmtime/pull/2819), - [#2828](https://github.com/bytecodealliance/wasmtime/pull/2828), - [#2823](https://github.com/bytecodealliance/wasmtime/pull/2823), - [#2862](https://github.com/bytecodealliance/wasmtime/pull/2862), - [#2886](https://github.com/bytecodealliance/wasmtime/pull/2886), - [#2889](https://github.com/bytecodealliance/wasmtime/pull/2889), - [#2905](https://github.com/bytecodealliance/wasmtime/pull/2905). - -* Improved performance of Wasmtime runtime substantially: - [#2811](https://github.com/bytecodealliance/wasmtime/pull/2811), - [#2818](https://github.com/bytecodealliance/wasmtime/pull/2818), - [#2821](https://github.com/bytecodealliance/wasmtime/pull/2821), - [#2847](https://github.com/bytecodealliance/wasmtime/pull/2847), - [#2900](https://github.com/bytecodealliance/wasmtime/pull/2900). - -* Fixed WASI issue with file metadata on Windows: - [#2884](https://github.com/bytecodealliance/wasmtime/pull/2884). - -* Fixed an issue with debug info and an underflowing (trapping) offset: - [#2866](https://github.com/bytecodealliance/wasmtime/pull/2866). - -* Fixed an issue with unwind information in the old x86 backend: - [#2845](https://github.com/bytecodealliance/wasmtime/pull/2845). - -* Fixed i32 spilling in x64 backend: - [#2840](https://github.com/bytecodealliance/wasmtime/pull/2840). - -## 0.26.0 - -Released 2021-04-05. - -### Added - -* Added the `wasmtime compile` command to support AOT compilation of Wasm - modules. This adds the `Engine::precompile_module` method. Also added the - `Config::target` method to change the compilation target of the - configuration. This can be used in conjunction with - `Engine::precompile_module` to target a different host triple than the - current one. - [#2791](https://github.com/bytecodealliance/wasmtime/pull/2791) - -* Support for macOS on aarch64 (Apple M1 Silicon), including Apple-specific - calling convention details and unwinding/exception handling using Mach ports. - [#2742](https://github.com/bytecodealliance/wasmtime/pull/2742), - [#2723](https://github.com/bytecodealliance/wasmtime/pull/2723) - -* A number of SIMD instruction implementations in the new x86-64 backend. - [#2771](https://github.com/bytecodealliance/wasmtime/pull/2771) - -* Added the `Config::cranelift_flag_enable` method to enable setting Cranelift - boolean flags or presets in a config. - -* Added CLI option `--cranelift-enable` to enable boolean settings and ISA presets. - -* Deduplicate function signatures in Wasm modules. - [#2772](https://github.com/bytecodealliance/wasmtime/pull/2772) - -* Optimize overheads of calling into Wasm functions. - [#2757](https://github.com/bytecodealliance/wasmtime/pull/2757), - [#2759](https://github.com/bytecodealliance/wasmtime/pull/2759) - -* Improvements related to Module Linking: compile fewer trampolines; - - [#2774](https://github.com/bytecodealliance/wasmtime/pull/2774) - -* Re-export sibling crates from `wasmtime-wasi` to make embedding easier - without needing to match crate versions. - [#2776](https://github.com/bytecodealliance/wasmtime/pull/2776) - -### Changed - -* Switched the default compiler backend on x86-64 to Cranelift's new backend. - This should not have any user-visible effects other than possibly runtime - performance improvements. The old backend is still available with the - `old-x86-backend` feature flag to the `cranelift-codegen` or `wasmtime` - crates, or programmatically with `BackendVariant::Legacy`. We plan to - maintain the old backend for at least one more release and ensure it works on - CI. - [#2718](https://github.com/bytecodealliance/wasmtime/pull/2718) - -* Breaking: `Module::deserialize` has been removed in favor of `Module::new`. - -* Breaking: `Config::cranelift_clear_cpu_flags` was removed. Use `Config::target` - to clear the CPU flags for the host's target. - -* Breaking: `Config::cranelift_other_flag` was renamed to `Config::cranelift_flag_set`. - -* CLI changes: - * Wasmtime CLI options to enable WebAssembly features have been replaced with - a singular `--wasm-features` option. The previous options are still - supported, but are not displayed in help text. - * Breaking: the CLI option `--cranelift-flags` was changed to - `--cranelift-set`. - * Breaking: the CLI option `--enable-reference-types=false` has been changed - to `--wasm-features=-reference-types`. - * Breaking: the CLI option `--enable-multi-value=false` has been changed to - `--wasm-features=-multi-value`. - * Breaking: the CLI option `--enable-bulk-memory=false` has been changed to - `--wasm-features=-bulk-memory`. - -* Improved error-reporting in wiggle. - [#2760](https://github.com/bytecodealliance/wasmtime/pull/2760) - -* Make WASI sleeping fallible (some systems do not support sleep). - [#2756](https://github.com/bytecodealliance/wasmtime/pull/2756) - -* WASI: Support `poll_oneoff` with a sleep. - [#2753](https://github.com/bytecodealliance/wasmtime/pull/2753) - -* Allow a `StackMapSink` to be passed when defining functions with - `cranelift-module`. - [#2739](https://github.com/bytecodealliance/wasmtime/pull/2739) - -* Some refactoring in new x86-64 backend to prepare for VEX/EVEX (e.g., - AVX-512) instruction encodings to be supported. - [#2799](https://github.com/bytecodealliance/wasmtime/pull/2799) - -### Fixed - -* Fixed a corner case in `srem` (signed remainder) in the new x86-64 backend: - `INT_MIN % -1` should return `0`, rather than trapping. This only occurred - when `avoid_div_traps == false` was set by the embedding. - [#2763](https://github.com/bytecodealliance/wasmtime/pull/2763) - -* Fixed a memory leak of the `Store` when an instance traps. - [#2803](https://github.com/bytecodealliance/wasmtime/pull/2803) - -* Some fuzzing-related fixes. - [#2788](https://github.com/bytecodealliance/wasmtime/pull/2788), - [#2770](https://github.com/bytecodealliance/wasmtime/pull/2770) - -* Fixed memory-initialization bug in uffd allocator that could copy into the - wrong destination under certain conditions. Does not affect the default - wasmtime instance allocator. - [#2801](https://github.com/bytecodealliance/wasmtime/pull/2801) - -* Fix printing of float values from the Wasmtime CLI. - [#2797](https://github.com/bytecodealliance/wasmtime/pull/2797) - -* Remove the ability for the `Linker` to instantiate modules with duplicate - import strings of different types. - [#2789](https://github.com/bytecodealliance/wasmtime/pull/2789) - -## 0.25.0 - -Released 2021-03-16. - -### Added - -* An implementation of a pooling instance allocator, optionally backed by - `userfaultfd` on Linux, was added to improve the performance of embeddings - that instantiate a large number of instances continuously. - [#2518](https://github.com/bytecodealliance/wasmtime/pull/2518) - -* Host functions can now be defined on `Config` to share the function across all - `Store` objects connected to an `Engine`. This can improve the time it takes - to instantiate instances in a short-lived `Store`. - [#2625](https://github.com/bytecodealliance/wasmtime/pull/2625) - -* The `Store` object now supports having typed values attached to it which can - be retrieved from host functions. - [#2625](https://github.com/bytecodealliance/wasmtime/pull/2625) - -* The `wiggle` code generator now supports `async` host functions. - [#2701](https://github.com/bytecodealliance/wasmtime/pull/2701) - -### Changed - -* The `Func::getN{,_async}` APIs have all been removed in favor of a new - `Func::typed` API which should be more compact in terms of API surface area as - well as more flexible in how it can be used. - [#2719](https://github.com/bytecodealliance/wasmtime/pull/2719) - -* `Engine::new` has been changed from returning `Engine` to returning - `anyhow::Result`. Callers of `Engine::new` will need to be updated to - use the `?` operator on the return value or otherwise unwrap the result to get - the `Engine`. - -### Fixed - -* Interpretation of timestamps in `poll_oneoff` for WASI have been fixed to - correctly use nanoseconds instead of microseconds. - [#2717](https://github.com/bytecodealliance/wasmtime/pull/2717) - -## 0.24.0 - -Released 2021-03-04. - -### Added - -* Implement support for `async` functions in Wasmtime - [#2434](https://github.com/bytecodealliance/wasmtime/pull/2434) - -### Fixed - -* Fix preservation of the sigaltstack on macOS - [#2676](https://github.com/bytecodealliance/wasmtime/pull/2676) -* Fix incorrect semver dependencies involving fs-set-times. - [#2705](https://github.com/bytecodealliance/wasmtime/pull/2705) -* Fix some `i128` shift-related bugs in x64 backend. - [#2682](https://github.com/bytecodealliance/wasmtime/pull/2682) -* Fix incomplete trap metadata due to multiple traps at one address - [#2685](https://github.com/bytecodealliance/wasmtime/pull/2685) - -## 0.23.0 - -Released 2021-02-16. - -### Added - -* Support for limiting WebAssembly execution with fuel was added, including - support in the C API. - [#2611](https://github.com/bytecodealliance/wasmtime/pull/2611) - [#2643](https://github.com/bytecodealliance/wasmtime/pull/2643) -* Wasmtime now has more knobs for limiting memory and table allocations - [#2617](https://github.com/bytecodealliance/wasmtime/pull/2617) -* Added a method to share `Config` across machines - [#2608](https://github.com/bytecodealliance/wasmtime/pull/2608) -* Added a safe memory read/write API - [#2528](https://github.com/bytecodealliance/wasmtime/pull/2528) -* Added support for the experimental wasi-crypto APIs - [#2597](https://github.com/bytecodealliance/wasmtime/pull/2597) -* Added an instance limit to `Config` - [#2593](https://github.com/bytecodealliance/wasmtime/pull/2593) -* Implemented module-linking's outer module aliases - [#2590](https://github.com/bytecodealliance/wasmtime/pull/2590) -* Cranelift now supports 128-bit operations for the new x64 backend. - [#2539](https://github.com/bytecodealliance/wasmtime/pull/2539) -* Cranelift now has detailed debug-info (DWARF) support in new backends (initially x64). - [#2565](https://github.com/bytecodealliance/wasmtime/pull/2565) -* Cranelift now uses the `POPCNT`, `TZCNT`, and `LZCNT`, as well as SSE 4.1 - rounding instructions on x64 when available. -* Cranelift now uses the `CNT`, instruction on aarch64 when available. - -### Changed - -* A new WASI implementation built on the new - [`cap-std`](https://github.com/bytecodealliance/cap-std) crate was added, - replacing the previous implementation. This brings improved robustness, - portability, and performance. - -* `wasmtime_wasi::WasiCtxBuilder` moved to - `wasi_cap_std_sync::WasiCtxBuilder`. - -* The WebAssembly C API is updated, with a few minor API changes - [#2579](https://github.com/bytecodealliance/wasmtime/pull/2579) - -### Fixed - -* Fixed a panic in WASI `fd_readdir` on large directories - [#2620](https://github.com/bytecodealliance/wasmtime/pull/2620) -* Fixed a memory leak with command modules - [#2017](https://github.com/bytecodealliance/wasmtime/pull/2017) - --------------------------------------------------------------------------------- - -## 0.22.0 - -Released 2021-01-07. - -### Added - -* Experimental support for [the module-linking - proposal](https://github.com/WebAssembly/module-linking) was - added. [#2094](https://github.com/bytecodealliance/wasmtime/pull/2094) - -* Added support for [the reference types - proposal](https://webassembly.github.io/reference-types) on the aarch64 - architecture. [#2410](https://github.com/bytecodealliance/wasmtime/pull/2410) - -* Experimental support for [wasi-nn](https://github.com/WebAssembly/wasi-nn) was - added. [#2208](https://github.com/bytecodealliance/wasmtime/pull/2208) - -### Changed - -### Fixed - -* Fixed an issue where the `select` instruction didn't accept `v128` SIMD - operands. [#2391](https://github.com/bytecodealliance/wasmtime/pull/2391) - -* Fixed an issue where Wasmtime could potentially use the wrong stack map during - GCs, leading to a - panic. [#2396](https://github.com/bytecodealliance/wasmtime/pull/2396) - -* Fixed an issue where if a host-defined function erroneously returned a value - from a different store, that value would be - leaked. [#2424](https://github.com/bytecodealliance/wasmtime/pull/2424) - -* Fixed a bug where in certain cases if a module's instantiation failed, it - could leave trampolines in the store that referenced the no-longer-valid - instance. These trampolines could be reused in future instantiations, leading - to use after free bugs. - [#2408](https://github.com/bytecodealliance/wasmtime/pull/2408) - -* Fixed a miscompilation on aarch64 where certain instructions would read `SP` - instead of the zero register. This could only affect you if you explicitly - enabled the Wasm SIMD - proposal. [#2548](https://github.com/bytecodealliance/wasmtime/pull/2548) - --------------------------------------------------------------------------------- - -## 0.21.0 - -Released 2020-11-05. - -### Added - -* Experimental support for the multi-memory proposal was added. - [#2263](https://github.com/bytecodealliance/wasmtime/pull/2263) - -* The `Trap::trap_code` API enables learning what kind of trap was raised. - [#2309](https://github.com/bytecodealliance/wasmtime/pull/2309) - -### Changed - -* WebAssembly module validation is now parallelized. - [#2059](https://github.com/bytecodealliance/wasmtime/pull/2059) - -* Documentation is now available at docs.wasmtime.dev. - [#2317](https://github.com/bytecodealliance/wasmtime/pull/2317) - -* Windows now compiles like other platforms with a huge guard page instead of - having its own custom limit which made modules compile and run more slowly. - [#2326](https://github.com/bytecodealliance/wasmtime/pull/2326) - -* The size of the cache entry for serialized modules has been greatly reduced. - [#2321](https://github.com/bytecodealliance/wasmtime/pull/2321) - [#2322](https://github.com/bytecodealliance/wasmtime/pull/2322) - [#2324](https://github.com/bytecodealliance/wasmtime/pull/2324) - [#2325](https://github.com/bytecodealliance/wasmtime/pull/2325) - -* The `FuncType` API constructor and accessors are now iterator-based. - [#2365](https://github.com/bytecodealliance/wasmtime/pull/2365) - -### Fixed - -* A panic in compiling reference-types-using modules has been fixed. - [#2350](https://github.com/bytecodealliance/wasmtime/pull/2350) - --------------------------------------------------------------------------------- - -## 0.20.0 - -Released 2020-09-23. - -### Added - -* Support for explicitly serializing and deserializing compiled wasm modules has - been added. - [#2020](https://github.com/bytecodealliance/wasmtime/pull/2020) - -* A `wasmtime_store_gc` C API was added to run GC for `externref`. - [#2052](https://github.com/bytecodealliance/wasmtime/pull/2052) - -* Support for atomics in Cranelift has been added. Support is not fully - implemented in Wasmtime at this time, however. - [#2077](https://github.com/bytecodealliance/wasmtime/pull/2077) - -* The `Caller::get_export` function is now implemented for `Func` references as - well. - [#2108](https://github.com/bytecodealliance/wasmtime/pull/2108) - -### Fixed - -* Leaks in the C API have been fixed. - [#2040](https://github.com/bytecodealliance/wasmtime/pull/2040) - -* The `wasm_val_copy` C API has been fixed for reference types. - [#2041](https://github.com/bytecodealliance/wasmtime/pull/2041) - -* Fix a panic with `Func::new` and reference types when the store doesn't have - reference types enabled. - [#2039](https://github.com/bytecodealliance/wasmtime/pull/2039) - --------------------------------------------------------------------------------- - -## 0.19.0 - -Released 2020-07-14. - -### Added - -* The [WebAssembly reference-types proposal][reftypes] is now supported in - Wasmtime and the C API. - [#1832](https://github.com/bytecodealliance/wasmtime/pull/1832), - [#1882](https://github.com/bytecodealliance/wasmtime/pull/1882), - [#1894](https://github.com/bytecodealliance/wasmtime/pull/1894), - [#1901](https://github.com/bytecodealliance/wasmtime/pull/1901), - [#1923](https://github.com/bytecodealliance/wasmtime/pull/1923), - [#1969](https://github.com/bytecodealliance/wasmtime/pull/1969), - [#1973](https://github.com/bytecodealliance/wasmtime/pull/1973), - [#1982](https://github.com/bytecodealliance/wasmtime/pull/1982), - [#1984](https://github.com/bytecodealliance/wasmtime/pull/1984), - [#1991](https://github.com/bytecodealliance/wasmtime/pull/1991), - [#1996](https://github.com/bytecodealliance/wasmtime/pull/1996) - -* The [WebAssembly simd proposal's][simd] spec tests now pass in Wasmtime. - [#1765](https://github.com/bytecodealliance/wasmtime/pull/1765), - [#1876](https://github.com/bytecodealliance/wasmtime/pull/1876), - [#1941](https://github.com/bytecodealliance/wasmtime/pull/1941), - [#1957](https://github.com/bytecodealliance/wasmtime/pull/1957), - [#1990](https://github.com/bytecodealliance/wasmtime/pull/1990), - [#1994](https://github.com/bytecodealliance/wasmtime/pull/1994) - -* Wasmtime can now be compiled without the usage of threads for parallel - compilation, although this is still enabled by default. - [#1903](https://github.com/bytecodealliance/wasmtime/pull/1903) - -* The C API is [now - documented](https://bytecodealliance.github.io/wasmtime/c-api/). - [#1928](https://github.com/bytecodealliance/wasmtime/pull/1928), - [#1959](https://github.com/bytecodealliance/wasmtime/pull/1959), - [#1968](https://github.com/bytecodealliance/wasmtime/pull/1968) - -* A `wasmtime_linker_get_one_by_name` function was added to the C API. - [#1897](https://github.com/bytecodealliance/wasmtime/pull/1897) - -* A `wasmtime_trap_exit_status` function was added to the C API. - [#1912](https://github.com/bytecodealliance/wasmtime/pull/1912) - -* Compilation for the `aarch64-linux-android` target should now work, although - keep in mind this platform is not fully tested still. - [#2002](https://github.com/bytecodealliance/wasmtime/pull/2002) - -[reftypes]: https://github.com/WebAssembly/reference-types - -### Fixed - -* Runtime warnings when using Wasmtime on musl have been fixed. - [#1914](https://github.com/bytecodealliance/wasmtime/pull/1914) - -* A bug affecting Windows unwind information with functions that have spilled - floating point registers has been fixed. - [#1983](https://github.com/bytecodealliance/wasmtime/pull/1983) - -### Changed - -* Wasmtime's default branch and development now happens on the `main` branch - instead of `master`. - [#1924](https://github.com/bytecodealliance/wasmtime/pull/1924) - -### Removed - -* The "host info" support in the C API has been removed since it was never fully - or correctly implemented. - [#1922](https://github.com/bytecodealliance/wasmtime/pull/1922) - -* Support for the `*_same` functions in the C API has been removed in the same - vein as the host info APIs. - [#1926](https://github.com/bytecodealliance/wasmtime/pull/1926) - --------------------------------------------------------------------------------- - -## 0.18.0 - -Release 2020-06-09. - -### Added - -The `WasmTy` trait is now implemented for `u32` and `u64`. - - [#1808](https://github.com/bytecodealliance/wasmtime/pull/1808) - --------------------------------------------------------------------------------- - -## 0.17.0 - -Released 2020-06-01. - -### Added - -* The [Commands and Reactors ABI] is now supported in the Rust API. `Linker::module` - loads a module and automatically handles Commands and Reactors semantics. - - [#1565](https://github.com/bytecodealliance/wasmtime/pull/1565) - -[Commands and Reactors ABI]: https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi - -The `Table::grow` function now returns the previous table size, making it consistent -with the `table.grow` instruction. - - [#1653](https://github.com/bytecodealliance/wasmtime/pull/1653) - -New Wasmtime-specific C APIs for working with tables were added which provide more -detailed error information and which make growing a table more consistent with the -`table.grow` instruction as well. - - [#1654](https://github.com/bytecodealliance/wasmtime/pull/1654) - -The C API now includes support for enabling logging in Wasmtime. - - [#1737](https://github.com/bytecodealliance/wasmtime/pull/1737) - -### Changed - -The WASI `proc_exit` function no longer exits the host process. It now unwinds the -callstack back to the wasm entrypoint, and the exit value is available from the -`Trap::i32_exit_status` method. - - [#1646](https://github.com/bytecodealliance/wasmtime/pull/1646) - -The WebAssembly [multi-value](https://github.com/WebAssembly/multi-value/) proposal -is now enabled by default. - - [#1667](https://github.com/bytecodealliance/wasmtime/pull/1667) - -The Rust API does not require a store provided during `Module::new` operation. The `Module` can be send accross threads and instantiate for a specific store. The `Instance::new` now requires the store. - - [#1761](https://github.com/bytecodealliance/wasmtime/pull/1761) - --------------------------------------------------------------------------------- - -## 0.16.0 - -Released 2020-04-29. - -### Added - -* The `Instance` struct has new accessors, `get_func`, `get_table`, - `get_memory`, and `get_global` for quickly looking up exported - functions, tables, memories, and globals by name. - [#1524](https://github.com/bytecodealliance/wasmtime/pull/1524) - -* The C API has a number of new `wasmtime_*` functions which return error - objects to get detailed error information when an API fails. - [#1467](https://github.com/bytecodealliance/wasmtime/pull/1467) - -* Users now have fine-grained control over creation of instances of `Memory` - with a new `MemoryCreator` trait. - [#1400](https://github.com/bytecodealliance/wasmtime/pull/1400) - -* Go bindings for Wasmtime are [now available][go-bindings]. - [#1481](https://github.com/bytecodealliance/wasmtime/pull/1481) - -* APIs for looking up values in a `Linker` have been added. - [#1480](https://github.com/bytecodealliance/wasmtime/pull/1480) - -* Preliminary support for AArch64, also known as ARM64. - [#1581](https://github.com/bytecodealliance/wasmtime/pull/1581) - -[go-bindings]: https://github.com/bytecodealliance/wasmtime-go - -### Changed - -* `Instance::exports` now returns `Export` objects which contain - the `name`s of the exports in addition to their `Extern` definitions, - so it's no longer necessary to use `Module::exports` to obtain the - export names. - [#1524](https://github.com/bytecodealliance/wasmtime/pull/1524) - -* The `Func::call` API has changed its error type from `Trap` to `anyhow::Error` - to distinguish between wasm traps and runtime violations (like the wrong - number of parameters). - [#1467](https://github.com/bytecodealliance/wasmtime/pull/1467) - -* A number of `wasmtime_linker_*` and `wasmtime_config_*` C APIs have new type - signatures which reflect returning errors. - [#1467](https://github.com/bytecodealliance/wasmtime/pull/1467) - -* Bindings for .NET have moved to - https://github.com/bytecodealliance/wasmtime-dotnet. - [#1477](https://github.com/bytecodealliance/wasmtime/pull/1477) - -* Passing too many imports to `Instance::new` is now considered an error. - [#1478](https://github.com/bytecodealliance/wasmtime/pull/1478) - -### Fixed - -* Spurious segfaults due to out-of-stack conditions when handling signals have - been fixed. - [#1315](https://github.com/bytecodealliance/wasmtime/pull/1315) - --------------------------------------------------------------------------------- - -## 0.15.0 - -Released 2020-03-31. - -### Fixed - -Full release produced for all artifacts to account for hiccups in 0.13.0 and -0.14.0. - --------------------------------------------------------------------------------- - -## 0.14.0 - -*This version ended up not getting a full release* - -### Fixed - -Fix build errors in wasi-common on Windows. - --------------------------------------------------------------------------------- - -## 0.13.0 - -Released 2020-03-24. - -### Added - -* Lots of documentation of `wasmtime` has been updated. Be sure to check out the - [book](https://bytecodealliance.github.io/wasmtime/) and [API - documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/)! - -* All wasmtime example programs are now in a top-level `examples` directory and - are available in both C and Rust. - [#1286](https://github.com/bytecodealliance/wasmtime/pull/1286) - -* A `wasmtime::Linker` type was added to conveniently link link wasm modules - together and create instances that reference one another. - [#1384](https://github.com/bytecodealliance/wasmtime/pull/1384) - -* Wasmtime now has "jitdump" support enabled by default which allows [profiling - wasm code on linux][jitdump]. - [#1310](https://github.com/bytecodealliance/wasmtime/pull/1310) - -* The `wasmtime::Caller` type now exists as a first-class way to access the - caller's exports, namely memory, when implementing host APIs. This can be the - first argument of functions defined with `Func::new` or `Func::wrap` which - allows easily implementing methods which take a pointer into wasm memory. Note - that this only works for accessing the caller's `Memory` for now and it must - be exported. This will eventually be replaced with a more general-purpose - mechanism like interface types. - [#1290](https://github.com/bytecodealliance/wasmtime/pull/1290) - -* The bulk memory proposal has been fully implemented. - [#1264](https://github.com/bytecodealliance/wasmtime/pull/1264) - [#976](https://github.com/bytecodealliance/wasmtime/pull/976) - -* Virtual file support has been added to `wasi-common`. - [#701](https://github.com/bytecodealliance/wasmtime/pull/701) - -* The C API has been enhanced with a Wasmtime-specific `wasmtime_wat2wasm` to - parse `*.wat` files via the C API. - [#1206](https://github.com/bytecodealliance/wasmtime/pull/1206) - -[jitdump]: https://bytecodealliance.github.io/wasmtime/examples-profiling.html - -### Changed - -* The `wast` and `wasm2obj` standalone binaries have been removed. They're - available via the `wasmtime wast` and `wasmtime wasm2obj` subcommands. - [#1372](https://github.com/bytecodealliance/wasmtime/pull/1372) - -* The `wasi-common` crate now uses the new `wiggle` crate to auto-generate a - trait which is implemented for the current wasi snapshot. - [#1202](https://github.com/bytecodealliance/wasmtime/pull/1202) - -* Wasmtime no longer has a dependency on a C++ compiler. - [#1365](https://github.com/bytecodealliance/wasmtime/pull/1365) - -* The `Func::wrapN` APIs have been consolidated into one `Func::wrap` API. - [#1363](https://github.com/bytecodealliance/wasmtime/pull/1363) - -* The `Callable` trait has been removed and now `Func::new` takes a closure - directly. - [#1363](https://github.com/bytecodealliance/wasmtime/pull/1363) - -* The Cranelift repository has been merged into the Wasmtime repository. - -* Support for interface types has been temporarily removed. - [#1292](https://github.com/bytecodealliance/wasmtime/pull/1292) - -* The exit code of the `wasmtime` CLI has changed if the program traps. - [#1274](https://github.com/bytecodealliance/wasmtime/pull/1274) - -* The `wasmtime` CLI now logs to stderr by default and the `-d` flag has been - renamed to `--log-to-file`. - [#1266](https://github.com/bytecodealliance/wasmtime/pull/1266) - -* Values cannot cross `Store` objects, meaning you can't instantiate a module - with values from different stores nor pass values from different stores into - methods. - [#1016](https://github.com/bytecodealliance/wasmtime/pull/1016) - --------------------------------------------------------------------------------- - -## 0.12.0 - -Released 2020-02-26. - -### Added - -* Support for the [WebAssembly text annotations proposal][annotations-proposal] - has been added. - [#998](https://github.com/bytecodealliance/wasmtime/pull/998) - -* An initial C API for instantiating WASI modules has been added. - [#977](https://github.com/bytecodealliance/wasmtime/pull/977) - -* A new suite of `Func::getN` functions have been added to the `wasmtime` API to - call statically-known function signatures in a highly optimized fashion. - [#955](https://github.com/bytecodealliance/wasmtime/pull/955) - -* Initial support for profiling JIT code through perf jitdump has been added. - [#360](https://github.com/bytecodealliance/wasmtime/pull/360) - -* More CLI flags corresponding to proposed WebAssembly features have been added. - [#917](https://github.com/bytecodealliance/wasmtime/pull/917) - -[annotations-proposal]: https://github.com/webassembly/annotations - -### Changed - -* The `wasmtime` CLI as well as embedding API will optimize WebAssembly code by - default now. - [#973](https://github.com/bytecodealliance/wasmtime/pull/973) - [#988](https://github.com/bytecodealliance/wasmtime/pull/988) - -* The `verifier` pass in Cranelift is now no longer run by default when using - the embedding API. - [#882](https://github.com/bytecodealliance/wasmtime/pull/882) - -### Fixed - -* Code caching now accurately accounts for optimization levels, ensuring that if - you ask for optimized code you're not accidentally handed unoptimized code - from the cache. - [#974](https://github.com/bytecodealliance/wasmtime/pull/974) - -* Automated releases for tags should be up and running again, along with - automatic publication of the `wasmtime` Python package. - [#971](https://github.com/bytecodealliance/wasmtime/pull/971) +Release notes for previous releases of Wasmtime can be found on the respective +release branches of the Wasmtime repository. + + +* [24.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-24.0.0/RELEASES.md) +* [23.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-23.0.0/RELEASES.md) +* [22.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-22.0.0/RELEASES.md) +* [21.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-21.0.0/RELEASES.md) +* [20.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-20.0.0/RELEASES.md) +* [19.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-19.0.0/RELEASES.md) +* [18.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-18.0.0/RELEASES.md) +* [17.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-17.0.0/RELEASES.md) +* [16.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-16.0.0/RELEASES.md) +* [15.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-15.0.0/RELEASES.md) +* [14.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-14.0.0/RELEASES.md) +* [13.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-13.0.0/RELEASES.md) +* [12.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-12.0.0/RELEASES.md) +* [11.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-11.0.0/RELEASES.md) +* [10.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-10.0.0/RELEASES.md) +* [9.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-9.0.0/RELEASES.md) +* [8.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-8.0.0/RELEASES.md) +* [7.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-7.0.0/RELEASES.md) +* [6.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-6.0.0/RELEASES.md) +* [5.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-5.0.0/RELEASES.md) +* [4.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-4.0.0/RELEASES.md) +* [3.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-3.0.0/RELEASES.md) +* [2.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-2.0.0/RELEASES.md) +* [1.0.x](https://github.com/bytecodealliance/wasmtime/blob/release-1.0.0/RELEASES.md) +* [0.40.x](https://github.com/bytecodealliance/wasmtime/blob/release-0.40.0/RELEASES.md) +* [0.39.x](https://github.com/bytecodealliance/wasmtime/blob/release-0.39.0/RELEASES.md) +* [0.38.x](https://github.com/bytecodealliance/wasmtime/blob/release-0.38.0/RELEASES.md) +* [0.37.x](https://github.com/bytecodealliance/wasmtime/blob/release-0.37.0/RELEASES.md) +* [0.36.x](https://github.com/bytecodealliance/wasmtime/blob/release-0.36.0/RELEASES.md) +* [0.35.x](https://github.com/bytecodealliance/wasmtime/blob/release-0.35.0/RELEASES.md) +* [0.34.x](https://github.com/bytecodealliance/wasmtime/blob/release-0.34.0/RELEASES.md) +* [0.33.x](https://github.com/bytecodealliance/wasmtime/blob/release-0.33.0/RELEASES.md) +* [0.32.x (and prior)](https://github.com/bytecodealliance/wasmtime/blob/release-0.32.0/RELEASES.md) diff --git a/SECURITY.md b/SECURITY.md index a1991e90af64..c1aca31e57e6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,3 +1,11 @@ # Security Policy -Please refer to the [Bytecode Alliance security policy](https://bytecodealliance.org/security) for details on how to report security issues in Wasmtime, our disclosure policy, and how to receive notifications about security issues. +Please refer to the [Bytecode Alliance security +policy](https://bytecodealliance.org/security) for details on how to report +security issues in Wasmtime, our disclosure policy, and how to receive +notifications about security issues. + +For classification of what is and what isn't a security issue please see our +[online +documentation](https://docs.wasmtime.dev/security-what-is-considered-a-security-vulnerability.html) +on the subject. diff --git a/benches/call.rs b/benches/call.rs index 90f9beca60b2..ac6880c5119f 100644 --- a/benches/call.rs +++ b/benches/call.rs @@ -23,6 +23,7 @@ enum IsAsync { Yes, YesPooling, No, + NoPooling, } impl IsAsync { @@ -31,12 +32,13 @@ impl IsAsync { IsAsync::Yes => "async", IsAsync::YesPooling => "async-pool", IsAsync::No => "sync", + IsAsync::NoPooling => "sync-pool", } } fn use_async(&self) -> bool { match self { IsAsync::Yes | IsAsync::YesPooling => true, - IsAsync::No => false, + IsAsync::No | IsAsync::NoPooling => false, } } } @@ -47,14 +49,29 @@ fn engines() -> Vec<(Engine, IsAsync)> { #[cfg(feature = "component-model")] config.wasm_component_model(true); + let mut pool = PoolingAllocationConfig::default(); + if std::env::var("WASMTIME_TEST_FORCE_MPK").is_ok() { + pool.memory_protection_keys(MpkEnabled::Enable); + } + vec![ (Engine::new(&config).unwrap(), IsAsync::No), + ( + Engine::new( + config + .clone() + .allocation_strategy(InstanceAllocationStrategy::Pooling(pool.clone())), + ) + .unwrap(), + IsAsync::NoPooling, + ), ( Engine::new(config.async_support(true)).unwrap(), IsAsync::Yes, ), ( - Engine::new(config.allocation_strategy(InstanceAllocationStrategy::pooling())).unwrap(), + Engine::new(config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool))) + .unwrap(), IsAsync::YesPooling, ), ] @@ -122,7 +139,7 @@ fn bench_host_to_wasm( { // Benchmark the "typed" version, which should be faster than the versions // below. - c.bench_function(&format!("core - host-to-wasm - typed - {}", name), |b| { + c.bench_function(&format!("core - host-to-wasm - typed - {name}"), |b| { let typed = instance .get_typed_func::(&mut *store, name) .unwrap(); @@ -138,7 +155,7 @@ fn bench_host_to_wasm( // Benchmark the "untyped" version which should be the slowest of the three // here, but not unduly slow. - c.bench_function(&format!("core - host-to-wasm - untyped - {}", name), |b| { + c.bench_function(&format!("core - host-to-wasm - untyped - {name}"), |b| { let untyped = instance.get_func(&mut *store, name).unwrap(); let params = typed_params.to_vals(); let expected_results = typed_results.to_vals(); @@ -163,29 +180,25 @@ fn bench_host_to_wasm( // Benchmark the "unchecked" version which should be between the above two, // but is unsafe. - c.bench_function( - &format!("core - host-to-wasm - unchecked - {}", name), - |b| { - let untyped = instance.get_func(&mut *store, name).unwrap(); - let params = typed_params.to_vals(); - let results = typed_results.to_vals(); - let mut space = vec![ValRaw::i32(0); params.len().max(results.len())]; - b.iter(|| unsafe { - for (i, param) in params.iter().enumerate() { - space[i] = param.to_raw(&mut *store); - } - untyped - .call_unchecked(&mut *store, space.as_mut_ptr(), space.len()) - .unwrap(); - for (i, expected) in results.iter().enumerate() { - assert_vals_eq( - expected, - &Val::from_raw(&mut *store, space[i], expected.ty()), - ); - } - }) - }, - ); + c.bench_function(&format!("core - host-to-wasm - unchecked - {name}"), |b| { + let untyped = instance.get_func(&mut *store, name).unwrap(); + let params = typed_params.to_vals(); + let results = typed_results.to_vals(); + let mut space = vec![ValRaw::i32(0); params.len().max(results.len())]; + b.iter(|| unsafe { + for (i, param) in params.iter().enumerate() { + space[i] = param.to_raw(&mut *store).unwrap(); + } + untyped + .call_unchecked(&mut *store, space.as_mut_ptr(), space.len()) + .unwrap(); + for (i, expected) in results.iter().enumerate() { + let ty = expected.ty(&store).unwrap(); + let actual = Val::from_raw(&mut *store, space[i], ty); + assert_vals_eq(expected, &actual); + } + }) + }); } /// Benchmarks the overhead of calling the host from WebAssembly itself @@ -285,9 +298,9 @@ fn wasm_to_host(c: &mut Criterion) { let mut untyped = Linker::new(&engine); untyped - .func_new("", "nop", FuncType::new([], []), |_, _, _| Ok(())) + .func_new("", "nop", FuncType::new(&engine, [], []), |_, _, _| Ok(())) .unwrap(); - let ty = FuncType::new([ValType::I32, ValType::I64], [ValType::F32]); + let ty = FuncType::new(&engine, [ValType::I32, ValType::I64], [ValType::F32]); untyped .func_new( "", @@ -319,9 +332,9 @@ fn wasm_to_host(c: &mut Criterion) { unsafe { let mut unchecked = Linker::new(&engine); unchecked - .func_new_unchecked("", "nop", FuncType::new([], []), |_, _| Ok(())) + .func_new_unchecked("", "nop", FuncType::new(&engine, [], []), |_, _| Ok(())) .unwrap(); - let ty = FuncType::new([ValType::I32, ValType::I64], [ValType::F32]); + let ty = FuncType::new(&engine, [ValType::I32, ValType::I64], [ValType::F32]); unchecked .func_new_unchecked("", "nop-params-and-results", ty, |mut caller, space| { match Val::from_raw(&mut caller, space[0], ValType::I32) { @@ -332,7 +345,7 @@ fn wasm_to_host(c: &mut Criterion) { Val::I64(0) => {} _ => unreachable!(), } - space[0] = Val::F32(0).to_raw(&mut caller); + space[0] = Val::F32(0).to_raw(&mut caller).unwrap(); Ok(()) }) .unwrap(); @@ -351,20 +364,24 @@ fn wasm_to_host(c: &mut Criterion) { let mut typed = Linker::new(&engine); typed - .func_wrap0_async("", "nop", |caller| { + .func_wrap_async("", "nop", |caller, _: ()| { Box::new(async { drop(caller); }) }) .unwrap(); typed - .func_wrap2_async("", "nop-params-and-results", |_caller, x: i32, y: i64| { - Box::new(async move { - assert_eq!(x, 0); - assert_eq!(y, 0); - 0.0f32 - }) - }) + .func_wrap_async( + "", + "nop-params-and-results", + |_caller, (x, y): (i32, i64)| { + Box::new(async move { + assert_eq!(x, 0); + assert_eq!(y, 0); + 0.0f32 + }) + }, + ) .unwrap(); let instance = run_await(typed.instantiate_async(&mut *store, &module)).unwrap(); bench_instance(group, store, &instance, "async-typed", is_async); @@ -378,7 +395,7 @@ fn wasm_to_host(c: &mut Criterion) { desc: &str, is_async: IsAsync, ) { - group.bench_function(&format!("core - wasm-to-host - {} - nop", desc), |b| { + group.bench_function(&format!("core - wasm-to-host - {desc} - nop"), |b| { let run = instance .get_typed_func::(&mut *store, "run-nop") .unwrap(); @@ -393,7 +410,7 @@ fn wasm_to_host(c: &mut Criterion) { }) }); group.bench_function( - &format!("core - wasm-to-host - {} - nop-params-and-results", desc), + &format!("core - wasm-to-host - {desc} - nop-params-and-results"), |b| { let run = instance .get_typed_func::(&mut *store, "run-nop-params-and-results") @@ -616,31 +633,28 @@ mod component { + Sync, { // Benchmark the "typed" version. - c.bench_function( - &format!("component - host-to-wasm - typed - {}", name), - |b| { - let typed = instance - .get_typed_func::(&mut *store, name) - .unwrap(); - b.iter(|| { - let results = if is_async.use_async() { - run_await(typed.call_async(&mut *store, typed_params)).unwrap() - } else { - typed.call(&mut *store, typed_params).unwrap() - }; - assert_eq!(results, typed_results); - if is_async.use_async() { - run_await(typed.post_return_async(&mut *store)).unwrap() - } else { - typed.post_return(&mut *store).unwrap() - } - }) - }, - ); + c.bench_function(&format!("component - host-to-wasm - typed - {name}"), |b| { + let typed = instance + .get_typed_func::(&mut *store, name) + .unwrap(); + b.iter(|| { + let results = if is_async.use_async() { + run_await(typed.call_async(&mut *store, typed_params)).unwrap() + } else { + typed.call(&mut *store, typed_params).unwrap() + }; + assert_eq!(results, typed_results); + if is_async.use_async() { + run_await(typed.post_return_async(&mut *store)).unwrap() + } else { + typed.post_return(&mut *store).unwrap() + } + }) + }); // Benchmark the "untyped" version. c.bench_function( - &format!("component - host-to-wasm - untyped - {}", name), + &format!("component - host-to-wasm - untyped - {name}"), |b| { let untyped = instance.get_func(&mut *store, name).unwrap(); let params = typed_params.to_component_vals(); @@ -782,30 +796,23 @@ mod component { bench_instance(group, store, &instance, "typed", is_async); let mut untyped = component::Linker::new(&engine); + untyped.root().func_new("nop", |_, _, _| Ok(())).unwrap(); untyped .root() - .func_new(&component, "nop", |_, _, _| Ok(())) - .unwrap(); - untyped - .root() - .func_new( - &component, - "nop-params-and-results", - |_caller, params, results| { - assert_eq!(params.len(), 2); - match params[0] { - component::Val::U32(0) => {} - _ => unreachable!(), - } - match params[1] { - component::Val::U64(0) => {} - _ => unreachable!(), - } - assert_eq!(results.len(), 1); - results[0] = component::Val::Float32(0.0); - Ok(()) - }, - ) + .func_new("nop-params-and-results", |_caller, params, results| { + assert_eq!(params.len(), 2); + match params[0] { + component::Val::U32(0) => {} + _ => unreachable!(), + } + match params[1] { + component::Val::U64(0) => {} + _ => unreachable!(), + } + assert_eq!(results.len(), 1); + results[0] = component::Val::Float32(0.0); + Ok(()) + }) .unwrap(); let instance = if is_async.use_async() { run_await(untyped.instantiate_async(&mut *store, &component)).unwrap() @@ -851,7 +858,7 @@ mod component { desc: &str, is_async: IsAsync, ) { - group.bench_function(&format!("component - wasm-to-host - {} - nop", desc), |b| { + group.bench_function(&format!("component - wasm-to-host - {desc} - nop"), |b| { let run = instance .get_typed_func::<(u64,), ()>(&mut *store, "run-nop") .unwrap(); @@ -868,10 +875,7 @@ mod component { }) }); group.bench_function( - &format!( - "component - wasm-to-host - {} - nop-params-and-results", - desc - ), + &format!("component - wasm-to-host - {desc} - nop-params-and-results"), |b| { let run = instance .get_typed_func::<(u64,), ()>(&mut *store, "run-nop-params-and-results") diff --git a/benches/instantiation.rs b/benches/instantiation.rs index e43bd650545e..5020478ad8f9 100644 --- a/benches/instantiation.rs +++ b/benches/instantiation.rs @@ -6,8 +6,8 @@ use std::process::Command; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; use std::sync::Arc; use std::thread; +use wasi_common::{sync::WasiCtxBuilder, WasiCtx}; use wasmtime::*; -use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx}; fn store(engine: &Engine) -> Store { let wasi = WasiCtxBuilder::new().build(); @@ -20,7 +20,7 @@ fn instantiate(pre: &InstancePre, engine: &Engine) -> Result<()> { Ok(()) } -fn benchmark_name<'a>(strategy: &InstanceAllocationStrategy) -> &'static str { +fn benchmark_name(strategy: &InstanceAllocationStrategy) -> &'static str { match strategy { InstanceAllocationStrategy::OnDemand => "default", InstanceAllocationStrategy::Pooling { .. } => "pooling", @@ -48,7 +48,7 @@ fn bench_sequential(c: &mut Criterion, path: &Path) { // benchmark programs. linker.func_wrap("bench", "start", || {}).unwrap(); linker.func_wrap("bench", "end", || {}).unwrap(); - wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap(); + wasi_common::sync::add_to_linker(&mut linker, |cx| cx).unwrap(); let pre = linker .instantiate_pre(&module) .expect("failed to pre-instantiate"); @@ -82,7 +82,7 @@ fn bench_parallel(c: &mut Criterion, path: &Path) { // benchmark programs. linker.func_wrap("bench", "start", || {}).unwrap(); linker.func_wrap("bench", "end", || {}).unwrap(); - wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap(); + wasi_common::sync::add_to_linker(&mut linker, |cx| cx).unwrap(); let pre = Arc::new( linker .instantiate_pre(&module) @@ -185,7 +185,7 @@ fn build_wasi_example() { "-p", "example-wasi-wasm", "--target", - "wasm32-wasi", + "wasm32-wasip1", ]) .spawn() .expect("failed to run cargo to build WASI example") @@ -193,11 +193,11 @@ fn build_wasi_example() { .expect("failed to wait for cargo to build") .success() { - panic!("failed to build WASI example for target `wasm32-wasi`"); + panic!("failed to build WASI example for target `wasm32-wasip1`"); } std::fs::copy( - "target/wasm32-wasi/release/wasi.wasm", + "target/wasm32-wasip1/release/wasi.wasm", "benches/instantiation/wasi.wasm", ) .expect("failed to copy WASI example module"); @@ -219,7 +219,7 @@ fn strategies() -> impl Iterator { InstanceAllocationStrategy::OnDemand, InstanceAllocationStrategy::Pooling({ let mut config = PoolingAllocationConfig::default(); - config.memory_pages(10_000); + config.max_memory_size(10_000 << 16); config }), ] diff --git a/benches/trap.rs b/benches/trap.rs index 979f30676445..a006c871b9bc 100644 --- a/benches/trap.rs +++ b/benches/trap.rs @@ -187,7 +187,7 @@ fn bench_host_wasm_frames_traps(c: &mut Criterion) { let mut store = Store::new(&engine, ()); let host_func = Func::new( &mut store, - FuncType::new(vec![ValType::I32], vec![]), + FuncType::new(&engine, vec![ValType::I32], vec![]), |mut caller, args, _results| { let f = caller.get_export("f").unwrap(); let f = f.into_func().unwrap(); diff --git a/benches/wasi.rs b/benches/wasi.rs index 41dc25e3bd8a..77944071ba9e 100644 --- a/benches/wasi.rs +++ b/benches/wasi.rs @@ -2,8 +2,8 @@ use criterion::{criterion_group, criterion_main, Criterion}; use std::{fs::File, path::Path, time::Instant}; +use wasi_common::{sync::WasiCtxBuilder, WasiCtx}; use wasmtime::{Engine, Linker, Module, Store, TypedFunc}; -use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx}; criterion_group!(benches, bench_wasi); criterion_main!(benches); @@ -53,7 +53,7 @@ fn instantiate(wat: &[u8]) -> (Store, TypedFunc) { let mut store = Store::new(&engine, wasi); let module = Module::new(&engine, wat).unwrap(); let mut linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap(); + wasi_common::sync::add_to_linker(&mut linker, |cx| cx).unwrap(); let instance = linker.instantiate(&mut store, &module).unwrap(); let run = instance.get_typed_func(&mut store, "run").unwrap(); (store, run) @@ -77,9 +77,9 @@ fn wasi_context() -> WasiCtx { ]) .unwrap() .preopened_dir( - wasmtime_wasi::Dir::open_ambient_dir( + wasi_common::sync::Dir::open_ambient_dir( "benches/wasi", - wasmtime_wasi::ambient_authority(), + wasi_common::sync::ambient_authority(), ) .unwrap(), "/", diff --git a/benches/wasmtime-serve-rps.sh b/benches/wasmtime-serve-rps.sh new file mode 100755 index 000000000000..33d83a74ab15 --- /dev/null +++ b/benches/wasmtime-serve-rps.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# Usage: +# +# wasmtime-serve-rps.sh [WASMTIME-FLAGS] path/to/wasi-http-component.wasm +# +# For a basic WASI HTTP component, check out +# https://github.com/sunfishcode/hello-wasi-http +# +# You must have the `hey` tool installed on your `$PATH`. It is available in at +# least the `apt` and `brew` package managers, as well as a binary download via +# its github page: https://github.com/rakyll/hey + +set -e + +repo_dir="$(dirname $0)/.." +cargo_toml="$repo_dir/Cargo.toml" +target_dir="$CARGO_TARGET_DIR" +if [[ "$target_dir" == "" ]]; then + target_dir="$repo_dir/target" +fi + +# Build Wasmtime. +cargo build --manifest-path "$cargo_toml" --release -p wasmtime-cli + +# Spawn `wasmtime serve` in the background. +"$target_dir/release/wasmtime" serve "$@" & +pid=$! + +# Give it a second to print its diagnostic information and get the server up and +# running. +sleep 1 + +echo 'Running `wasmtime serve` in background as pid '"$pid" + +# Benchmark the server! +echo "Benchmarking for 10 seconds..." +hey -z 10s http://0.0.0.0:8080/ + +kill "$pid" diff --git a/build.rs b/build.rs index 041373eba0c3..2057d27f34c6 100644 --- a/build.rs +++ b/build.rs @@ -1,268 +1,36 @@ -//! Build program to generate a program which runs all the testsuites. -//! -//! By generating a separate `#[test]` test for each file, we allow cargo test -//! to automatically run the files in parallel. - -use anyhow::Context; use std::env; -use std::fmt::Write; -use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::process::Command; -fn main() -> anyhow::Result<()> { +fn main() { println!("cargo:rerun-if-changed=build.rs"); - let out_dir = PathBuf::from( - env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"), - ); - let mut out = String::new(); - - for strategy in &["Cranelift", "Winch"] { - writeln!(out, "#[cfg(test)]")?; - writeln!(out, "#[allow(non_snake_case)]")?; - if *strategy == "Winch" { - // We only test Winch on x86_64, for now. - writeln!(out, "{}", "#[cfg(all(target_arch = \"x86_64\"))]")?; - } - writeln!(out, "mod {} {{", strategy)?; - - with_test_module(&mut out, "misc", |out| { - test_directory(out, "tests/misc_testsuite", strategy)?; - test_directory_module(out, "tests/misc_testsuite/multi-memory", strategy)?; - test_directory_module(out, "tests/misc_testsuite/simd", strategy)?; - test_directory_module(out, "tests/misc_testsuite/tail-call", strategy)?; - test_directory_module(out, "tests/misc_testsuite/threads", strategy)?; - test_directory_module(out, "tests/misc_testsuite/memory64", strategy)?; - test_directory_module(out, "tests/misc_testsuite/component-model", strategy)?; - test_directory_module(out, "tests/misc_testsuite/function-references", strategy)?; - // The testsuite of Winch is a subset of the official - // WebAssembly test suite, until parity is reached. This - // check is in place to prevent Cranelift from duplicating - // tests. - if *strategy == "Winch" { - test_directory_module(out, "tests/misc_testsuite/winch", strategy)?; - } - Ok(()) - })?; - - with_test_module(&mut out, "spec", |out| { - let spec_tests = test_directory(out, "tests/spec_testsuite", strategy)?; - // Skip running spec_testsuite tests if the submodule isn't checked - // out. - if spec_tests > 0 { - test_directory_module(out, "tests/spec_testsuite/proposals/memory64", strategy)?; - test_directory_module( - out, - "tests/spec_testsuite/proposals/function-references", - strategy, - )?; - test_directory_module( - out, - "tests/spec_testsuite/proposals/multi-memory", - strategy, - )?; - test_directory_module(out, "tests/spec_testsuite/proposals/threads", strategy)?; - test_directory_module( - out, - "tests/spec_testsuite/proposals/relaxed-simd", - strategy, - )?; - test_directory_module(out, "tests/spec_testsuite/proposals/tail-call", strategy)?; - } else { - println!( - "cargo:warning=The spec testsuite is disabled. To enable, run `git submodule \ - update --remote`." - ); - } - Ok(()) - })?; - - writeln!(out, "}}")?; - } - - // Write out our auto-generated tests and opportunistically format them with - // `rustfmt` if it's installed. - let output = out_dir.join("wast_testsuite_tests.rs"); - fs::write(&output, out)?; - drop(Command::new("rustfmt").arg(&output).status()); - Ok(()) -} - -fn test_directory_module( - out: &mut String, - path: impl AsRef, - strategy: &str, -) -> anyhow::Result { - let path = path.as_ref(); - let testsuite = &extract_name(path); - with_test_module(out, testsuite, |out| test_directory(out, path, strategy)) + set_commit_info_for_rustc(); } -fn test_directory( - out: &mut String, - path: impl AsRef, - strategy: &str, -) -> anyhow::Result { - let path = path.as_ref(); - let mut dir_entries: Vec<_> = path - .read_dir() - .context(format!("failed to read {:?}", path))? - .map(|r| r.expect("reading testsuite directory entry")) - .filter_map(|dir_entry| { - let p = dir_entry.path(); - let ext = p.extension()?; - // Only look at wast files. - if ext != "wast" { - return None; - } - // Ignore files starting with `.`, which could be editor temporary files - if p.file_stem()?.to_str()?.starts_with('.') { - return None; - } - Some(p) - }) - .collect(); - - dir_entries.sort(); - - let testsuite = &extract_name(path); - for entry in dir_entries.iter() { - write_testsuite_tests(out, entry, testsuite, strategy, false)?; - write_testsuite_tests(out, entry, testsuite, strategy, true)?; - } - - Ok(dir_entries.len()) -} - -/// Extract a valid Rust identifier from the stem of a path. -fn extract_name(path: impl AsRef) -> String { - path.as_ref() - .file_stem() - .expect("filename should have a stem") - .to_str() - .expect("filename should be representable as a string") - .replace(['-', '/'], "_") -} - -fn with_test_module( - out: &mut String, - testsuite: &str, - f: impl FnOnce(&mut String) -> anyhow::Result, -) -> anyhow::Result { - out.push_str("mod "); - out.push_str(testsuite); - out.push_str(" {\n"); - - let result = f(out)?; - - out.push_str("}\n"); - Ok(result) -} - -fn write_testsuite_tests( - out: &mut String, - path: impl AsRef, - testsuite: &str, - strategy: &str, - pooling: bool, -) -> anyhow::Result<()> { - let path = path.as_ref(); - let testname = extract_name(path); - - writeln!(out, "#[test]")?; - // Ignore when using QEMU for running tests (limited memory). - if ignore(testsuite, &testname, strategy) { - writeln!(out, "#[ignore]")?; - } else { - writeln!(out, "#[cfg_attr(miri, ignore)]")?; - } - - writeln!( - out, - "fn r#{}{}() {{", - &testname, - if pooling { "_pooling" } else { "" } - )?; - writeln!(out, " let _ = env_logger::try_init();")?; - writeln!( - out, - " crate::wast::run_wast(r#\"{}\"#, crate::wast::Strategy::{}, {}).unwrap();", - path.display(), - strategy, - pooling, - )?; - writeln!(out, "}}")?; - writeln!(out)?; - Ok(()) -} - -/// Ignore tests that aren't supported yet. -fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool { - assert!(strategy == "Cranelift" || strategy == "Winch"); - - // Ignore everything except the winch misc test suite. - // We ignore tests that assert for traps on windows, given - // that Winch doesn't encode unwind information for Windows, yet. - if strategy == "Winch" { - if testsuite == "misc_testsuite" { - // The misc/call_indirect is fully supported by Winch. - if testname == "call_indirect" { - return false; - } - } - if testsuite != "winch" { - return true; - } - - let assert_trap = ["i32", "i64", "call_indirect"].contains(&testname); - - if assert_trap && env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "windows" { - return true; - } - } - - // This is an empty file right now which the `wast` crate doesn't parse - if testname.contains("memory_copy1") { - return true; - } - - if testsuite == "function_references" { - // The following tests fail due to function references not yet - // being exposed in the public API. - if testname == "ref_null" || testname == "local_init" { - return true; - } - // This test fails due to incomplete support for the various - // table/elem syntactic sugar in wasm-tools/wast. - if testname == "br_table" { - return true; - } - // This test fails due to the current implementation of type - // canonicalisation being broken as a result of - // #[derive(hash)] on WasmHeapType. - if testname == "type_equivalence" { - return true; - } - } - - match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() { - "s390x" => { - // TODO(#6530): These tests require tail calls, but s390x - // doesn't support them yet. - testsuite == "function_references" || testsuite == "tail_call" - } - - "riscv64" => { - // This test case is disabled because it relies on `fvpromote_low` - // not flipping the sign bit of the input when it is a NaN. This - // is allowed by the spec. It's worth keeping the testcase as is - // since it is stressing a specific codegen bug in another arch. - // - // See #6961 for more details - testname == "issue_3327_bnot_lowering" - } - - _ => false, +fn set_commit_info_for_rustc() { + if !Path::new(".git").exists() { + return; } + let output = match Command::new("git") + .arg("log") + .arg("-1") + .arg("--date=short") + .arg("--format=%H %h %cd") + .arg("--abbrev=9") + .output() + { + Ok(output) if output.status.success() => output, + _ => return, + }; + let stdout = String::from_utf8(output.stdout).unwrap(); + let mut parts = stdout.split_whitespace(); + let mut next = || parts.next().unwrap(); + println!("cargo:rustc-env=WASMTIME_GIT_HASH={}", next()); + println!( + "cargo:rustc-env=WASMTIME_VERSION_INFO={} ({} {})", + env!("CARGO_PKG_VERSION"), + next(), + next() + ); } diff --git a/ci/RELEASES-template.md b/ci/RELEASES-template.md index f4c86db8478c..70b0e3b3424a 100644 --- a/ci/RELEASES-template.md +++ b/ci/RELEASES-template.md @@ -1,5 +1,3 @@ --------------------------------------------------------------------------------- - ## VERSION Unreleased. @@ -8,3 +6,4 @@ Unreleased. ### Changed +-------------------------------------------------------------------------------- diff --git a/ci/build-build-matrix.js b/ci/build-build-matrix.js new file mode 100644 index 000000000000..321eca46d9a2 --- /dev/null +++ b/ci/build-build-matrix.js @@ -0,0 +1,83 @@ +// Small script used to calculate the matrix of builds that are going to be +// done if CI decides to do a release build. +// +// This is a separate script primarily to write out all the release +// targets/platforms once and then duplicate them all with a "min" build. + +const array = [ + { + // The name of the build which shows up in the name of the artifact for + // Wasmtime's github releases. + "build": "x86_64-linux", + // The GitHub Actions platform that this build runs on + "os": "ubuntu-latest", + // The Rust target that will be used for the build. + "target": "x86_64-unknown-linux-gnu", + }, + { + "build": "aarch64-linux", + "os": "ubuntu-latest", + "target": "aarch64-unknown-linux-gnu", + }, + { + "build": "s390x-linux", + "os": "ubuntu-latest", + "target": "s390x-unknown-linux-gnu", + }, + { + "build": "riscv64gc-linux", + "os": "ubuntu-latest", + "target": "riscv64gc-unknown-linux-gnu", + }, + { + "build": "x86_64-macos", + "os": "macos-latest", + "target": "x86_64-apple-darwin", + }, + { + "build": "aarch64-macos", + "os": "macos-latest", + "target": "aarch64-apple-darwin", + }, + { + "build": "x86_64-windows", + "os": "windows-latest", + "target": "x86_64-pc-windows-msvc", + }, + { + "build": "x86_64-mingw", + "os": "windows-latest", + "target": "x86_64-pc-windows-gnu", + }, + { + "build": "aarch64-android", + "os": "ubuntu-latest", + "target": "aarch64-linux-android", + }, + { + "build": "x86_64-android", + "os": "ubuntu-latest", + "target": "x86_64-linux-android", + }, + { + "build": "x86_64-musl", + "os": "ubuntu-latest", + "target": "x86_64-unknown-linux-musl", + }, +]; + +const builds = []; +for (let build of array) { + // Perform a "deep clone" roundtripping through JSON for a copy of the build + // that's normal + build.rust = 'default'; + builds.push(JSON.parse(JSON.stringify(build))); + + // Next generate a "min" build and add it to the builds list. Min builds + // require Nightly rust due to some nightly build options that are configured. + build.build += '-min'; + build.rust = 'wasmtime-ci-pinned-nightly'; + builds.push(build); +} + +console.log(JSON.stringify(builds)); diff --git a/ci/build-release-artifacts.sh b/ci/build-release-artifacts.sh new file mode 100755 index 000000000000..cd91e430032b --- /dev/null +++ b/ci/build-release-artifacts.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# A script to build the release artifacts of Wasmtime into the `target` +# directory. For now this is the CLI and the C API. Note that this script only +# produces the artifacts through Cargo and doesn't package things up. That's +# intended for the `build-tarballs.sh` script. +# +# This script takes a Rust target as its first input and optionally a parameter +# afterwards which can be "-min" to indicate that a minimal build should be +# produced with as many features as possible stripped out. + +set -ex + +build=$1 +target=$2 + +# Default build flags for release artifacts. Leave debugging for +# builds-from-source which have richer information anyway, and additionally the +# CLI won't benefit from catching unwinds and neither will the C API so use +# panic=abort in both situations. +export CARGO_PROFILE_RELEASE_STRIP=debuginfo +export CARGO_PROFILE_RELEASE_PANIC=abort + +if [[ "$build" = *-min ]]; then + # Configure a whole bunch of compile-time options which help reduce the size + # of the binary artifact produced. + export CARGO_PROFILE_RELEASE_OPT_LEVEL=s + export RUSTFLAGS="-Zlocation-detail=none $RUSTFLAGS" + export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 + export CARGO_PROFILE_RELEASE_LTO=true + build_std=-Zbuild-std=std,panic_abort + build_std_features=-Zbuild-std-features=std_detect_dlsym_getauxval + flags="$build_std $build_std_features --no-default-features --features disable-logging" + cmake_flags="-DWASMTIME_DISABLE_ALL_FEATURES=ON" + cmake_flags="$cmake_flags -DWASMTIME_FEATURE_DISABLE_LOGGING=ON" + cmake_flags="$cmake_flags -DWASMTIME_USER_CARGO_BUILD_OPTIONS:LIST=$build_std;$build_std_features" +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" +fi + +cargo build --release $flags --target $target -p wasmtime-cli $bin_flags --features run + +mkdir -p target/c-api-build +cd target/c-api-build +cmake \ + ../../crates/c-api \ + $cmake_flags \ + -DCMAKE_BUILD_TYPE=Release \ + -DWASMTIME_TARGET=$target \ + -DCMAKE_INSTALL_PREFIX=../c-api-install \ + -DCMAKE_INSTALL_LIBDIR=../c-api-install/lib +cmake --build . --target install diff --git a/ci/build-src-tarball.sh b/ci/build-src-tarball.sh index 571fcc47a3a7..c5d3c10fa9b7 100755 --- a/ci/build-src-tarball.sh +++ b/ci/build-src-tarball.sh @@ -18,3 +18,5 @@ cargo vendor > .cargo/config.toml tar -czf /tmp/$pkgname.tar.gz --transform "s/^\./$pkgname/S" --exclude=.git . mkdir -p dist mv /tmp/$pkgname.tar.gz dist/ + +rm .cargo/config.toml diff --git a/ci/build-tarballs.sh b/ci/build-tarballs.sh index eb6197c2a5ac..b8e1684c0a13 100755 --- a/ci/build-tarballs.sh +++ b/ci/build-tarballs.sh @@ -1,19 +1,18 @@ #!/bin/bash # A small script used for assembling release tarballs for both the `wasmtime` -# binary and the C API. This is executed with two arguments, mostly coming from -# the CI matrix. +# binary and the C API. This is executed with two arguments, mostly coming +# from the CI matrix. # -# * The first argument is the name of the platform, used to name the release -# * The second argument is the "target", if present, currently only for -# cross-compiles +# * The first argument is the name of the "build", used to name the release. +# * The second argument is the Rust target that the build was performed for. # # This expects the build to already be done and will assemble release artifacts # in `dist/` set -ex -platform=$1 +build=$1 target=$2 rm -rf tmp @@ -25,59 +24,69 @@ if [[ $GITHUB_REF == refs/heads/release-* ]]; then tag=v$(./ci/print-current-version.sh) fi -bin_pkgname=wasmtime-$tag-$platform -api_pkgname=wasmtime-$tag-$platform-c-api +# For *-min builds produce the same named artifacts as the normal build and +# they'll get unioned together in a later step in the CI. +build_pkgname=$build +if [[ $build == *-min ]]; then + build_pkgname=${build%-min} +fi + +bin_pkgname=wasmtime-$tag-$build_pkgname +api_pkgname=wasmtime-$tag-$build_pkgname-c-api +api_install=target/c-api-install mkdir tmp/$api_pkgname -mkdir tmp/$api_pkgname/lib -mkdir tmp/$api_pkgname/include mkdir tmp/$bin_pkgname cp LICENSE README.md tmp/$api_pkgname cp LICENSE README.md tmp/$bin_pkgname -cp -r crates/c-api/include tmp/$api_pkgname -cp crates/c-api/wasm-c-api/include/wasm.h tmp/$api_pkgname/include -fmt=tar -if [ "$platform" = "x86_64-windows" ]; then - cp target/release/wasmtime.exe tmp/$bin_pkgname - cp target/release/{wasmtime.dll,wasmtime.lib,wasmtime.dll.lib} tmp/$api_pkgname/lib - fmt=zip - - # Generate a `*.msi` installer for Windows as well - export WT_VERSION=`cat Cargo.toml | sed -n 's/^version = "\([^"]*\)".*/\1/p'` - "$WIX/bin/candle" -arch x64 -out target/wasmtime.wixobj ci/wasmtime.wxs - "$WIX/bin/light" -out dist/$bin_pkgname.msi target/wasmtime.wixobj -ext WixUtilExtension - rm dist/$bin_pkgname.wixpdb -elif [ "$platform" = "x86_64-mingw" ]; then - cp target/x86_64-pc-windows-gnu/release/wasmtime.exe tmp/$bin_pkgname - cp target/x86_64-pc-windows-gnu/release/{wasmtime.dll,libwasmtime.a,libwasmtime.dll.a} tmp/$api_pkgname/lib - fmt=zip -elif [ "$platform" = "x86_64-macos" ]; then - # Postprocess the macOS dylib a bit to have a more reasonable `LC_ID_DYLIB` - # directive than the default one that comes out of the linker when typically - # doing `cargo build`. For more info see #984 - install_name_tool -id "@rpath/libwasmtime.dylib" target/release/libwasmtime.dylib - cp target/release/wasmtime tmp/$bin_pkgname - cp target/release/libwasmtime.{a,dylib} tmp/$api_pkgname/lib -elif [ "$platform" = "aarch64-macos" ]; then - install_name_tool -id "@rpath/libwasmtime.dylib" target/aarch64-apple-darwin/release/libwasmtime.dylib - cp target/aarch64-apple-darwin/release/wasmtime tmp/$bin_pkgname - cp target/aarch64-apple-darwin/release/libwasmtime.{a,dylib} tmp/$api_pkgname/lib -elif [ "$target" = "" ]; then - cp target/release/wasmtime tmp/$bin_pkgname - cp target/release/libwasmtime.{a,so} tmp/$api_pkgname/lib +# For *-min builds rename artifacts with a `-min` suffix to avoid eventual +# clashes with the normal builds when the tarballs are unioned together. +if [[ $build == *-min ]]; then + min="-min" + cp -r $api_install/include tmp/$api_pkgname/min + cp -r $api_install/lib tmp/$api_pkgname/min else - cp target/$target/release/wasmtime tmp/$bin_pkgname - cp target/$target/release/libwasmtime.{a,so} tmp/$api_pkgname/lib + cp -r $api_install/include tmp/$api_pkgname + cp -r $api_install/lib tmp/$api_pkgname fi +fmt=tar + +case $build in + x86_64-windows*) + cp target/$target/release/wasmtime.exe tmp/$bin_pkgname/wasmtime$min.exe + fmt=zip + + if [ "$min" = "" ]; then + # Generate a `*.msi` installer for Windows as well + export WT_VERSION=`cat Cargo.toml | sed -n 's/^version = "\([^"]*\)".*/\1/p'` + "$WIX/bin/candle" -arch x64 -out target/wasmtime.wixobj ci/wasmtime.wxs + "$WIX/bin/light" -out dist/$bin_pkgname.msi target/wasmtime.wixobj -ext WixUtilExtension + rm dist/$bin_pkgname.wixpdb + fi + ;; + + x86_64-mingw*) + cp target/$target/release/wasmtime.exe tmp/$bin_pkgname/wasmtime$min.exe + fmt=zip + ;; + + *-macos*) + cp target/$target/release/wasmtime tmp/$bin_pkgname/wasmtime$min + ;; + + *) + cp target/$target/release/wasmtime tmp/$bin_pkgname/wasmtime$min + ;; +esac + + mktarball() { dir=$1 if [ "$fmt" = "tar" ]; then - # this is a bit wonky, but the goal is to use `xz` with threaded compression - # to ideally get better performance with the `-T0` flag. - tar -cvf - -C tmp $dir | xz -9 -T0 > dist/$dir.tar.xz + tar -czvf dist/$dir.tar.gz -C tmp $dir else # Note that this runs on Windows, and it looks like GitHub Actions doesn't # have a `zip` tool there, so we use something else diff --git a/ci/build-test-matrix.js b/ci/build-test-matrix.js index 298203f5d4de..b1c0b73c7fd7 100644 --- a/ci/build-test-matrix.js +++ b/ci/build-test-matrix.js @@ -5,58 +5,92 @@ // couldn't figure out how to write it in bash. const fs = require('fs'); +const { spawn } = require('node:child_process'); -// Our first argument is a file that is a giant json blob which contains at -// least all the messages for all of the commits that were a part of this PR. -// This is used to test if any commit message includes a string. -const commits = fs.readFileSync(process.argv[2]).toString(); +// Number of generic buckets to shard crates into. Note that we additionally add +// single-crate buckets for our biggest crates. +const GENERIC_BUCKETS = 3; -// The second argument is a file that contains the names of all files modified -// for a PR, used for file-based filters. -const names = fs.readFileSync(process.argv[3]).toString(); +// Crates which are their own buckets. These are the very slowest to +// compile-and-test crates. +const SINGLE_CRATE_BUCKETS = ["wasmtime", "wasmtime-cli", "wasmtime-wasi"]; -// This is the full matrix of what we test on CI. This includes a number of -// platforms and a number of cross-compiled targets that are emulated with QEMU. -// This must be kept tightly in sync with the `test` step in `main.yml`. +// This is the small, fast-to-execute matrix we use for PRs before they enter +// the merge queue. Same schema as `FULL_MATRIX`. +const FAST_MATRIX = [ + { + "os": "ubuntu-latest", + "name": "Test Linux x86_64", + "filter": "linux-x64", + "isa": "x64", + }, +]; + +// Returns whether the given package supports a 32-bit architecture, used when +// testing on i686 and armv7 below. +function supports32Bit(pkg) { + if (pkg.indexOf("pulley") !== -1) + return true; + + return pkg == 'wasmtime-fiber'; +} + +// This is the full, unsharded, and unfiltered matrix of what we test on +// CI. This includes a number of platforms and a number of cross-compiled +// targets that are emulated with QEMU. This must be kept tightly in sync with +// the `test` step in `main.yml`. // // The supported keys here are: // // * `os` - the github-actions name of the runner os +// // * `name` - the human-readable name of the job +// // * `filter` - a string which if `prtest:$filter` is in the commit messages // it'll force running this test suite on PR CI. +// +// * `isa` - changes to `cranelift/codegen/src/$isa` will automatically run this +// test suite. +// // * `target` - used for cross-compiles if present. Effectively Cargo's // `--target` option for all its operations. +// // * `gcc_package`, `gcc`, `qemu`, `qemu_target` - configuration for building // QEMU and installing cross compilers to execute a cross-compiled test suite // on CI. -// * `isa` - changes to `cranelift/codegen/src/$isa` will automatically run this -// test suite. +// // * `rust` - the Rust version to install, and if unset this'll be set to // `default` -const array = [ +const FULL_MATRIX = [ + ...FAST_MATRIX, { "os": "ubuntu-latest", - "name": "Test Linux x86_64", + "name": "Test MSRV on Linux x86_64", "filter": "linux-x64", - "isa": "x64" + "isa": "x64", + "rust": "msrv", }, { "os": "ubuntu-latest", - "name": "Test MSRV on Linux x86_64", + "name": "Test Linux x86_64 with MPK", "filter": "linux-x64", - "isa": "x64", - "rust": "msrv", + "isa": "x64" }, { - "os": "macos-latest", + "os": "macos-13", "name": "Test macOS x86_64", - "filter": "macos-x64" + "filter": "macos-x64", + }, + { + "os": "macos-14", + "name": "Test macOS arm64", + "filter": "macos-arm64", + "target": "aarch64-apple-darwin", }, { "os": "windows-latest", "name": "Test Windows MSVC x86_64", - "filter": "windows-x64" + "filter": "windows-x64", }, { "os": "windows-latest", @@ -73,7 +107,7 @@ const array = [ "qemu_target": "aarch64-linux-user", "name": "Test Linux arm64", "filter": "linux-arm64", - "isa": "aarch64" + "isa": "aarch64", }, { "os": "ubuntu-latest", @@ -91,58 +125,206 @@ const array = [ "target": "riscv64gc-unknown-linux-gnu", "gcc_package": "gcc-riscv64-linux-gnu", "gcc": "riscv64-linux-gnu-gcc", - "qemu": "qemu-riscv64 -cpu rv64,v=true,vlen=256,vext_spec=v1.0,zba=true,zbb=true,zbc=true,zbs=true,zbkb=true,zcb=true -L /usr/riscv64-linux-gnu", + "qemu": "qemu-riscv64 -cpu rv64,v=true,vlen=256,vext_spec=v1.0,Zfa=true,Zfh=true,zba=true,zbb=true,zbc=true,zbs=true,zbkb=true,zcb=true,x-zicond=true -L /usr/riscv64-linux-gnu", "qemu_target": "riscv64-linux-user", "name": "Test Linux riscv64", "filter": "linux-riscv64", "isa": "riscv64", - // There appears to be a miscompile in Rust 1.72 for riscv64 where - // wasmtime-wasi tests are segfaulting in CI with the stack pointing in - // Tokio. Updating rustc seems to do the trick, so without doing a full - // rigorous investigation this uses beta for now but Rust 1.73 should be - // good to go for this. - "rust": "beta-2023-09-10", - } + }, + { + "name": "Tests on i686-unknown-linux-gnu", + "32-bit": true, + "os": "ubuntu-latest", + "target": "i686-unknown-linux-gnu", + "gcc_package": "gcc-i686-linux-gnu", + "gcc": "i686-linux-gnu-gcc", + }, + { + "name": "Tests on armv7-unknown-linux-gnueabihf", + "32-bit": true, + "os": "ubuntu-latest", + "target": "armv7-unknown-linux-gnueabihf", + "gcc_package": "gcc-arm-linux-gnueabihf", + "gcc": "arm-linux-gnueabihf-gcc", + "qemu": "qemu-arm -L /usr/arm-linux-gnueabihf -E LD_LIBRARY_PATH=/usr/arm-linux-gnueabihf/lib", + "qemu_target": "arm-linux-user", + }, ]; -for (let config of array) { - if (config.rust === undefined) { - config.rust = 'default'; +/// Get the workspace's full list of member crates. +async function getWorkspaceMembers() { + // Spawn a `cargo metadata` subprocess, accumulate its JSON output from + // `stdout`, and wait for it to exit. + const child = spawn("cargo", ["metadata"], { encoding: "utf8" }); + let data = ""; + child.stdout.on("data", chunk => data += chunk); + await new Promise((resolve, reject) => { + child.on("close", resolve); + child.on("error", reject); + }); + + // Get the names of the crates in the workspace from the JSON metadata by + // building a package-id to name map and then translating the package-ids + // listed as workspace members. + const metadata = JSON.parse(data); + const id_to_name = {}; + for (const pkg of metadata.packages) { + id_to_name[pkg.id] = pkg.name; } + return metadata.workspace_members.map(m => id_to_name[m]); } -function myFilter(item) { - if (item.isa && names.includes(`cranelift/codegen/src/isa/${item.isa}`)) { - return true; +/// For each given target configuration, shard the workspace's crates into +/// buckets across that config. +/// +/// This is essentially a `flat_map` where each config that logically tests all +/// crates in the workspace is mapped to N sharded configs that each test only a +/// subset of crates in the workspace. Each sharded config's subset of crates to +/// test are disjoint from all its siblings, and the union of all these siblings' +/// crates to test is the full workspace members set. +/// +/// With some poetic license around a `crates_to_test` key that doesn't actually +/// exist, logically each element of the input `configs` list gets transformed +/// like this: +/// +/// { os: "ubuntu-latest", isa: "x64", ..., crates: "all" } +/// +/// ==> +/// +/// [ +/// { os: "ubuntu-latest", isa: "x64", ..., crates: ["wasmtime"] }, +/// { os: "ubuntu-latest", isa: "x64", ..., crates: ["wasmtime-cli"] }, +/// { os: "ubuntu-latest", isa: "x64", ..., crates: ["wasmtime-wasi"] }, +/// { os: "ubuntu-latest", isa: "x64", ..., crates: ["cranelift", "cranelift-codegen", ...] }, +/// { os: "ubuntu-latest", isa: "x64", ..., crates: ["wasmtime-slab", "cranelift-entity", ...] }, +/// { os: "ubuntu-latest", isa: "x64", ..., crates: ["cranelift-environ", "wasmtime-cli-flags", ...] }, +/// ... +/// ] +/// +/// Note that `crates: "all"` is implicit in the input and omitted. Similarly, +/// `crates: [...]` in each output config is actually implemented via adding a +/// `bucket` key, which contains the CLI flags we must pass to `cargo` to run +/// tests for just this config's subset of crates. +async function shard(configs) { + const members = await getWorkspaceMembers(); + + // Divide the workspace crates into N disjoint subsets. Crates that are + // particularly expensive to compile and test form their own singleton subset. + const buckets = Array.from({ length: GENERIC_BUCKETS }, _ => new Set()); + let i = 0; + for (const crate of members) { + if (SINGLE_CRATE_BUCKETS.indexOf(crate) != -1) continue; + buckets[i].add(crate); + i = (i + 1) % GENERIC_BUCKETS; } - if (item.filter && commits.includes(`prtest:${item.filter}`)) { - return true; + for (crate of SINGLE_CRATE_BUCKETS) { + buckets.push(new Set([crate])); } - // If any runtest was modified, re-run the whole test suite as those can - // target any backend. - if (names.includes(`cranelift/filetests/filetests/runtests`)) { - return true; - } + // For each config, expand it into N configs, one for each disjoint set we + // created above. + const sharded = []; + for (const config of configs) { + // Special case 32-bit configs. Only some crates, according to + // `supports32Bit`, run on this target. At this time the set of supported + // crates is small enough that they're not sharded. + if (config["32-bit"] === true) { + sharded.push(Object.assign( + {}, + config, + { + bucket: members + .map(c => supports32Bit(c) ? `--package ${c}` : `--exclude ${c}`) + .join(" "), + } + )); + continue; + } - return false; + for (const bucket of buckets) { + sharded.push(Object.assign( + {}, + config, + { + name: `${config.name} (${Array.from(bucket).join(', ')})`, + // We run tests via `cargo test --workspace`, so exclude crates that + // aren't in this bucket, rather than naming only the crates that are + // in this bucket. + bucket: members + .map(c => bucket.has(c) ? `--package ${c}` : `--exclude ${c}`) + .join(" "), + } + )); + } + } + return sharded; } -const filtered = array.filter(myFilter); +async function main() { + // Our first argument is a file that is a giant json blob which contains at + // least all the messages for all of the commits that were a part of this PR. + // This is used to test if any commit message includes a string. + const commits = fs.readFileSync(process.argv[2]).toString(); -// If the optional third argument to this script is `true` then that means all -// tests are being run and no filtering should happen. -if (process.argv[4] == 'true') { - console.log(JSON.stringify(array)); - return; -} + // The second argument is a file that contains the names of all files modified + // for a PR, used for file-based filters. + const names = fs.readFileSync(process.argv[3]).toString(); + + for (let config of FULL_MATRIX) { + if (config.rust === undefined) { + config.rust = 'default'; + } + } + + // If the optional third argument to this script is `true` then that means all + // tests are being run and no filtering should happen. + if (process.argv[4] == 'true') { + console.log(JSON.stringify(await shard(FULL_MATRIX), undefined, 2)); + return; + } + + // When we aren't running the full CI matrix, filter configs down to just the + // relevant bits based on files changed in this commit or if the commit asks + // for a certain config to run. + const filtered = FULL_MATRIX.filter(config => { + // If an ISA-specific test was modified, then include that ISA config. + if (config.isa && names.includes(`cranelift/codegen/src/isa/${config.isa}`)) { + return true; + } + + // If any runtest was modified, include all ISA configs as runtests can + // target any backend. + if (names.includes(`cranelift/filetests/filetests/runtests`)) { + if (config.isa !== undefined) + return true; + } + + // For matrix entries that represent 32-bit only some crates support that, + // so whenever the crates are changed be sure to run 32-bit tests on PRs + // too. + if (config["32-bit"] === true) { + if (names.includes("pulley")) + return true; + if (names.includes("fiber")) + return true; + } + + // If the commit explicitly asks for this test config, then include it. + if (config.filter && commits.includes(`prtest:${config.filter}`)) { + return true; + } + + return false; + }); + + // If at least one test is being run via our filters then run those tests. + if (filtered.length > 0) { + console.log(JSON.stringify(await shard(filtered), undefined, 2)); + return; + } -// If at least one test is being run via our filters then run those tests. -if (filtered.length > 0) { - console.log(JSON.stringify(filtered)); - return; + // Otherwise if nothing else is being run, run the fast subset of the matrix. + console.log(JSON.stringify(await shard(FAST_MATRIX), undefined, 2)); } -// Otherwise if nothing else is being run, run the first one which is Ubuntu -// Linux which should be the fastest for now. -console.log(JSON.stringify([array[0]])); +main() diff --git a/ci/build-wasi-preview1-component-adapter-provider.sh b/ci/build-wasi-preview1-component-adapter-provider.sh new file mode 100755 index 000000000000..c8c2bf4d2086 --- /dev/null +++ b/ci/build-wasi-preview1-component-adapter-provider.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -ex + +# Manifest the adapter provider into the workspace +cp crates/wasi-preview1-component-adapter/provider/Cargo.toml.in crates/wasi-preview1-component-adapter/provider/Cargo.toml +sed -i '/"crates\/wasi-preview1-component-adapter",/a\ \ "crates\/wasi-preview1-component-adapter\/provider",' Cargo.toml + +# Check the adapter provider's code formatting and style +cargo fmt -p wasi-preview1-component-adapter-provider -- --check +cargo check -p wasi-preview1-component-adapter-provider +cargo clippy -p wasi-preview1-component-adapter-provider + +# Check that publishing the adapter provider should work +cargo publish -p wasi-preview1-component-adapter-provider --dry-run --allow-dirty diff --git a/ci/build-wasi-preview1-component-adapter.sh b/ci/build-wasi-preview1-component-adapter.sh index ba1380d3bcd8..545a4e06f389 100755 --- a/ci/build-wasi-preview1-component-adapter.sh +++ b/ci/build-wasi-preview1-component-adapter.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -ex +# These flags reduce binary size by a combined 4.6k +export CARGO_PROFILE_RELEASE_LTO=fat +export CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS="$RUSTFLAGS -Ctarget-feature=+bulk-memory" + build_adapter="cargo build -p wasi-preview1-component-adapter --target wasm32-unknown-unknown" verify="cargo run -p verify-component-adapter --" @@ -11,18 +15,28 @@ release="target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm" $build_adapter $verify $debug +build() { + input=$1 + flavor=$2 + $verify $input + name=wasi_snapshot_preview1.$flavor.wasm + dst=$(dirname $input)/$name + wasm-tools metadata add --name "wasi_preview1_component_adapter.$flavor.adapter" $input \ + -o $dst +} + # Debug build, command $build_adapter --no-default-features --features command $verify $debug # Release build, command $build_adapter --release --no-default-features --features command -$verify $release -wasm-tools metadata add --name "wasi_preview1_component_adapter.command.adapter:${VERSION}" $release \ - -o target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.command.wasm +build $release command # Release build, default features (reactor) $build_adapter --release -$verify $release -wasm-tools metadata add --name "wasi_preview1_component_adapter.reactor.adapter:${VERSION}" $release \ - -o target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.reactor.wasm +build $release reactor + +# Release build, proxy +$build_adapter --release --no-default-features --features proxy +build $release proxy diff --git a/ci/docker/aarch64-linux/Dockerfile b/ci/docker/aarch64-linux/Dockerfile index 06c7cefc8d6a..4573e830e435 100644 --- a/ci/docker/aarch64-linux/Dockerfile +++ b/ci/docker/aarch64-linux/Dockerfile @@ -1,7 +1,11 @@ FROM ubuntu:16.04 -RUN apt-get update -y && apt-get install -y gcc gcc-aarch64-linux-gnu ca-certificates +RUN apt-get update -y && apt-get install -y gcc gcc-aarch64-linux-gnu ca-certificates curl make git +RUN git config --global --add safe.directory '*' + +# The CMake in Ubuntu 16.04 was a bit too old for us to use so download one from +# CMake's own releases and use that instead. +RUN curl -L https://github.com/Kitware/CMake/releases/download/v3.29.3/cmake-3.29.3-linux-x86_64.tar.gz | tar xzf - +ENV PATH=$PATH:/cmake-3.29.3-linux-x86_64/bin -ENV PATH=$PATH:/rust/bin -ENV CARGO_BUILD_TARGET=aarch64-unknown-linux-gnu ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc diff --git a/ci/docker/riscv64gc-linux/Dockerfile b/ci/docker/riscv64gc-linux/Dockerfile index 522867a67cb7..c10524d862b0 100644 --- a/ci/docker/riscv64gc-linux/Dockerfile +++ b/ci/docker/riscv64gc-linux/Dockerfile @@ -1,7 +1,6 @@ FROM ubuntu:22.04 -RUN apt-get update -y && apt-get install -y gcc gcc-riscv64-linux-gnu ca-certificates +RUN apt-get update -y && apt-get install -y gcc gcc-riscv64-linux-gnu ca-certificates cmake git +RUN git config --global --add safe.directory '*' -ENV PATH=$PATH:/rust/bin -ENV CARGO_BUILD_TARGET=riscv64gc-unknown-linux-gnu ENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc diff --git a/ci/docker/s390x-linux/Dockerfile b/ci/docker/s390x-linux/Dockerfile index 19acf1d8255b..6c97b174dea2 100644 --- a/ci/docker/s390x-linux/Dockerfile +++ b/ci/docker/s390x-linux/Dockerfile @@ -1,7 +1,11 @@ FROM ubuntu:16.04 -RUN apt-get update -y && apt-get install -y gcc gcc-s390x-linux-gnu ca-certificates +RUN apt-get update -y && apt-get install -y gcc gcc-s390x-linux-gnu ca-certificates curl make git +RUN git config --global --add safe.directory '*' + +# The CMake in Ubuntu 16.04 was a bit too old for us to use so download one from +# CMake's own releases and use that instead. +RUN curl -L https://github.com/Kitware/CMake/releases/download/v3.29.3/cmake-3.29.3-linux-x86_64.tar.gz | tar xzf - +ENV PATH=$PATH:/cmake-3.29.3-linux-x86_64/bin -ENV PATH=$PATH:/rust/bin -ENV CARGO_BUILD_TARGET=s390x-unknown-linux-gnu ENV CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_LINKER=s390x-linux-gnu-gcc diff --git a/ci/docker/x86_64-linux/Dockerfile b/ci/docker/x86_64-linux/Dockerfile index 89ddc2b2820b..add422150508 100644 --- a/ci/docker/x86_64-linux/Dockerfile +++ b/ci/docker/x86_64-linux/Dockerfile @@ -1,5 +1,4 @@ -FROM centos:7 +FROM almalinux:8 -RUN yum install -y git gcc - -ENV PATH=$PATH:/rust/bin +RUN dnf install -y git gcc make cmake git +RUN git config --global --add safe.directory '*' diff --git a/ci/docker/x86_64-musl/Dockerfile b/ci/docker/x86_64-musl/Dockerfile new file mode 100644 index 000000000000..acd6dd88d981 --- /dev/null +++ b/ci/docker/x86_64-musl/Dockerfile @@ -0,0 +1,16 @@ +# Rust binaries need `libgcc_s.so` but ubuntu's musl toolchain does not have it. +# Get it from alpine instead. +FROM alpine:3.16 as libgcc_s_src +RUN apk add libgcc + +# Use something glibc-based for the actual compile because the Rust toolchain +# we're using is glibc-based in CI. +FROM ubuntu:24.04 +RUN apt-get update -y && apt-get install -y cmake musl-tools git +COPY --from=libgcc_s_src /usr/lib/libgcc_s.so.1 /usr/lib/x86_64-linux-musl +RUN git config --global --add safe.directory '*' + +# Note that `-crt-feature` is passed here to specifically disable static linking +# with musl. We want a `*.so` to pop out so static linking isn't what we want. +ENV EXTRA_RUSTFLAGS=-Ctarget-feature=-crt-static +ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc diff --git a/ci/merge-artifacts.sh b/ci/merge-artifacts.sh new file mode 100755 index 000000000000..57200c0f6af1 --- /dev/null +++ b/ci/merge-artifacts.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Script to merge the outputs of a run on github actions to github releases. +# This is invoked from `.github/workflows/publish-artifacts.yml`. All previous +# artifacts from builds are located in `bins-*` folders. The main purpose of +# this script is to take the "min" build and merge it into the "normal" build to +# produce one final tarball. This means that the final artifacts will have both +# a normal and a min build in them for comparison and usage. + +set -ex + +# Prepare the upload folder and move all artifacts that aren't being merged into +# this folder, e.g. the MSI installer and adapter wasm files. +rm -rf dist +mkdir dist +mv -t dist bins-*/*.{msi,wasm} +mv wasmtime-platform-header/* dist + +# Merge tarballs and zips by searching for `*-min` builds, unpacking the +# min/normal builds, into the same destination, and then repacking into a +# tarball. +# +# Note that for now xz compression is used for the final artifact to try to get +# small artifacts, but it's left at the default level since a lot of artifacts +# are processed here and turning it up to the max 9 compression might take +# quite awhile on CI for this one builder to process. +for min in bins-*-min/*.tar.*; do + normal=${min/-min\//\/} + filename=$(basename $normal) + dir=${filename%.tar.gz} + + rm -rf tmp + mkdir tmp + tar xf $min -C tmp + tar xf $normal -C tmp + tar -cf - -C tmp $dir | xz -T0 > dist/$dir.tar.xz + rm $min $normal +done + +for min in bins-*-min/*.zip; do + normal=${min/-min\//\/} + filename=$(basename $normal) + dir=${filename%.zip} + + rm -rf tmp + mkdir tmp + (cd tmp && unzip -o ../$min) + (cd tmp && unzip -o ../$normal) + (cd tmp && 7z a ../dist/$dir.zip $dir/) + rm $min $normal +done + +# Copy over remaining source tarball into the dist folder +mv -t dist bins-*/*.tar.* diff --git a/ci/run-tests.sh b/ci/run-tests.sh index 568ff4405413..274d35232177 100755 --- a/ci/run-tests.sh +++ b/ci/run-tests.sh @@ -1,15 +1,23 @@ -#!/bin/bash +#!/usr/bin/env bash + +# Excludes: +# +# - test-programs: just programs used in tests. +# +# - wasmtime-wasi-nn: mutually-exclusive features that aren't available for all +# targets, needs its own CI job. +# +# - wasmtime-fuzzing: enabling all features brings in OCaml which is a pain to +# configure for all targets, so it has its own CI job. +# +# - wasm-spec-interpreter: brings in OCaml which is a pain to configure for all +# targets, tested as part of the wastime-fuzzing CI job. cargo test \ - --features "test-programs/test_programs" \ - --features wasi-threads \ - --features wasi-http \ - --features component-model \ - --features serve \ - --workspace \ - --exclude 'wasmtime-wasi-*' \ - --exclude wasi-tests \ - --exclude wasi-http-tests \ - --exclude command-tests \ - --exclude reactor-tests \ - $@ + --workspace \ + --all-features \ + --exclude test-programs \ + --exclude wasmtime-wasi-nn \ + --exclude wasmtime-fuzzing \ + --exclude wasm-spec-interpreter \ + $@ diff --git a/ci/run-wasi-nn-example.sh b/ci/run-wasi-nn-example.sh deleted file mode 100755 index 74bab3bd30fc..000000000000 --- a/ci/run-wasi-nn-example.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# The following script demonstrates how to execute a machine learning inference -# using the wasi-nn module optionally compiled into Wasmtime. Calling it will -# download the necessary model and tensor files stored separately in $FIXTURE -# into $TMP_DIR (optionally pass a directory with existing files as the first -# argument to re-try the script). Then, it will compile and run several examples -# in the Wasmtime CLI. -set -e -WASMTIME_DIR=$(dirname "$0" | xargs dirname) -FIXTURE=https://github.com/intel/openvino-rs/raw/main/crates/openvino/tests/fixtures/mobilenet -if [ -z "${1+x}" ]; then - # If no temporary directory is specified, create one. - TMP_DIR=$(mktemp -d -t ci-XXXXXXXXXX) - REMOVE_TMP_DIR=1 -else - # If a directory was specified, use it and avoid removing it. - TMP_DIR=$(realpath $1) - REMOVE_TMP_DIR=0 -fi - -# One of the examples expects to be in a specifically-named directory. -mkdir -p $TMP_DIR/mobilenet -TMP_DIR=$TMP_DIR/mobilenet - -# Build Wasmtime with wasi-nn enabled; we attempt this first to avoid extra work -# if the build fails. -cargo build -p wasmtime-cli --features wasi-nn - -# Download all necessary test fixtures to the temporary directory. -wget --no-clobber $FIXTURE/mobilenet.bin --output-document=$TMP_DIR/model.bin -wget --no-clobber $FIXTURE/mobilenet.xml --output-document=$TMP_DIR/model.xml -wget --no-clobber $FIXTURE/tensor-1x224x224x3-f32.bgr --output-document=$TMP_DIR/tensor.bgr - -# Now build an example that uses the wasi-nn API. Run the example in Wasmtime -# (note that the example uses `fixture` as the expected location of the -# model/tensor files). -pushd $WASMTIME_DIR/crates/wasi-nn/examples/classification-example -cargo build --release --target=wasm32-wasi -cp target/wasm32-wasi/release/wasi-nn-example.wasm $TMP_DIR -popd -cargo run -- run --dir fixture::$TMP_DIR -S nn $TMP_DIR/wasi-nn-example.wasm - -# Build and run another example, this time using Wasmtime's graph flag to -# preload the model. -pushd $WASMTIME_DIR/crates/wasi-nn/examples/classification-example-named -cargo build --release --target=wasm32-wasi -cp target/wasm32-wasi/release/wasi-nn-example-named.wasm $TMP_DIR -popd -cargo run -- run --dir fixture::$TMP_DIR -S nn,nn-graph=openvino::$TMP_DIR \ - $TMP_DIR/wasi-nn-example-named.wasm - -# Clean up the temporary directory only if it was not specified (users may want -# to keep the directory around). -if [[ $REMOVE_TMP_DIR -eq 1 ]]; then - rm -rf $TMP_DIR -fi diff --git a/ci/update-release-date.rs b/ci/update-release-date.rs deleted file mode 100644 index 72509bd245bb..000000000000 --- a/ci/update-release-date.rs +++ /dev/null @@ -1,18 +0,0 @@ -fn main() { - let date = std::env::args().nth(1).unwrap(); - let relnotes = std::fs::read_to_string("RELEASES.md").unwrap(); - let mut new_relnotes = String::new(); - let mut counter = 0; - for line in relnotes.lines() { - if line.starts_with("Unreleased") { - counter += 1; - if counter == 2 { - new_relnotes.push_str(&format!("Released {}\n", date)); - continue; - } - } - new_relnotes.push_str(line); - new_relnotes.push_str("\n"); - } - std::fs::write("RELEASES.md", new_relnotes).unwrap(); -} diff --git a/ci/vendor-c-api-headers.sh b/ci/vendor-c-api-headers.sh new file mode 100755 index 000000000000..fa0b417927d7 --- /dev/null +++ b/ci/vendor-c-api-headers.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# Script to re-vendor the headers from the `wasm-c-api` proposal. + +rev="2ce1367c9d1271c83fb63bef26d896a2f290cd23" +files="wasm.h wasm.hh" + +set -ex + +for file in $files; do + dst=crates/c-api/include/$file + pretty_url=https://github.com/WebAssembly/wasm-c-api/blob/$rev/include/$file + url=https://raw.githubusercontent.com/WebAssembly/wasm-c-api/$rev/include/$file + cat >$dst <<-EOF +// Wasmtime-vendored copy of this header file as present in upstream: +// <$pretty_url> +// +// Wasmtime maintainers can update this vendored copy in-tree with the +// ./ci/vendor-c-api-headers.sh shell script. +// +EOF + curl $url >> $dst +done diff --git a/ci/vendor-wit.sh b/ci/vendor-wit.sh new file mode 100755 index 000000000000..55ffdf2a4072 --- /dev/null +++ b/ci/vendor-wit.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Script to re-vendor the WIT files that Wasmtime uses as defined by a +# particular tag in upstream repositories. +# +# This script is executed on CI to ensure that everything is up-to-date. +set -ex + +# The make_vendor function takes a base path (e.g., "wasi") and a list +# of packages in the format "name@tag". It constructs the full destination +# path, downloads the tarballs from GitHub, extracts the relevant files, and +# removes any unwanted directories. +make_vendor() { + local name=$1 + local packages=$2 + local path="crates/$name/wit/deps" + + rm -rf $path + mkdir -p $path + + for package in $packages; do + IFS='@' read -r repo tag <<< "$package" + mkdir -p $path/$repo + cached_extracted_dir="$cache_dir/$repo-$tag" + + if [[ ! -d $cached_extracted_dir ]]; then + mkdir -p $cached_extracted_dir + curl -sL https://github.com/WebAssembly/wasi-$repo/archive/$tag.tar.gz | \ + tar xzf - --strip-components=1 -C $cached_extracted_dir + rm -rf $cached_extracted_dir/wit/deps* + fi + + cp -r $cached_extracted_dir/wit/* $path/$repo + done +} + +cache_dir=$(mktemp -d) + +make_vendor "wasi" " + cli@v0.2.1 + clocks@v0.2.1 + filesystem@v0.2.1 + io@v0.2.1 + random@v0.2.1 + sockets@v0.2.1 +" + +make_vendor "wasi-http" " + cli@v0.2.1 + clocks@v0.2.1 + filesystem@v0.2.1 + io@v0.2.1 + random@v0.2.1 + sockets@v0.2.1 + http@v0.2.1 +" + +make_vendor "wasi-runtime-config" "runtime-config@c667fe6" + +make_vendor "wasi-keyvalue" "keyvalue@219ea36" + +rm -rf $cache_dir + +# Separately (for now), vendor the `wasi-nn` WIT files since their retrieval is +# slightly different than above. +repo=https://raw.githubusercontent.com/WebAssembly/wasi-nn +revision=0.2.0-rc-2024-08-19 +curl -L $repo/$revision/wasi-nn.witx -o crates/wasi-nn/witx/wasi-nn.witx +curl -L $repo/$revision/wit/wasi-nn.wit -o crates/wasi-nn/wit/wasi-nn.wit diff --git a/ci/wasmtime.wxs b/ci/wasmtime.wxs index ec3a58f9aa13..0e947135d555 100644 --- a/ci/wasmtime.wxs +++ b/ci/wasmtime.wxs @@ -62,7 +62,7 @@ - + diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index f6b271112dbb..74cff6db2e09 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -8,24 +8,22 @@ documentation = "https://github.com/bytecodealliance/wasmtime/blob/main/cranelif repository = "https://github.com/bytecodealliance/wasmtime" publish = false edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [[bin]] name = "clif-util" path = "src/clif-util.rs" -[[test]] -name = "filetests" -path = "tests/filetests.rs" -harness = false - [dependencies] cfg-if = { workspace = true } -cranelift-codegen = { workspace = true, features = ["disas", "trace-log"] } +cranelift-codegen = { workspace = true, features = ["disas", "trace-log", "timing"] } cranelift-entity = { workspace = true } cranelift-interpreter = { workspace = true } cranelift-reader = { workspace = true } cranelift-frontend = { workspace = true } -cranelift-wasm = { workspace = true, optional = true } cranelift-native = { workspace = true } cranelift-filetests = { workspace = true } cranelift-module = { workspace = true } @@ -34,9 +32,7 @@ cranelift-jit = { workspace = true } cranelift = { workspace = true } filecheck = { workspace = true } log = { workspace = true } -termcolor = "1.1.2" capstone = { workspace = true, optional = true } -wat = { workspace = true, optional = true } target-lexicon = { workspace = true, features = ["std"] } pretty_env_logger = { workspace = true } rayon = { version = "1", optional = true } @@ -44,18 +40,17 @@ indicatif = "0.13.0" thiserror = { workspace = true } walkdir = { workspace = true } anyhow = { workspace = true } -clap = { workspace = true } +clap = { workspace = true, features = ['default'] } similar = { workspace = true } toml = { workspace = true } serde = { workspace = true } -fxhash = "0.2.1" +rustc-hash = { workspace = true } # Note that this just enables `trace-log` for `clif-util` and doesn't turn it on # for all of Cranelift, which would be bad. regalloc2 = { workspace = true, features = ["trace-log"] } [features] -default = ["disas", "wasm", "cranelift-codegen/all-arch", "cranelift-codegen/trace-log", "souper-harvest"] +default = ["disas", "cranelift-codegen/all-arch", "cranelift-codegen/trace-log", "souper-harvest"] disas = ["capstone"] -wasm = ["wat", "cranelift-wasm"] souper-harvest = ["cranelift-codegen/souper-harvest", "rayon"] all-arch = ["cranelift-codegen/all-arch"] diff --git a/cranelift/bforest/Cargo.toml b/cranelift/bforest/Cargo.toml index d55fdad495ee..41192317b9a2 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.101.0" +version = "0.112.0" description = "A forest of B+-trees" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-bforest" @@ -10,6 +10,10 @@ categories = ["no-std"] readme = "README.md" keywords = ["btree", "forest", "set", "map"] edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [dependencies] cranelift-entity = { workspace = true } diff --git a/cranelift/bforest/src/lib.rs b/cranelift/bforest/src/lib.rs index b0145d63d6a8..90eda03d85f6 100644 --- a/cranelift/bforest/src/lib.rs +++ b/cranelift/bforest/src/lib.rs @@ -13,8 +13,7 @@ //! - Empty trees have a very small 32-bit footprint. //! - All the trees in a forest can be cleared in constant time. -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces)] +#![deny(missing_docs)] #![no_std] #[cfg(test)] diff --git a/cranelift/bforest/src/map.rs b/cranelift/bforest/src/map.rs index 05732b61cc13..d539e1d8cc4e 100644 --- a/cranelift/bforest/src/map.rs +++ b/cranelift/bforest/src/map.rs @@ -426,7 +426,6 @@ where #[cfg(test)] mod tests { - use super::super::NodeData; use super::*; use alloc::vec::Vec; use core::mem; diff --git a/cranelift/bforest/src/node.rs b/cranelift/bforest/src/node.rs index 53a0dca38615..443c112ed888 100644 --- a/cranelift/bforest/src/node.rs +++ b/cranelift/bforest/src/node.rs @@ -174,7 +174,7 @@ impl NodeData { } => { let sz = usize::from(*size); debug_assert!(sz <= keys.len()); - debug_assert!(index <= sz, "Can't insert at {} with {} keys", index, sz); + debug_assert!(index <= sz, "Can't insert at {index} with {sz} keys"); if let Some(ks) = keys.get_mut(0..=sz) { *size = (sz + 1) as u8; @@ -547,7 +547,7 @@ impl ValDisp for SetValue { impl ValDisp for T { fn valfmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, ":{}", self) + write!(f, ":{self}") } } @@ -576,7 +576,7 @@ where } write!(f, " ]") } - Self::Free { next: Some(n) } => write!(f, "[ free -> {} ]", n), + Self::Free { next: Some(n) } => write!(f, "[ free -> {n} ]"), Self::Free { next: None } => write!(f, "[ free ]"), } } @@ -641,7 +641,7 @@ mod tests { // Splitting should be independent of the hint because we have an even number of node // references. - let saved = inner.clone(); + let saved = inner; let sp = inner.split(1); assert_eq!(sp.lhs_entries, 4); assert_eq!(sp.rhs_entries, 4); @@ -691,7 +691,7 @@ mod tests { assert!(!leaf.try_leaf_insert(15, 'x', SetValue())); // The index given to `split` is not the split position, it's a hint for balancing the node. - let saved = leaf.clone(); + let saved = leaf; let sp = leaf.split(12); assert_eq!(sp.lhs_entries, 8); assert_eq!(sp.rhs_entries, 7); diff --git a/cranelift/bforest/src/path.rs b/cranelift/bforest/src/path.rs index a55de6b2ae2b..896bd0e23db2 100644 --- a/cranelift/bforest/src/path.rs +++ b/cranelift/bforest/src/path.rs @@ -651,11 +651,7 @@ impl Path { for level in 0..self.size { match pool[self.node[level]] { NodeData::Inner { size, tree, .. } => { - assert!( - level < self.size - 1, - "Expected leaf node at level {}", - level - ); + assert!(level < self.size - 1, "Expected leaf node at level {level}"); assert!( self.entry[level] <= size, "OOB inner entry {}/{} at level {}", @@ -666,8 +662,7 @@ impl Path { assert_eq!( self.node[level + 1], tree[usize::from(self.entry[level])], - "Node mismatch at level {}", - level + "Node mismatch at level {level}" ); } NodeData::Leaf { size, .. } => { @@ -704,7 +699,6 @@ impl fmt::Display for Path { #[cfg(test)] mod tests { - use super::super::{Forest, NodeData, NodePool}; use super::*; use core::cmp::Ordering; diff --git a/cranelift/bforest/src/pool.rs b/cranelift/bforest/src/pool.rs index a2e416ad57f7..9e3f57fccedd 100644 --- a/cranelift/bforest/src/pool.rs +++ b/cranelift/bforest/src/pool.rs @@ -52,7 +52,7 @@ impl NodePool { /// Free a node. pub fn free_node(&mut self, node: Node) { // Quick check for a double free. - debug_assert!(!self.nodes[node].is_free(), "{} is already free", node); + debug_assert!(!self.nodes[node].is_free(), "{node} is already free"); self.nodes[node] = NodeData::Free { next: self.freelist, }; @@ -162,7 +162,7 @@ impl NodePool { // Verify occupancy. // Right-most nodes can be small, but others must be at least half full. - assert!(size > 0, "Leaf {} is empty", node); + assert!(size > 0, "Leaf {node} is empty"); assert!( rkey.is_none() || size * 2 >= capacity, "Only {}/{} entries in {}:{}, upper={}", diff --git a/cranelift/bforest/src/set.rs b/cranelift/bforest/src/set.rs index 543c4a414587..34a3063c2f3d 100644 --- a/cranelift/bforest/src/set.rs +++ b/cranelift/bforest/src/set.rs @@ -354,7 +354,6 @@ where #[cfg(test)] mod tests { - use super::super::NodeData; use super::*; use alloc::vec::Vec; use core::mem; diff --git a/cranelift/bitset/Cargo.toml b/cranelift/bitset/Cargo.toml new file mode 100644 index 000000000000..2e6d2dd06112 --- /dev/null +++ b/cranelift/bitset/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["The Cranelift Project Developers"] +name = "cranelift-bitset" +version = "0.112.0" +description = "Various bitset stuff for use inside Cranelift" +license = "Apache-2.0 WITH LLVM-exception" +documentation = "https://docs.rs/cranelift-bitset" +repository = "https://github.com/bytecodealliance/wasmtime" +edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[dependencies] +arbitrary = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } + +[features] +enable-serde = ["dep:serde", "dep:serde_derive"] +arbitrary = ["dep:arbitrary"] diff --git a/cranelift/bitset/src/compound.rs b/cranelift/bitset/src/compound.rs new file mode 100644 index 000000000000..ebb9b4e7d703 --- /dev/null +++ b/cranelift/bitset/src/compound.rs @@ -0,0 +1,508 @@ +//! Compound bit sets. + +use crate::scalar::{self, ScalarBitSet}; +use alloc::boxed::Box; +use core::{cmp, iter, mem}; + +/// A large bit set backed by dynamically-sized storage. +/// +/// # Example +/// +/// ``` +/// use cranelift_bitset::CompoundBitSet; +/// +/// // Create a new bitset. +/// let mut bitset = CompoundBitSet::new(); +/// +/// // Bitsets are initially empty. +/// assert!(bitset.is_empty()); +/// assert_eq!(bitset.len(), 0); +/// +/// // Insert into the bitset. +/// bitset.insert(444); +/// bitset.insert(555); +/// bitset.insert(666); +/// +/// // Now the bitset is not empty. +/// assert_eq!(bitset.len(), 3); +/// assert!(!bitset.is_empty()); +/// assert!(bitset.contains(444)); +/// assert!(bitset.contains(555)); +/// assert!(bitset.contains(666)); +/// +/// // Remove an element from the bitset. +/// let was_present = bitset.remove(666); +/// assert!(was_present); +/// assert!(!bitset.contains(666)); +/// assert_eq!(bitset.len(), 2); +/// +/// // Can iterate over the elements in the set. +/// let elems: Vec<_> = bitset.iter().collect(); +/// assert_eq!(elems, [444, 555]); +/// ``` +#[derive(Clone, Default, PartialEq, Eq)] +#[cfg_attr( + feature = "enable-serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct CompoundBitSet { + elems: Box<[ScalarBitSet]>, + max: Option, +} + +impl core::fmt::Debug for CompoundBitSet { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "CompoundBitSet ")?; + f.debug_set().entries(self.iter()).finish() + } +} + +const BITS_PER_WORD: usize = mem::size_of::() * 8; + +impl CompoundBitSet { + /// Construct a new, empty bit set. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let bitset = CompoundBitSet::new(); + /// + /// assert!(bitset.is_empty()); + /// ``` + #[inline] + pub fn new() -> Self { + CompoundBitSet::default() + } + + /// Construct a new, empty bit set with space reserved to store any element + /// `x` such that `x < capacity`. + /// + /// The actual capacity reserved may be greater than that requested. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let bitset = CompoundBitSet::with_capacity(4096); + /// + /// assert!(bitset.is_empty()); + /// assert!(bitset.capacity() >= 4096); + /// ``` + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + let mut bitset = Self::new(); + bitset.ensure_capacity(capacity); + bitset + } + + /// Get the number of elements in this bitset. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// assert_eq!(bitset.len(), 0); + /// + /// bitset.insert(24); + /// bitset.insert(130); + /// bitset.insert(3600); + /// + /// assert_eq!(bitset.len(), 3); + /// ``` + #[inline] + pub fn len(&self) -> usize { + self.elems.iter().map(|sub| usize::from(sub.len())).sum() + } + + /// Get `n + 1` where `n` is the largest value that can be stored inside + /// this set without growing the backing storage. + /// + /// That is, this set can store any value `x` such that `x < + /// bitset.capacity()` without growing the backing storage. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// // New bitsets have zero capacity -- they allocate lazily. + /// assert_eq!(bitset.capacity(), 0); + /// + /// // Insert into the bitset, growing its capacity. + /// bitset.insert(999); + /// + /// // The bitset must now have capacity for at least `999` elements, + /// // perhaps more. + /// assert!(bitset.capacity() >= 999); + ///``` + pub fn capacity(&self) -> usize { + self.elems.len() * BITS_PER_WORD + } + + /// Is this bitset empty? + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// assert!(bitset.is_empty()); + /// + /// bitset.insert(1234); + /// + /// assert!(!bitset.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Convert an element `i` into the `word` that can be used to index into + /// `self.elems` and the `bit` that can be tested in the + /// `ScalarBitSet` at `self.elems[word]`. + #[inline] + fn word_and_bit(i: usize) -> (usize, u8) { + let word = i / BITS_PER_WORD; + let bit = i % BITS_PER_WORD; + let bit = u8::try_from(bit).unwrap(); + (word, bit) + } + + /// The opposite of `word_and_bit`: convert the pair of an index into + /// `self.elems` and associated bit index into a set element. + #[inline] + fn elem(word: usize, bit: u8) -> usize { + let bit = usize::from(bit); + debug_assert!(bit < BITS_PER_WORD); + word * BITS_PER_WORD + bit + } + + /// Is `i` contained in this bitset? + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// assert!(!bitset.contains(666)); + /// + /// bitset.insert(666); + /// + /// assert!(bitset.contains(666)); + /// ``` + #[inline] + pub fn contains(&self, i: usize) -> bool { + let (word, bit) = Self::word_and_bit(i); + if word < self.elems.len() { + self.elems[word].contains(bit) + } else { + false + } + } + + /// Ensure there is space in this bitset for the values `0..n`, growing the + /// backing storage if necessary. + /// + /// After calling `bitset.ensure_capacity(n)`, inserting any element `i` + /// where `i < n` is guaranteed to succeed without growing the bitset's + /// backing storage. + /// + /// # Example + /// + /// ``` + /// # use cranelift_bitset::CompoundBitSet; + /// # let mut bitset = CompoundBitSet::new(); + /// // We are going to do a series of inserts where `1000` will be the + /// // maximum value inserted. Make sure that our bitset has capacity for + /// // these elements once up front, to avoid growing the backing storage + /// // multiple times incrementally. + /// bitset.ensure_capacity(1001); + /// + /// for i in 0..=1000 { + /// if i % 2 == 0 { + /// // Inserting this value should not require growing the backing + /// // storage. + /// assert!(bitset.capacity() > i); + /// bitset.insert(i); + /// } + /// } + /// ``` + #[inline] + pub fn ensure_capacity(&mut self, n: usize) { + let (word, _bit) = Self::word_and_bit(n); + if word >= self.elems.len() { + assert!(word < usize::try_from(isize::MAX).unwrap()); + + let delta = word - self.elems.len(); + let to_grow = delta + 1; + + // Amortize the cost of growing. + let to_grow = cmp::max(to_grow, self.elems.len() * 2); + // Don't make ridiculously small allocations. + let to_grow = cmp::max(to_grow, 4); + + let new_elems = self + .elems + .iter() + .copied() + .chain(iter::repeat(ScalarBitSet::new()).take(to_grow)) + .collect::>(); + self.elems = new_elems; + } + } + + /// Insert `i` into this bitset. + /// + /// Returns whether the value was newly inserted. That is: + /// + /// * If the set did not previously contain `i` then `true` is returned. + /// + /// * If the set already contained `i` then `false` is returned. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// // When an element is inserted that was not already present in the set, + /// // then `true` is returned. + /// let is_new = bitset.insert(1234); + /// assert!(is_new); + /// + /// // The element is now present in the set. + /// assert!(bitset.contains(1234)); + /// + /// // And when the element is already in the set, `false` is returned from + /// // `insert`. + /// let is_new = bitset.insert(1234); + /// assert!(!is_new); + /// ``` + #[inline] + pub fn insert(&mut self, i: usize) -> bool { + self.ensure_capacity(i + 1); + + let (word, bit) = Self::word_and_bit(i); + let is_new = self.elems[word].insert(bit); + + let i = u32::try_from(i).unwrap(); + self.max = self.max.map(|max| cmp::max(max, i)).or(Some(i)); + + is_new + } + + /// Remove `i` from this bitset. + /// + /// Returns whether `i` was previously in this set or not. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// // Removing an element that was not present in the set returns `false`. + /// let was_present = bitset.remove(456); + /// assert!(!was_present); + /// + /// // And when the element was in the set, `true` is returned. + /// bitset.insert(456); + /// let was_present = bitset.remove(456); + /// assert!(was_present); + /// ``` + #[inline] + pub fn remove(&mut self, i: usize) -> bool { + let (word, bit) = Self::word_and_bit(i); + if word < self.elems.len() { + let sub = &mut self.elems[word]; + let was_present = sub.remove(bit); + if was_present && self.max() == Some(i) { + self.update_max(word); + } + was_present + } else { + false + } + } + + /// Update the `self.max` field, based on the old word index of `self.max`. + fn update_max(&mut self, word_of_old_max: usize) { + self.max = self.elems[0..word_of_old_max + 1] + .iter() + .enumerate() + .rev() + .filter_map(|(word, sub)| { + let bit = sub.max()?; + Some(u32::try_from(Self::elem(word, bit)).unwrap()) + }) + .next(); + } + + /// Get the largest value in this set, or `None` if this set is empty. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// // Returns `None` if the bitset is empty. + /// assert!(bitset.max().is_none()); + /// + /// bitset.insert(123); + /// bitset.insert(987); + /// bitset.insert(999); + /// + /// // Otherwise, it returns the largest value in the set. + /// assert_eq!(bitset.max(), Some(999)); + /// ``` + #[inline] + pub fn max(&self) -> Option { + self.max.map(|m| usize::try_from(m).unwrap()) + } + + /// Removes and returns the largest value in this set. + /// + /// Returns `None` if this set is empty. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// bitset.insert(111); + /// bitset.insert(222); + /// bitset.insert(333); + /// bitset.insert(444); + /// bitset.insert(555); + /// + /// assert_eq!(bitset.pop(), Some(555)); + /// assert_eq!(bitset.pop(), Some(444)); + /// assert_eq!(bitset.pop(), Some(333)); + /// assert_eq!(bitset.pop(), Some(222)); + /// assert_eq!(bitset.pop(), Some(111)); + /// assert_eq!(bitset.pop(), None); + /// ``` + #[inline] + pub fn pop(&mut self) -> Option { + let max = self.max()?; + self.remove(max); + Some(max) + } + + /// Remove all elements from this bitset. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// bitset.insert(100); + /// bitset.insert(200); + /// bitset.insert(300); + /// + /// bitset.clear(); + /// + /// assert!(bitset.is_empty()); + /// ``` + #[inline] + pub fn clear(&mut self) { + let max = match self.max() { + Some(max) => max, + None => return, + }; + let (word, _bit) = Self::word_and_bit(max); + debug_assert!(self.elems[word + 1..].iter().all(|sub| sub.is_empty())); + for sub in &mut self.elems[..=word] { + *sub = ScalarBitSet::new(); + } + self.max = None; + } + + /// Iterate over the elements in this bitset. + /// + /// The elements are always yielded in sorted order. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::CompoundBitSet; + /// + /// let mut bitset = CompoundBitSet::new(); + /// + /// bitset.insert(0); + /// bitset.insert(4096); + /// bitset.insert(123); + /// bitset.insert(456); + /// bitset.insert(789); + /// + /// assert_eq!( + /// bitset.iter().collect::>(), + /// [0, 123, 456, 789, 4096], + /// ); + /// ``` + #[inline] + pub fn iter(&self) -> Iter<'_> { + Iter { + bitset: self, + word: 0, + sub: None, + } + } +} + +impl<'a> IntoIterator for &'a CompoundBitSet { + type Item = usize; + + type IntoIter = Iter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +/// An iterator over the elements in a [`CompoundBitSet`]. +pub struct Iter<'a> { + bitset: &'a CompoundBitSet, + word: usize, + sub: Option>, +} + +impl Iterator for Iter<'_> { + type Item = usize; + + #[inline] + fn next(&mut self) -> Option { + loop { + if let Some(sub) = &mut self.sub { + if let Some(bit) = sub.next() { + return Some(CompoundBitSet::elem(self.word, bit)); + } else { + self.word += 1; + } + } + + self.sub = Some(self.bitset.elems.get(self.word)?.iter()); + } + } +} diff --git a/cranelift/bitset/src/lib.rs b/cranelift/bitset/src/lib.rs new file mode 100644 index 000000000000..5a9fd354aa15 --- /dev/null +++ b/cranelift/bitset/src/lib.rs @@ -0,0 +1,19 @@ +//! Bitsets for Cranelift. +//! +//! This module provides two bitset implementations: +//! +//! 1. [`ScalarBitSet`]: A small bitset built on top of a single integer. +//! +//! 2. [`CompoundBitSet`]: A bitset that can store more bits than fit in a +//! single integer, but which internally has heap allocations. + +#![deny(missing_docs)] +#![no_std] + +extern crate alloc; + +pub mod compound; +pub mod scalar; + +pub use compound::CompoundBitSet; +pub use scalar::ScalarBitSet; diff --git a/cranelift/bitset/src/scalar.rs b/cranelift/bitset/src/scalar.rs new file mode 100644 index 000000000000..07649c297efe --- /dev/null +++ b/cranelift/bitset/src/scalar.rs @@ -0,0 +1,626 @@ +//! Scalar bitsets. + +use core::mem::size_of; +use core::ops::{Add, BitAnd, BitOr, Not, Shl, Shr, Sub}; + +/// A small bitset built on top of a single primitive integer type. +/// +/// # Example +/// +/// ``` +/// use cranelift_bitset::ScalarBitSet; +/// +/// // Create a new bitset backed with a `u32`. +/// let mut bitset = ScalarBitSet::::new(); +/// +/// // Bitsets are initially empty. +/// assert!(bitset.is_empty()); +/// assert_eq!(bitset.len(), 0); +/// +/// // Insert into the bitset. +/// bitset.insert(4); +/// bitset.insert(5); +/// bitset.insert(6); +/// +/// // Now the bitset is not empty. +/// assert_eq!(bitset.len(), 3); +/// assert!(!bitset.is_empty()); +/// assert!(bitset.contains(4)); +/// assert!(bitset.contains(5)); +/// assert!(bitset.contains(6)); +/// +/// // Remove an element from the bitset. +/// let was_present = bitset.remove(6); +/// assert!(was_present); +/// assert!(!bitset.contains(6)); +/// assert_eq!(bitset.len(), 2); +/// +/// // Can iterate over the elements in the set. +/// let elems: Vec<_> = bitset.iter().collect(); +/// assert_eq!(elems, [4, 5]); +/// ``` +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr( + feature = "enable-serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct ScalarBitSet(pub T); + +impl core::fmt::Debug for ScalarBitSet +where + T: ScalarBitSetStorage, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut s = f.debug_struct(core::any::type_name::()); + for i in 0..Self::capacity() { + use alloc::string::ToString; + let i = u8::try_from(i).unwrap(); + s.field(&i.to_string(), &self.contains(i)); + } + s.finish() + } +} + +impl Default for ScalarBitSet +where + T: ScalarBitSetStorage, +{ + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl ScalarBitSet +where + T: ScalarBitSetStorage, +{ + /// Create a new, empty bitset. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let bitset = ScalarBitSet::::new(); + /// + /// assert!(bitset.is_empty()); + /// ``` + #[inline] + pub fn new() -> Self { + Self(T::from(0)) + } + + /// Construct a bitset with the half-open range `[lo, hi)` inserted. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let bitset = ScalarBitSet::::from_range(3, 6); + /// + /// assert_eq!(bitset.len(), 3); + /// + /// assert!(bitset.contains(3)); + /// assert!(bitset.contains(4)); + /// assert!(bitset.contains(5)); + /// ``` + /// + /// # Panics + /// + /// Panics if `lo > hi` or if `hi > Self::capacity()`. + /// + /// ```should_panic + /// use cranelift_bitset::ScalarBitSet; + /// + /// // The lower bound may not be larger than the upper bound. + /// let bitset = ScalarBitSet::::from_range(6, 3); + /// ``` + /// + /// ```should_panic + /// use cranelift_bitset::ScalarBitSet; + /// + /// // The bounds must fit within the backing scalar type. + /// let bitset = ScalarBitSet::::from_range(3, 69); + /// ``` + #[inline] + pub fn from_range(lo: u8, hi: u8) -> Self { + assert!(lo <= hi); + assert!(hi <= Self::capacity()); + + let one = T::from(1); + + // We can't just do (one << hi) - one here as the shift may overflow + let hi_rng = if hi >= 1 { + (one << (hi - 1)) + ((one << (hi - 1)) - one) + } else { + T::from(0) + }; + + let lo_rng = (one << lo) - one; + + Self(hi_rng - lo_rng) + } + + /// The maximum number of bits that can be stored in this bitset. + /// + /// If you need more bits than this, use a + /// [`CompoundBitSet`][crate::CompoundBitSet] instead of a `ScalarBitSet`. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// assert_eq!(ScalarBitSet::::capacity(), 8); + /// assert_eq!(ScalarBitSet::::capacity(), 64); + /// ``` + #[inline] + pub fn capacity() -> u8 { + u8::try_from(size_of::()).unwrap() * 8 + } + + /// Get the number of elements in this set. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// assert_eq!(bitset.len(), 0); + /// + /// bitset.insert(24); + /// bitset.insert(13); + /// bitset.insert(36); + /// + /// assert_eq!(bitset.len(), 3); + /// ``` + #[inline] + pub fn len(&self) -> u8 { + self.0.count_ones() + } + + /// Is this bitset empty? + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// assert!(bitset.is_empty()); + /// + /// bitset.insert(10); + /// + /// assert!(!bitset.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.0 == T::from(0) + } + + /// Check whether this bitset contains `i`. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// assert!(!bitset.contains(7)); + /// + /// bitset.insert(7); + /// + /// assert!(bitset.contains(7)); + /// ``` + /// + /// # Panics + /// + /// Panics if `i` is greater than or equal to [`Self::capacity()`][Self::capacity]. + /// + /// ```should_panic + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// // A `ScalarBitSet` can only hold the elements `0..=7`, so `8` is + /// // out of bounds and will trigger a panic. + /// bitset.contains(8); + /// ``` + #[inline] + pub fn contains(&self, i: u8) -> bool { + assert!(i < Self::capacity()); + self.0 & (T::from(1) << i) != T::from(0) + } + + /// Insert `i` into this bitset. + /// + /// Returns whether the value was newly inserted. That is: + /// + /// * If the set did not previously contain `i` then `true` is returned. + /// + /// * If the set already contained `i` then `false` is returned. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// // When an element is inserted that was not already present in the set, + /// // then `true` is returned. + /// let is_new = bitset.insert(7); + /// assert!(is_new); + /// + /// // The element is now present in the set. + /// assert!(bitset.contains(7)); + /// + /// // And when the element is already in the set, `false` is returned from + /// // `insert`. + /// let is_new = bitset.insert(7); + /// assert!(!is_new); + /// ``` + /// + /// # Panics + /// + /// Panics if `i` is greater than or equal to [`Self::capacity()`][Self::capacity]. + /// + /// ```should_panic + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// // A `ScalarBitSet` can only hold the elements `0..=31`, so `42` is + /// // out of bounds and will trigger a panic. + /// bitset.insert(42); + /// ``` + #[inline] + pub fn insert(&mut self, i: u8) -> bool { + let is_new = !self.contains(i); + self.0 = self.0 | (T::from(1) << i); + is_new + } + + /// Remove `i` from this bitset. + /// + /// Returns whether `i` was previously in this set or not. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// // Removing an element that was not present in the set returns `false`. + /// let was_present = bitset.remove(100); + /// assert!(!was_present); + /// + /// // And when the element was in the set, `true` is returned. + /// bitset.insert(100); + /// let was_present = bitset.remove(100); + /// assert!(was_present); + /// ``` + /// + /// # Panics + /// + /// Panics if `i` is greater than or equal to [`Self::capacity()`][Self::capacity]. + /// + /// ```should_panic + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// // A `ScalarBitSet` can only hold the elements `0..=15`, so `20` is + /// // out of bounds and will trigger a panic. + /// bitset.remove(20); + /// ``` + #[inline] + pub fn remove(&mut self, i: u8) -> bool { + let was_present = self.contains(i); + self.0 = self.0 & !(T::from(1) << i); + was_present + } + + /// Remove all entries from this bitset. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// bitset.insert(10); + /// bitset.insert(20); + /// bitset.insert(30); + /// + /// bitset.clear(); + /// + /// assert!(bitset.is_empty()); + /// ``` + #[inline] + pub fn clear(&mut self) { + self.0 = T::from(0); + } + + /// Remove and return the smallest value in the bitset. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// bitset.insert(0); + /// bitset.insert(24); + /// bitset.insert(13); + /// bitset.insert(36); + /// + /// assert_eq!(bitset.pop_min(), Some(0)); + /// assert_eq!(bitset.pop_min(), Some(13)); + /// assert_eq!(bitset.pop_min(), Some(24)); + /// assert_eq!(bitset.pop_min(), Some(36)); + /// assert_eq!(bitset.pop_min(), None); + /// ``` + #[inline] + pub fn pop_min(&mut self) -> Option { + let min = self.min()?; + self.remove(min); + Some(min) + } + + /// Remove and return the largest value in the bitset. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// bitset.insert(0); + /// bitset.insert(24); + /// bitset.insert(13); + /// bitset.insert(36); + /// + /// assert_eq!(bitset.pop_max(), Some(36)); + /// assert_eq!(bitset.pop_max(), Some(24)); + /// assert_eq!(bitset.pop_max(), Some(13)); + /// assert_eq!(bitset.pop_max(), Some(0)); + /// assert_eq!(bitset.pop_max(), None); + /// ``` + #[inline] + pub fn pop_max(&mut self) -> Option { + let max = self.max()?; + self.remove(max); + Some(max) + } + + /// Return the smallest number contained in this bitset or `None` if this + /// bitset is empty. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// // When the bitset is empty, `min` returns `None`. + /// assert_eq!(bitset.min(), None); + /// + /// bitset.insert(28); + /// bitset.insert(1); + /// bitset.insert(63); + /// + /// // When the bitset is not empty, it returns the smallest element. + /// assert_eq!(bitset.min(), Some(1)); + /// ``` + #[inline] + pub fn min(&self) -> Option { + if self.0 == T::from(0) { + None + } else { + Some(self.0.trailing_zeros()) + } + } + + /// Return the largest number contained in the bitset or None if this bitset + /// is empty + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// // When the bitset is empty, `max` returns `None`. + /// assert_eq!(bitset.max(), None); + /// + /// bitset.insert(0); + /// bitset.insert(36); + /// bitset.insert(49); + /// + /// // When the bitset is not empty, it returns the smallest element. + /// assert_eq!(bitset.max(), Some(49)); + /// ``` + #[inline] + pub fn max(&self) -> Option { + if self.0 == T::from(0) { + None + } else { + let leading_zeroes = self.0.leading_zeros(); + Some(Self::capacity() - leading_zeroes - 1) + } + } + + /// Iterate over the items in this set. + /// + /// Items are always yielded in sorted order. + /// + /// # Example + /// + /// ``` + /// use cranelift_bitset::ScalarBitSet; + /// + /// let mut bitset = ScalarBitSet::::new(); + /// + /// bitset.insert(19); + /// bitset.insert(3); + /// bitset.insert(63); + /// bitset.insert(0); + /// + /// assert_eq!( + /// bitset.iter().collect::>(), + /// [0, 3, 19, 63], + /// ); + /// ``` + #[inline] + pub fn iter(self) -> Iter { + Iter { bitset: self } + } +} + +impl IntoIterator for ScalarBitSet +where + T: ScalarBitSetStorage, +{ + type Item = u8; + + type IntoIter = Iter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, T> IntoIterator for &'a ScalarBitSet +where + T: ScalarBitSetStorage, +{ + type Item = u8; + + type IntoIter = Iter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl From for ScalarBitSet { + fn from(bits: T) -> Self { + Self(bits) + } +} + +/// A trait implemented by all integers that can be used as the backing storage +/// for a [`ScalarBitSet`]. +/// +/// You shouldn't have to implement this yourself, it is already implemented for +/// `u{8,16,32,64,128}` and if you need more bits than that, then use +/// [`CompoundBitSet`][crate::CompoundBitSet] instead. +pub trait ScalarBitSetStorage: + Default + + From + + Shl + + Shr + + BitAnd + + BitOr + + Not + + Sub + + Add + + PartialEq + + Copy +{ + /// Count the number of leading zeros. + fn leading_zeros(self) -> u8; + + /// Count the number of trailing zeros. + fn trailing_zeros(self) -> u8; + + /// Count the number of bits set in this integer. + fn count_ones(self) -> u8; +} + +macro_rules! impl_storage { + ( $int:ty ) => { + impl ScalarBitSetStorage for $int { + fn leading_zeros(self) -> u8 { + u8::try_from(self.leading_zeros()).unwrap() + } + + fn trailing_zeros(self) -> u8 { + u8::try_from(self.trailing_zeros()).unwrap() + } + + fn count_ones(self) -> u8 { + u8::try_from(self.count_ones()).unwrap() + } + } + }; +} + +impl_storage!(u8); +impl_storage!(u16); +impl_storage!(u32); +impl_storage!(u64); +impl_storage!(u128); +impl_storage!(usize); + +/// An iterator over the elements in a [`ScalarBitSet`]. +pub struct Iter { + bitset: ScalarBitSet, +} + +impl Iterator for Iter +where + T: ScalarBitSetStorage, +{ + type Item = u8; + + #[inline] + fn next(&mut self) -> Option { + self.bitset.pop_min() + } +} + +impl DoubleEndedIterator for Iter +where + T: ScalarBitSetStorage, +{ + #[inline] + fn next_back(&mut self) -> Option { + self.bitset.pop_max() + } +} + +impl ExactSizeIterator for Iter +where + T: ScalarBitSetStorage, +{ + #[inline] + fn len(&self) -> usize { + usize::from(self.bitset.len()) + } +} + +#[cfg(feature = "arbitrary")] +impl<'a, T> arbitrary::Arbitrary<'a> for ScalarBitSet +where + T: ScalarBitSetStorage + arbitrary::Arbitrary<'a>, +{ + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + T::arbitrary(u).map(Self) + } +} diff --git a/cranelift/bitset/tests/bitset.rs b/cranelift/bitset/tests/bitset.rs new file mode 100644 index 000000000000..d0f5aedd6428 --- /dev/null +++ b/cranelift/bitset/tests/bitset.rs @@ -0,0 +1,78 @@ +use cranelift_bitset::*; + +#[test] +fn contains() { + let s = ScalarBitSet::(255); + for i in 0..7 { + assert!(s.contains(i)); + } + + let s1 = ScalarBitSet::(0); + for i in 0..7 { + assert!(!s1.contains(i)); + } + + let s2 = ScalarBitSet::(127); + for i in 0..6 { + assert!(s2.contains(i)); + } + assert!(!s2.contains(7)); + + let s3 = ScalarBitSet::(2 | 4 | 64); + assert!(!s3.contains(0) && !s3.contains(3) && !s3.contains(4)); + assert!(!s3.contains(5) && !s3.contains(7)); + assert!(s3.contains(1) && s3.contains(2) && s3.contains(6)); + + let s4 = ScalarBitSet::(4 | 8 | 256 | 1024); + assert!( + !s4.contains(0) + && !s4.contains(1) + && !s4.contains(4) + && !s4.contains(5) + && !s4.contains(6) + && !s4.contains(7) + && !s4.contains(9) + && !s4.contains(11) + ); + assert!(s4.contains(2) && s4.contains(3) && s4.contains(8) && s4.contains(10)); +} + +#[test] +fn minmax() { + let s = ScalarBitSet::(255); + assert_eq!(s.min(), Some(0)); + assert_eq!(s.max(), Some(7)); + assert!(s.min() == Some(0) && s.max() == Some(7)); + let s1 = ScalarBitSet::(0); + assert!(s1.min() == None && s1.max() == None); + let s2 = ScalarBitSet::(127); + assert!(s2.min() == Some(0) && s2.max() == Some(6)); + let s3 = ScalarBitSet::(2 | 4 | 64); + assert!(s3.min() == Some(1) && s3.max() == Some(6)); + let s4 = ScalarBitSet::(4 | 8 | 256 | 1024); + assert!(s4.min() == Some(2) && s4.max() == Some(10)); +} + +#[test] +fn from_range() { + let s = ScalarBitSet::::from_range(5, 5); + assert!(s.0 == 0); + + let s = ScalarBitSet::::from_range(0, 8); + assert!(s.0 == 255); + + let s = ScalarBitSet::::from_range(0, 8); + assert!(s.0 == 255u16); + + let s = ScalarBitSet::::from_range(0, 16); + assert!(s.0 == 65535u16); + + let s = ScalarBitSet::::from_range(5, 6); + assert!(s.0 == 32u8); + + let s = ScalarBitSet::::from_range(3, 7); + assert!(s.0 == 8 | 16 | 32 | 64); + + let s = ScalarBitSet::::from_range(5, 11); + assert!(s.0 == 32 | 64 | 128 | 256 | 512 | 1024); +} diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml index 640444c9c959..1126c2233631 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.101.0" +version = "0.112.0" description = "Low-level code generator library" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-codegen" @@ -11,41 +11,49 @@ readme = "README.md" keywords = ["compile", "compiler", "jit"] build = "build.rs" edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [dependencies] -anyhow = { workspace = true, optional = true } +anyhow = { workspace = true, optional = true, features = ['std'] } bumpalo = "3" capstone = { workspace = true, optional = true } -cranelift-codegen-shared = { path = "./shared", version = "0.101.0" } +cranelift-codegen-shared = { path = "./shared", version = "0.112.0" } cranelift-entity = { workspace = true } cranelift-bforest = { workspace = true } +cranelift-bitset = { workspace = true } cranelift-control = { workspace = true } hashbrown = { workspace = true, features = ["raw"] } target-lexicon = { workspace = true } log = { workspace = true } -serde = { version = "1.0.188", optional = true } -serde_derive = { version = "1.0.188", optional = true } -bincode = { version = "1.2.1", optional = true } -gimli = { workspace = true, features = ["write"], optional = true } +serde = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } +pulley-interpreter = { workspace = true, optional = true } +postcard = { workspace = true, optional = true } +gimli = { workspace = true, features = ["write", "std"], optional = true } smallvec = { workspace = true } regalloc2 = { workspace = true, features = ["checker"] } souper-ir = { version = "2.1.0", optional = true } sha2 = { version = "0.10.2", optional = true } +rustc-hash = { workspace = true } # It is a goal of the cranelift-codegen crate to have minimal external dependencies. # Please don't add any unless they are essential to the task of creating binary # machine code. Integration tests that need external dependencies can be -# accomodated in `tests`. +# accommodated in `tests`. [dev-dependencies] -criterion = { version = "0.5.0", features = ["html_reports"] } +criterion = { workspace = true } similar = "2.1.0" +env_logger = { workspace = true } [build-dependencies] -cranelift-codegen-meta = { path = "meta", version = "0.101.0" } -cranelift-isle = { path = "../isle/isle", version = "=0.101.0" } +cranelift-codegen-meta = { path = "meta", version = "0.112.0" } +cranelift-isle = { path = "../isle/isle", version = "=0.112.0" } [features] -default = ["std", "unwind", "host-arch"] +default = ["std", "unwind", "host-arch", "timing"] # The "std" feature enables use of libstd. The "core" feature enables use # of some minimal std-like replacement libraries. At least one of these two @@ -63,7 +71,7 @@ core = [] disas = ["anyhow", "capstone"] # Enables detailed logging which can be somewhat expensive. -trace-log = [] +trace-log = ["regalloc2/trace-log"] # This enables unwind info generation functionality. unwind = ["gimli"] @@ -74,6 +82,7 @@ x86 = [] arm64 = [] s390x = [] riscv64 = [] +pulley = ["dep:pulley-interpreter", "pulley-interpreter/encode", "pulley-interpreter/disas"] # Enable the ISA target for the host machine host-arch = [] @@ -82,7 +91,8 @@ all-arch = [ "x86", "arm64", "s390x", - "riscv64" + "riscv64", + "pulley", ] # For dependent crates that want to serialize some parts of cranelift @@ -90,6 +100,7 @@ enable-serde = [ "serde", "serde_derive", "cranelift-entity/enable-serde", + "cranelift-bitset/enable-serde", "regalloc2/enable-serde", "smallvec/serde" ] @@ -97,7 +108,7 @@ enable-serde = [ # Enable the incremental compilation cache for hot-reload use cases. incremental-cache = [ "enable-serde", - "bincode", + "postcard", "sha2" ] @@ -111,6 +122,11 @@ isle-errors = ["cranelift-isle/fancy-errors"] # inspection, rather than inside of target/. isle-in-source-tree = [] +# Enable tracking how long passes take in Cranelift. +# +# Enabled by default. +timing = [] + [[bench]] name = "x64-evex-encoding" harness = false diff --git a/cranelift/codegen/build.rs b/cranelift/codegen/build.rs index 211b62177dd2..7a879b9d1168 100644 --- a/cranelift/codegen/build.rs +++ b/cranelift/codegen/build.rs @@ -16,6 +16,7 @@ use cranelift_codegen_meta as meta; use cranelift_isle::error::Errors; +use meta::isle::IsleCompilation; use std::env; use std::io::Read; @@ -26,24 +27,33 @@ fn main() { let start_time = Instant::now(); let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"); + let out_dir = std::path::Path::new(&out_dir); let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set"); + let all_arch = env::var("CARGO_FEATURE_ALL_ARCH").is_ok(); + let mut isas = meta::isa::Isa::all() .iter() .cloned() .filter(|isa| { - let env_key = format!("CARGO_FEATURE_{}", isa.to_string().to_uppercase()); - env::var(env_key).is_ok() + let env_key = match isa { + meta::isa::Isa::Pulley32 | meta::isa::Isa::Pulley64 => { + "CARGO_FEATURE_PULLEY".to_string() + } + _ => format!("CARGO_FEATURE_{}", isa.to_string().to_uppercase()), + }; + all_arch || env::var(env_key).is_ok() }) .collect::>(); - let host_isa = env::var("CARGO_FEATURE_HOST_ARCH").is_ok(); + // Don't require host isa if under 'all-arch' feature. + let host_isa = env::var("CARGO_FEATURE_HOST_ARCH").is_ok() && !all_arch; if isas.is_empty() || host_isa { // Try to match native target. let target_name = target_triple.split('-').next().unwrap(); let isa = meta::isa_from_arch(&target_name).expect("error when identifying target"); - println!("cargo:rustc-cfg=feature=\"{}\"", isa); + println!("cargo:rustc-cfg=feature=\"{isa}\""); isas.push(isa); } @@ -56,7 +66,7 @@ fn main() { #[cfg(feature = "isle-in-source-tree")] let isle_dir = explicit_isle_dir; #[cfg(not(feature = "isle-in-source-tree"))] - let isle_dir = std::path::Path::new(&out_dir); + let isle_dir = &out_dir; #[cfg(feature = "isle-in-source-tree")] { @@ -76,14 +86,14 @@ fn main() { } } - if let Err(err) = meta::generate(&isas, &out_dir, isle_dir.to_str().unwrap()) { - eprintln!("Error: {}", err); + if let Err(err) = meta::generate(&isas, &out_dir, isle_dir) { + eprintln!("Error: {err}"); process::exit(1); } if &std::env::var("SKIP_ISLE").unwrap_or("0".to_string()) != "1" { if let Err(err) = build_isle(crate_dir, isle_dir) { - eprintln!("Error: {}", err); + eprintln!("Error: {err}"); process::exit(1); } } @@ -96,7 +106,7 @@ fn main() { "cargo:warning=Build step took {:?}.", Instant::now() - start_time ); - println!("cargo:warning=Generated files are in {}", out_dir); + println!("cargo:warning=Generated files are in {}", out_dir.display()); } let pkg_version = env::var("CARGO_PKG_VERSION").unwrap(); @@ -116,7 +126,7 @@ fn main() { let status = child.wait().unwrap(); if status.success() { let git_rev = git_rev.trim().chars().take(9).collect::(); - format!("{}-{}", pkg_version, git_rev) + format!("{pkg_version}-{git_rev}") } else { // not a git repo pkg_version @@ -129,8 +139,7 @@ fn main() { std::path::Path::new(&out_dir).join("version.rs"), format!( "/// Version number of this crate. \n\ - pub const VERSION: &str = \"{}\";", - version + pub const VERSION: &str = \"{version}\";" ), ) .unwrap(); @@ -140,166 +149,36 @@ fn main() { /// includes them in the generated source, and this helps us maintain /// deterministic builds that don't include those local file paths. fn make_isle_source_path_relative( - cur_dir: &std::path::PathBuf, - filename: std::path::PathBuf, + cur_dir: &std::path::Path, + filename: &std::path::Path, ) -> std::path::PathBuf { if let Ok(suffix) = filename.strip_prefix(&cur_dir) { suffix.to_path_buf() } else { - filename + filename.to_path_buf() } } -/// A list of compilations (transformations from ISLE source to -/// generated Rust source) that exist in the repository. -/// -/// This list is used either to regenerate the Rust source in-tree (if -/// the `rebuild-isle` feature is enabled), or to verify that the ISLE -/// source in-tree corresponds to the ISLE source that was last used -/// to rebuild the Rust source (if the `rebuild-isle` feature is not -/// enabled). -#[derive(Clone, Debug)] -struct IsleCompilations { - items: Vec, -} - -#[derive(Clone, Debug)] -struct IsleCompilation { - output: std::path::PathBuf, - inputs: Vec, - untracked_inputs: Vec, -} - -/// Construct the list of compilations (transformations from ISLE -/// source to generated Rust source) that exist in the repository. -fn get_isle_compilations( - crate_dir: &std::path::Path, - out_dir: &std::path::Path, -) -> Result { - let cur_dir = std::env::current_dir()?; - - // Preludes. - let clif_lower_isle = out_dir.join("clif_lower.isle"); - let clif_opt_isle = out_dir.join("clif_opt.isle"); - let prelude_isle = - make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("prelude.isle")); - let prelude_opt_isle = - make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("prelude_opt.isle")); - let prelude_lower_isle = - make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("prelude_lower.isle")); - - // Directory for mid-end optimizations. - let src_opts = make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("opts")); - // Directories for lowering backends. - let src_isa_x64 = - make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("isa").join("x64")); - let src_isa_aarch64 = - make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("isa").join("aarch64")); - let src_isa_s390x = - make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("isa").join("s390x")); - - let src_isa_risc_v = - make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("isa").join("riscv64")); - // This is a set of ISLE compilation units. - // - // The format of each entry is: - // - // (output Rust code file, input ISLE source files) - // - // There should be one entry for each backend that uses ISLE for lowering, - // and if/when we replace our peephole optimization passes with ISLE, there - // should be an entry for each of those as well. - // - // N.B.: add any new compilation outputs to - // `scripts/force-rebuild-isle.sh` if they do not fit the pattern - // `cranelift/codegen/src/isa/*/lower/isle/generated_code.rs`! - Ok(IsleCompilations { - items: vec![ - // The mid-end optimization rules. - IsleCompilation { - output: out_dir.join("isle_opt.rs"), - inputs: vec![ - prelude_isle.clone(), - prelude_opt_isle, - src_opts.join("arithmetic.isle"), - src_opts.join("bitops.isle"), - src_opts.join("cprop.isle"), - src_opts.join("extends.isle"), - src_opts.join("icmp.isle"), - src_opts.join("remat.isle"), - src_opts.join("selects.isle"), - src_opts.join("shifts.isle"), - src_opts.join("vector.isle"), - ], - untracked_inputs: vec![clif_opt_isle], - }, - // The x86-64 instruction selector. - IsleCompilation { - output: out_dir.join("isle_x64.rs"), - inputs: vec![ - prelude_isle.clone(), - prelude_lower_isle.clone(), - src_isa_x64.join("inst.isle"), - src_isa_x64.join("lower.isle"), - ], - untracked_inputs: vec![clif_lower_isle.clone()], - }, - // The aarch64 instruction selector. - IsleCompilation { - output: out_dir.join("isle_aarch64.rs"), - inputs: vec![ - prelude_isle.clone(), - prelude_lower_isle.clone(), - src_isa_aarch64.join("inst.isle"), - src_isa_aarch64.join("inst_neon.isle"), - src_isa_aarch64.join("lower.isle"), - src_isa_aarch64.join("lower_dynamic_neon.isle"), - ], - untracked_inputs: vec![clif_lower_isle.clone()], - }, - // The s390x instruction selector. - IsleCompilation { - output: out_dir.join("isle_s390x.rs"), - inputs: vec![ - prelude_isle.clone(), - prelude_lower_isle.clone(), - src_isa_s390x.join("inst.isle"), - src_isa_s390x.join("lower.isle"), - ], - untracked_inputs: vec![clif_lower_isle.clone()], - }, - // The risc-v instruction selector. - IsleCompilation { - output: out_dir.join("isle_riscv64.rs"), - inputs: vec![ - prelude_isle.clone(), - prelude_lower_isle.clone(), - src_isa_risc_v.join("inst.isle"), - src_isa_risc_v.join("inst_vector.isle"), - src_isa_risc_v.join("lower.isle"), - ], - untracked_inputs: vec![clif_lower_isle.clone()], - }, - ], - }) -} - fn build_isle( crate_dir: &std::path::Path, isle_dir: &std::path::Path, ) -> Result<(), Box> { - let isle_compilations = get_isle_compilations(crate_dir, isle_dir)?; + let cur_dir = std::env::current_dir()?; + let isle_compilations = meta::isle::get_isle_compilations( + &make_isle_source_path_relative(&cur_dir, &crate_dir), + &make_isle_source_path_relative(&cur_dir, &isle_dir), + ); let mut had_error = false; for compilation in &isle_compilations.items { - for file in &compilation.inputs { + for file in &compilation.tracked_inputs { println!("cargo:rerun-if-changed={}", file.display()); } if let Err(e) = run_compilation(compilation) { had_error = true; eprintln!("Error building ISLE files:"); - eprintln!("{:?}", e); + eprintln!("{e}"); #[cfg(not(feature = "isle-errors"))] { eprintln!("To see a more detailed error report, run: "); @@ -330,11 +209,11 @@ fn run_compilation(compilation: &IsleCompilation) -> Result<(), Errors> { let code = { let file_paths = compilation - .inputs - .iter() - .chain(compilation.untracked_inputs.iter()); + .paths() + .map_err(|e| Errors::from_io(e, "list isle compilation file paths"))?; let mut options = isle::codegen::CodegenOptions::default(); + options.rule_trace = compilation.rule_trace; // Because we include!() the generated ISLE source, we cannot // put the global pragmas (`#![allow(...)]`) in the ISLE // source itself; we have to put them in the source that @@ -346,10 +225,7 @@ fn run_compilation(compilation: &IsleCompilation) -> Result<(), Errors> { }; let code = rustfmt(&code).unwrap_or_else(|e| { - println!( - "cargo:warning=Failed to run `rustfmt` on ISLE-generated code: {:?}", - e - ); + println!("cargo:warning=Failed to run `rustfmt` on ISLE-generated code: {e:?}"); code }); @@ -383,7 +259,7 @@ fn rustfmt(code: &str) -> std::io::Result { if !status.success() { return Err(std::io::Error::new( std::io::ErrorKind::Other, - format!("`rustfmt` exited with status {}", status), + format!("`rustfmt` exited with status {status}"), )); } diff --git a/cranelift/codegen/meta/Cargo.toml b/cranelift/codegen/meta/Cargo.toml index 4b930571018d..f6f039c08286 100644 --- a/cranelift/codegen/meta/Cargo.toml +++ b/cranelift/codegen/meta/Cargo.toml @@ -1,15 +1,19 @@ [package] name = "cranelift-codegen-meta" authors = ["The Cranelift Project Developers"] -version = "0.101.0" +version = "0.112.0" description = "Metaprogram for cranelift-codegen code generator library" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" readme = "README.md" edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [package.metadata.docs.rs] rustdoc-args = [ "--document-private-items" ] [dependencies] -cranelift-codegen-shared = { path = "../shared", version = "0.101.0" } +cranelift-codegen-shared = { path = "../shared", version = "0.112.0" } diff --git a/cranelift/codegen/meta/src/cdsl/settings.rs b/cranelift/codegen/meta/src/cdsl/settings.rs index ce209dba8c28..6ed68d24a2aa 100644 --- a/cranelift/codegen/meta/src/cdsl/settings.rs +++ b/cranelift/codegen/meta/src/cdsl/settings.rs @@ -189,7 +189,7 @@ impl PredicateNode { group.name, group.settings[bool_setting_index.0].name ), PredicateNode::SharedBool(ref group_name, ref bool_name) => { - format!("{}.{}()", group_name, bool_name) + format!("{group_name}.{bool_name}()") } PredicateNode::And(ref lhs, ref rhs) => { format!("{} && {}", lhs.render(group), rhs.render(group)) diff --git a/cranelift/codegen/meta/src/cdsl/types.rs b/cranelift/codegen/meta/src/cdsl/types.rs index 3eb32ebee67a..ce3c2e582553 100644 --- a/cranelift/codegen/meta/src/cdsl/types.rs +++ b/cranelift/codegen/meta/src/cdsl/types.rs @@ -17,7 +17,6 @@ static RUST_NAME_PREFIX: &str = "ir::types::"; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum ValueType { Lane(LaneType), - Reference(ReferenceType), Vector(VectorType), DynamicVector(DynamicVectorType), } @@ -28,15 +27,10 @@ impl ValueType { LaneTypeIterator::new() } - pub fn all_reference_types() -> ReferenceTypeIterator { - ReferenceTypeIterator::new() - } - /// Return a string containing the documentation comment for this type. pub fn doc(&self) -> String { match *self { ValueType::Lane(l) => l.doc(), - ValueType::Reference(r) => r.doc(), ValueType::Vector(ref v) => v.doc(), ValueType::DynamicVector(ref v) => v.doc(), } @@ -46,7 +40,6 @@ impl ValueType { pub fn lane_bits(&self) -> u64 { match *self { ValueType::Lane(l) => l.lane_bits(), - ValueType::Reference(r) => r.lane_bits(), ValueType::Vector(ref v) => v.lane_bits(), ValueType::DynamicVector(ref v) => v.lane_bits(), } @@ -69,7 +62,6 @@ impl ValueType { pub fn number(&self) -> u16 { match *self { ValueType::Lane(l) => l.number(), - ValueType::Reference(r) => r.number(), ValueType::Vector(ref v) => v.number(), ValueType::DynamicVector(ref v) => v.number(), } @@ -90,7 +82,6 @@ impl fmt::Display for ValueType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { ValueType::Lane(l) => l.fmt(f), - ValueType::Reference(r) => r.fmt(f), ValueType::Vector(ref v) => v.fmt(f), ValueType::DynamicVector(ref v) => v.fmt(f), } @@ -104,13 +95,6 @@ impl From for ValueType { } } -/// Create a ValueType from a given reference type. -impl From for ValueType { - fn from(reference: ReferenceType) -> Self { - ValueType::Reference(reference) - } -} - /// Create a ValueType from a given vector type. impl From for ValueType { fn from(vector: VectorType) -> Self { @@ -136,6 +120,12 @@ impl LaneType { /// Return a string containing the documentation comment for this lane type. pub fn doc(self) -> String { match self { + LaneType::Float(shared_types::Float::F16) => String::from( + "A 16-bit floating point type represented in the IEEE 754-2008 + *binary16* interchange format. This corresponds to the :c:type:`_Float16` + type in most C implementations. + WARNING: f16 support is a work-in-progress and is incomplete", + ), LaneType::Float(shared_types::Float::F32) => String::from( "A 32-bit floating point type represented in the IEEE 754-2008 *binary32* interchange format. This corresponds to the :c:type:`float` @@ -146,6 +136,12 @@ impl LaneType { *binary64* interchange format. This corresponds to the :c:type:`double` type in most C implementations.", ), + LaneType::Float(shared_types::Float::F128) => String::from( + "A 128-bit floating point type represented in the IEEE 754-2008 + *binary128* interchange format. This corresponds to the :c:type:`_Float128` + type in most C implementations. + WARNING: f128 support is a work-in-progress and is incomplete", + ), LaneType::Int(_) if self.lane_bits() < 32 => format!( "An integer type with {} bits. WARNING: arithmetic on {}bit integers is incomplete", @@ -168,13 +164,15 @@ impl LaneType { pub fn number(self) -> u16 { constants::LANE_BASE + match self { - LaneType::Int(shared_types::Int::I8) => 6, - LaneType::Int(shared_types::Int::I16) => 7, - LaneType::Int(shared_types::Int::I32) => 8, - LaneType::Int(shared_types::Int::I64) => 9, - LaneType::Int(shared_types::Int::I128) => 10, - LaneType::Float(shared_types::Float::F32) => 11, - LaneType::Float(shared_types::Float::F64) => 12, + LaneType::Int(shared_types::Int::I8) => 4, + LaneType::Int(shared_types::Int::I16) => 5, + LaneType::Int(shared_types::Int::I32) => 6, + LaneType::Int(shared_types::Int::I64) => 7, + LaneType::Int(shared_types::Int::I128) => 8, + LaneType::Float(shared_types::Float::F16) => 9, + LaneType::Float(shared_types::Float::F32) => 10, + LaneType::Float(shared_types::Float::F64) => 11, + LaneType::Float(shared_types::Float::F128) => 12, } } @@ -185,15 +183,17 @@ impl LaneType { 32 => shared_types::Int::I32, 64 => shared_types::Int::I64, 128 => shared_types::Int::I128, - _ => unreachable!("unxpected num bits for int"), + _ => unreachable!("unexpected num bits for int"), }) } pub fn float_from_bits(num_bits: u16) -> LaneType { LaneType::Float(match num_bits { + 16 => shared_types::Float::F16, 32 => shared_types::Float::F32, 64 => shared_types::Float::F64, - _ => unreachable!("unxpected num bits for float"), + 128 => shared_types::Float::F128, + _ => unreachable!("unexpected num bits for float"), }) } @@ -226,8 +226,8 @@ impl fmt::Debug for LaneType { f, "{}", match *self { - LaneType::Float(_) => format!("FloatType({})", inner_msg), - LaneType::Int(_) => format!("IntType({})", inner_msg), + LaneType::Float(_) => format!("FloatType({inner_msg})"), + LaneType::Int(_) => format!("IntType({inner_msg})"), } ) } @@ -418,79 +418,3 @@ impl fmt::Debug for DynamicVectorType { ) } } - -/// Reference type is scalar type, but not lane type. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct ReferenceType(pub shared_types::Reference); - -impl ReferenceType { - /// Return a string containing the documentation comment for this reference type. - pub fn doc(self) -> String { - format!("An opaque reference type with {} bits.", self.lane_bits()) - } - - /// Return the number of bits in a lane. - pub fn lane_bits(self) -> u64 { - match self.0 { - shared_types::Reference::R32 => 32, - shared_types::Reference::R64 => 64, - } - } - - /// Find the unique number associated with this reference type. - pub fn number(self) -> u16 { - constants::REFERENCE_BASE - + match self { - ReferenceType(shared_types::Reference::R32) => 0, - ReferenceType(shared_types::Reference::R64) => 1, - } - } - - pub fn ref_from_bits(num_bits: u16) -> ReferenceType { - ReferenceType(match num_bits { - 32 => shared_types::Reference::R32, - 64 => shared_types::Reference::R64, - _ => unreachable!("unexpected number of bits for a reference type"), - }) - } -} - -impl fmt::Display for ReferenceType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "r{}", self.lane_bits()) - } -} - -impl fmt::Debug for ReferenceType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ReferenceType(bits={})", self.lane_bits()) - } -} - -/// Create a ReferenceType from a given reference variant. -impl From for ReferenceType { - fn from(r: shared_types::Reference) -> Self { - ReferenceType(r) - } -} - -/// An iterator for different reference types. -pub(crate) struct ReferenceTypeIterator { - reference_iter: shared_types::ReferenceIterator, -} - -impl ReferenceTypeIterator { - /// Create a new reference type iterator. - fn new() -> Self { - Self { - reference_iter: shared_types::ReferenceIterator::new(), - } - } -} - -impl Iterator for ReferenceTypeIterator { - type Item = ReferenceType; - fn next(&mut self) -> Option { - self.reference_iter.next().map(ReferenceType::from) - } -} diff --git a/cranelift/codegen/meta/src/cdsl/typevar.rs b/cranelift/codegen/meta/src/cdsl/typevar.rs index fc42f55a36da..61f31d9dbf50 100644 --- a/cranelift/codegen/meta/src/cdsl/typevar.rs +++ b/cranelift/codegen/meta/src/cdsl/typevar.rs @@ -2,15 +2,14 @@ use std::cell::RefCell; use std::collections::BTreeSet; use std::fmt; use std::hash; -use std::iter::FromIterator; use std::ops; use std::rc::Rc; -use crate::cdsl::types::{LaneType, ReferenceType, ValueType}; +use crate::cdsl::types::{LaneType, ValueType}; const MAX_LANES: u16 = 256; const MAX_BITS: u16 = 128; -const MAX_FLOAT_BITS: u16 = 64; +const MAX_FLOAT_BITS: u16 = 128; /// Type variables can be used in place of concrete types when defining /// instructions. This makes the instructions *polymorphic*. @@ -57,10 +56,6 @@ impl TypeVar { let mut builder = TypeSetBuilder::new(); let (scalar_type, num_lanes) = match value_type { - ValueType::Reference(ReferenceType(reference_type)) => { - let bits = reference_type as RangeBound; - return TypeVar::new(name, doc, builder.refs(bits..bits).build()); - } ValueType::Lane(lane_type) => (lane_type, 1), ValueType::Vector(vec_type) => { (vec_type.lane_type(), vec_type.lane_count() as RangeBound) @@ -160,7 +155,7 @@ impl TypeVar { "can't halve all integer types" ); assert!( - ts.floats.is_empty() || *ts.floats.iter().min().unwrap() > 32, + ts.floats.is_empty() || *ts.floats.iter().min().unwrap() > 16, "can't halve all float types" ); } @@ -180,7 +175,7 @@ impl TypeVar { "can't halve all integer types" ); assert!( - ts.floats.is_empty() || *ts.floats.iter().min().unwrap() > 32, + ts.floats.is_empty() || *ts.floats.iter().min().unwrap() > 16, "can't halve all float types" ); assert!( @@ -209,9 +204,7 @@ impl TypeVar { "The `narrower` constraint does not apply to vectors" ); assert!( - (!ts.ints.is_empty() || !ts.floats.is_empty()) - && ts.refs.is_empty() - && ts.dynamic_lanes.is_empty(), + (!ts.ints.is_empty() || !ts.floats.is_empty()) && ts.dynamic_lanes.is_empty(), "The `narrower` constraint only applies to scalar ints or floats" ); } @@ -222,9 +215,7 @@ impl TypeVar { "The `wider` constraint does not apply to vectors" ); assert!( - (!ts.ints.is_empty() || !ts.floats.is_empty()) - && ts.refs.is_empty() - && ts.dynamic_lanes.is_empty(), + (!ts.ints.is_empty() || !ts.floats.is_empty()) && ts.dynamic_lanes.is_empty(), "The `wider` constraint only applies to scalar ints or floats" ); } @@ -395,30 +386,22 @@ pub(crate) struct TypeSet { pub dynamic_lanes: NumSet, pub ints: NumSet, pub floats: NumSet, - pub refs: NumSet, } impl TypeSet { - fn new( - lanes: NumSet, - dynamic_lanes: NumSet, - ints: NumSet, - floats: NumSet, - refs: NumSet, - ) -> Self { + fn new(lanes: NumSet, dynamic_lanes: NumSet, ints: NumSet, floats: NumSet) -> Self { Self { lanes, dynamic_lanes, ints, floats, - refs, } } /// Return the number of concrete types represented by this typeset. pub fn size(&self) -> usize { - self.lanes.len() * (self.ints.len() + self.floats.len() + self.refs.len()) - + self.dynamic_lanes.len() * (self.ints.len() + self.floats.len() + self.refs.len()) + self.lanes.len() * (self.ints.len() + self.floats.len()) + + self.dynamic_lanes.len() * (self.ints.len() + self.floats.len()) } /// Return the image of self across the derived function func. @@ -457,7 +440,6 @@ impl TypeSet { } copy.floats = NumSet::new(); - copy.refs = NumSet::new(); copy } @@ -465,7 +447,7 @@ impl TypeSet { fn half_width(&self) -> TypeSet { let mut copy = self.clone(); copy.ints = NumSet::from_iter(self.ints.iter().filter(|&&x| x > 8).map(|&x| x / 2)); - copy.floats = NumSet::from_iter(self.floats.iter().filter(|&&x| x > 32).map(|&x| x / 2)); + copy.floats = NumSet::from_iter(self.floats.iter().filter(|&&x| x > 16).map(|&x| x / 2)); copy } @@ -522,9 +504,6 @@ impl TypeSet { for &bits in &self.floats { ret.push(LaneType::float_from_bits(bits).by(num_lanes)); } - for &bits in &self.refs { - ret.push(ReferenceType::ref_from_bits(bits).into()); - } } for &num_lanes in &self.dynamic_lanes { for &bits in &self.ints { @@ -574,12 +553,6 @@ impl fmt::Debug for TypeSet { Vec::from_iter(self.floats.iter().map(|x| x.to_string())).join(", ") )); } - if !self.refs.is_empty() { - subsets.push(format!( - "refs={{{}}}", - Vec::from_iter(self.refs.iter().map(|x| x.to_string())).join(", ") - )); - } write!(fmt, "{})", subsets.join(", "))?; Ok(()) @@ -589,7 +562,6 @@ impl fmt::Debug for TypeSet { pub(crate) struct TypeSetBuilder { ints: Interval, floats: Interval, - refs: Interval, includes_scalars: bool, simd_lanes: Interval, dynamic_simd_lanes: Interval, @@ -600,7 +572,6 @@ impl TypeSetBuilder { Self { ints: Interval::None, floats: Interval::None, - refs: Interval::None, includes_scalars: true, simd_lanes: Interval::None, dynamic_simd_lanes: Interval::None, @@ -617,11 +588,6 @@ impl TypeSetBuilder { self.floats = interval.into(); self } - pub fn refs(mut self, interval: impl Into) -> Self { - assert!(self.refs == Interval::None); - self.refs = interval.into(); - self - } pub fn includes_scalars(mut self, includes_scalars: bool) -> Self { self.includes_scalars = includes_scalars; self @@ -644,8 +610,7 @@ impl TypeSetBuilder { range_to_set(self.simd_lanes.to_range(min_lanes..MAX_LANES, Some(1))), range_to_set(self.dynamic_simd_lanes.to_range(2..MAX_LANES, None)), range_to_set(self.ints.to_range(8..MAX_BITS, None)), - range_to_set(self.floats.to_range(32..64, None)), - range_to_set(self.refs.to_range(32..64, None)), + range_to_set(self.floats.to_range(16..MAX_FLOAT_BITS, None)), ) } } @@ -712,7 +677,7 @@ fn test_typevar_builder() { let type_set = TypeSetBuilder::new().floats(Interval::All).build(); assert_eq!(type_set.lanes, num_set![1]); - assert_eq!(type_set.floats, num_set![32, 64]); + assert_eq!(type_set.floats, num_set![16, 32, 64, 128]); assert!(type_set.ints.is_empty()); let type_set = TypeSetBuilder::new() @@ -721,7 +686,7 @@ fn test_typevar_builder() { .includes_scalars(false) .build(); assert_eq!(type_set.lanes, num_set![2, 4, 8, 16, 32, 64, 128, 256]); - assert_eq!(type_set.floats, num_set![32, 64]); + assert_eq!(type_set.floats, num_set![16, 32, 64, 128]); assert!(type_set.ints.is_empty()); let type_set = TypeSetBuilder::new() @@ -730,7 +695,7 @@ fn test_typevar_builder() { .includes_scalars(true) .build(); assert_eq!(type_set.lanes, num_set![1, 2, 4, 8, 16, 32, 64, 128, 256]); - assert_eq!(type_set.floats, num_set![32, 64]); + assert_eq!(type_set.floats, num_set![16, 32, 64, 128]); assert!(type_set.ints.is_empty()); let type_set = TypeSetBuilder::new() @@ -739,7 +704,7 @@ fn test_typevar_builder() { .includes_scalars(false) .build(); assert_eq!(type_set.lanes, num_set![2, 4, 8, 16, 32, 64, 128, 256]); - assert_eq!(type_set.floats, num_set![32, 64]); + assert_eq!(type_set.floats, num_set![16, 32, 64, 128]); assert!(type_set.dynamic_lanes.is_empty()); assert!(type_set.ints.is_empty()); @@ -754,7 +719,7 @@ fn test_typevar_builder() { num_set![2, 4, 8, 16, 32, 64, 128, 256] ); assert_eq!(type_set.ints, num_set![8, 16, 32, 64, 128]); - assert_eq!(type_set.floats, num_set![32, 64]); + assert_eq!(type_set.floats, num_set![16, 32, 64, 128]); assert_eq!(type_set.lanes, num_set![1]); let type_set = TypeSetBuilder::new() @@ -766,7 +731,7 @@ fn test_typevar_builder() { type_set.dynamic_lanes, num_set![2, 4, 8, 16, 32, 64, 128, 256] ); - assert_eq!(type_set.floats, num_set![32, 64]); + assert_eq!(type_set.floats, num_set![16, 32, 64, 128]); assert_eq!(type_set.lanes, num_set![1]); assert!(type_set.ints.is_empty()); @@ -872,12 +837,12 @@ fn test_forward_images() { TypeSetBuilder::new().ints(8..16).build() ); assert_eq!( - TypeSetBuilder::new().floats(32..32).build().half_width(), + TypeSetBuilder::new().floats(16..16).build().half_width(), empty_set ); assert_eq!( - TypeSetBuilder::new().floats(32..64).build().half_width(), - TypeSetBuilder::new().floats(32..32).build() + TypeSetBuilder::new().floats(32..128).build().half_width(), + TypeSetBuilder::new().floats(16..64).build() ); // Double width. @@ -894,8 +859,8 @@ fn test_forward_images() { TypeSetBuilder::new().floats(64..64).build() ); assert_eq!( - TypeSetBuilder::new().floats(32..64).build().double_width(), - TypeSetBuilder::new().floats(64..64).build() + TypeSetBuilder::new().floats(16..64).build().double_width(), + TypeSetBuilder::new().floats(32..128).build() ); } diff --git a/cranelift/codegen/meta/src/error.rs b/cranelift/codegen/meta/src/error.rs index 4cbf3d82851c..e898dea4e296 100644 --- a/cranelift/codegen/meta/src/error.rs +++ b/cranelift/codegen/meta/src/error.rs @@ -18,6 +18,8 @@ impl Error { } } +impl std::error::Error for Error {} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.inner) @@ -41,8 +43,8 @@ enum ErrorInner { impl fmt::Display for ErrorInner { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - ErrorInner::Msg(ref s) => write!(f, "{}", s), - ErrorInner::IoError(ref e) => write!(f, "{}", e), + ErrorInner::Msg(ref s) => write!(f, "{s}"), + ErrorInner::IoError(ref e) => write!(f, "{e}"), } } } diff --git a/cranelift/codegen/meta/src/gen_inst.rs b/cranelift/codegen/meta/src/gen_inst.rs index e43ebf79f1fe..7609d060d671 100644 --- a/cranelift/codegen/meta/src/gen_inst.rs +++ b/cranelift/codegen/meta/src/gen_inst.rs @@ -92,7 +92,7 @@ fn gen_instruction_data(formats: &[Rc], fmt: &mut Formatter) "blocks: [ir::BlockCall; {}],", format.num_block_operands ), - n => panic!("Too many block operands in instruction: {}", n), + n => panic!("Too many block operands in instruction: {n}"), } for field in &format.imm_fields { @@ -137,7 +137,7 @@ fn gen_arguments_method(formats: &[Rc], fmt: &mut Formatter, m.arm( name, vec![format!("ref {}args", mut_), "..".to_string()], - format!("args.{}(pool)", as_slice), + format!("args.{as_slice}(pool)"), ); continue; } @@ -145,13 +145,13 @@ fn gen_arguments_method(formats: &[Rc], fmt: &mut Formatter, // Fixed args. let mut fields = Vec::new(); let arg = if format.num_value_operands == 0 { - format!("&{}[]", mut_) + format!("&{mut_}[]") } else if format.num_value_operands == 1 { - fields.push(format!("ref {}arg", mut_)); - format!("{}(arg)", rslice) + fields.push(format!("ref {mut_}arg")); + format!("{rslice}(arg)") } else { let arg = format!("args_arity{}", format.num_value_operands); - fields.push(format!("args: ref {}{}", mut_, arg)); + fields.push(format!("args: ref {mut_}{arg}")); arg }; fields.push("..".into()); @@ -277,8 +277,8 @@ fn gen_instruction_data_impl(formats: &[Rc], fmt: &mut Format members.push(field.member); } - let pat1 = members.iter().map(|x| format!("{}: ref {}1", x, x)).collect::>().join(", "); - let pat2 = members.iter().map(|x| format!("{}: ref {}2", x, x)).collect::>().join(", "); + let pat1 = members.iter().map(|x| format!("{x}: ref {x}1")).collect::>().join(", "); + let pat2 = members.iter().map(|x| format!("{x}: ref {x}2")).collect::>().join(", "); fmtln!(fmt, "({} {{ {} }}, {} {{ {} }}) => {{", name, pat1, name, pat2); fmt.indent(|fmt| { fmt.line("opcode1 == opcode2"); @@ -725,7 +725,7 @@ fn gen_bitset<'a, T: IntoIterator>( assert!(u32::from(*x) < (1 << u32::from(field_size))); acc | x }); - fmtln!(fmt, "{}: BitSet::({}),", name, field_size, bits); + fmtln!(fmt, "{}: ScalarBitSet::({}),", name, field_size, bits); } fn iterable_to_string>(iterable: T) -> String { @@ -734,7 +734,7 @@ fn iterable_to_string>(iterable: T) - .map(|x| x.to_string()) .collect::>() .join(", "); - format!("{{{}}}", elems) + format!("{{{elems}}}") } fn typeset_to_string(ts: &TypeSet) -> String { @@ -745,9 +745,6 @@ fn typeset_to_string(ts: &TypeSet) -> String { if !ts.floats.is_empty() { result += &format!(", floats={}", iterable_to_string(&ts.floats)); } - if !ts.refs.is_empty() { - result += &format!(", refs={}", iterable_to_string(&ts.refs)); - } result += ")"; result } @@ -774,7 +771,6 @@ pub(crate) fn gen_typesets_table(type_sets: &UniqueTable, fmt: &mut For gen_bitset(&ts.dynamic_lanes, "dynamic_lanes", 16, fmt); gen_bitset(&ts.ints, "ints", 8, fmt); gen_bitset(&ts.floats, "floats", 8, fmt); - gen_bitset(&ts.refs, "refs", 8, fmt); }); fmt.line("},"); } @@ -853,7 +849,7 @@ fn gen_type_constraints(all_inst: &AllInstructions, fmt: &mut Formatter) { ); fmt.comment(format!("Constraints=[{}]", constraints .iter() - .map(|x| format!("'{}'", x)) + .map(|x| format!("'{x}'")) .collect::>() .join(", "))); if let Some(poly) = &inst.polymorphic_info { @@ -916,7 +912,7 @@ fn gen_member_inits(format: &InstructionFormat, fmt: &mut Formatter) { } else if format.num_value_operands > 1 { let mut args = Vec::new(); for i in 0..format.num_value_operands { - args.push(format!("arg{}", i)); + args.push(format!("arg{i}")); } fmtln!(fmt, "args: [{}],", args.join(", ")); } @@ -928,7 +924,7 @@ fn gen_member_inits(format: &InstructionFormat, fmt: &mut Formatter) { n => { let mut blocks = Vec::new(); for i in 0..n { - blocks.push(format!("block{}", i)); + blocks.push(format!("block{i}")); } fmtln!(fmt, "blocks: [{}],", blocks.join(", ")); } @@ -953,7 +949,7 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) { } // Then the block operands. - args.extend((0..format.num_block_operands).map(|i| format!("block{}: ir::BlockCall", i))); + args.extend((0..format.num_block_operands).map(|i| format!("block{i}: ir::BlockCall"))); // Then the value operands. if format.has_value_list { @@ -963,7 +959,7 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) { } else { // Take a fixed number of value operands. for i in 0..format.num_value_operands { - args.push(format!("arg{}: Value", i)); + args.push(format!("arg{i}: Value")); } } @@ -973,7 +969,7 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) { args.join(", ") ); - let imms_need_sign_extension = format + let imms_need_masking = format .imm_fields .iter() .any(|f| f.kind.rust_type == "ir::immediates::Imm64"); @@ -986,7 +982,7 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) { fmtln!( fmt, "let{} data = ir::InstructionData::{} {{", - if imms_need_sign_extension { " mut" } else { "" }, + if imms_need_masking { " mut" } else { "" }, format.name ); fmt.indent(|fmt| { @@ -995,12 +991,12 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) { }); fmtln!(fmt, "};"); - if imms_need_sign_extension { - fmtln!(fmt, "data.sign_extend_immediates(ctrl_typevar);"); + if imms_need_masking { + fmtln!(fmt, "data.mask_immediates(ctrl_typevar);"); } // Assert that this opcode belongs to this format - fmtln!(fmt, "debug_assert_eq!(opcode.format(), InstructionFormat::from(&data), \"Wrong InstructionFormat for Opcode: {}\", opcode);"); + fmtln!(fmt, "debug_assert_eq!(opcode.format(), InstructionFormat::from(&data), \"Wrong InstructionFormat for Opcode: {opcode}\");"); fmt.line("self.build(data, ctrl_typevar)"); }); @@ -1202,7 +1198,7 @@ fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Fo inst.value_results .iter() .enumerate() - .map(|(i, _)| format!("results[{}]", i)) + .map(|(i, _)| format!("results[{i}]")) .collect::>() .join(", ") ); @@ -1211,499 +1207,6 @@ fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Fo fmtln!(fmt, "}") } -/// Which ISLE target are we generating code for? -#[derive(Clone, Copy, PartialEq, Eq)] -enum IsleTarget { - /// Generating code for instruction selection and lowering. - Lower, - /// Generating code for CLIF to CLIF optimizations. - Opt, -} - -fn gen_common_isle( - formats: &[Rc], - instructions: &AllInstructions, - fmt: &mut Formatter, - isle_target: IsleTarget, -) { - use std::collections::{BTreeMap, BTreeSet}; - use std::fmt::Write; - - use crate::cdsl::formats::FormatField; - - fmt.multi_line( - r#" -;; GENERATED BY `gen_isle`. DO NOT EDIT!!! -;; -;; This ISLE file defines all the external type declarations for Cranelift's -;; data structures that ISLE will process, such as `InstructionData` and -;; `Opcode`. - "#, - ); - fmt.empty_line(); - - // Collect and deduplicate the immediate types from the instruction fields. - let rust_name = |f: &FormatField| f.kind.rust_type.rsplit("::").next().unwrap(); - let fields = |f: &FormatField| f.kind.fields.clone(); - let immediate_types: BTreeMap<_, _> = formats - .iter() - .flat_map(|f| { - f.imm_fields - .iter() - .map(|i| (rust_name(i), fields(i))) - .collect::>() - }) - .collect(); - - // Separate the `enum` immediates (e.g., `FloatCC`) from other kinds of - // immediates. - let (enums, others): (BTreeMap<_, _>, BTreeMap<_, _>) = immediate_types - .iter() - .partition(|(_, field)| field.enum_values().is_some()); - - // Generate all the extern type declarations we need for the non-`enum` - // immediates. - fmt.line(";;;; Extern type declarations for immediates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); - fmt.empty_line(); - for ty in others.keys() { - fmtln!(fmt, "(type {} (primitive {}))", ty, ty); - } - fmt.empty_line(); - - // Generate the `enum` immediates, expanding all of the available variants - // into ISLE. - for (name, field) in enums { - let field = field.enum_values().expect("only enums considered here"); - let variants = field.values().cloned().collect(); - gen_isle_enum(name, variants, fmt) - } - - // Generate all of the value arrays we need for `InstructionData` as well as - // the constructors and extractors for them. - fmt.line(";;;; Value Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); - fmt.empty_line(); - let value_array_arities: BTreeSet<_> = formats - .iter() - .filter(|f| f.typevar_operand.is_some() && !f.has_value_list && f.num_value_operands != 1) - .map(|f| f.num_value_operands) - .collect(); - for n in value_array_arities { - fmtln!(fmt, ";; ISLE representation of `[Value; {}]`.", n); - fmtln!(fmt, "(type ValueArray{} extern (enum))", n); - fmt.empty_line(); - - fmtln!( - fmt, - "(decl value_array_{} ({}) ValueArray{})", - n, - (0..n).map(|_| "Value").collect::>().join(" "), - n - ); - fmtln!( - fmt, - "(extern constructor value_array_{} pack_value_array_{})", - n, - n - ); - fmtln!( - fmt, - "(extern extractor infallible value_array_{} unpack_value_array_{})", - n, - n - ); - fmt.empty_line(); - } - - // Generate all of the block arrays we need for `InstructionData` as well as - // the constructors and extractors for them. - fmt.line(";;;; Block Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); - fmt.empty_line(); - let block_array_arities: BTreeSet<_> = formats - .iter() - .filter(|f| f.num_block_operands > 1) - .map(|f| f.num_block_operands) - .collect(); - for n in block_array_arities { - fmtln!(fmt, ";; ISLE representation of `[BlockCall; {}]`.", n); - fmtln!(fmt, "(type BlockArray{} extern (enum))", n); - fmt.empty_line(); - - fmtln!( - fmt, - "(decl block_array_{0} ({1}) BlockArray{0})", - n, - (0..n).map(|_| "BlockCall").collect::>().join(" ") - ); - - fmtln!( - fmt, - "(extern constructor block_array_{0} pack_block_array_{0})", - n - ); - - fmtln!( - fmt, - "(extern extractor infallible block_array_{0} unpack_block_array_{0})", - n - ); - fmt.empty_line(); - } - - // Generate the extern type declaration for `Opcode`. - fmt.line(";;;; `Opcode` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); - fmt.empty_line(); - fmt.line("(type Opcode extern"); - fmt.indent(|fmt| { - fmt.line("(enum"); - fmt.indent(|fmt| { - for inst in instructions { - fmtln!(fmt, "{}", inst.camel_name); - } - }); - fmt.line(")"); - }); - fmt.line(")"); - fmt.empty_line(); - - // Generate the extern type declaration for `InstructionData`. - fmtln!( - fmt, - ";;;; `InstructionData` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;", - ); - fmt.empty_line(); - fmtln!(fmt, "(type InstructionData extern"); - fmt.indent(|fmt| { - fmt.line("(enum"); - fmt.indent(|fmt| { - for format in formats { - let mut s = format!("({} (opcode Opcode)", format.name); - if format.has_value_list { - s.push_str(" (args ValueList)"); - } else if format.num_value_operands == 1 { - s.push_str(" (arg Value)"); - } else if format.num_value_operands > 1 { - write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap(); - } - - match format.num_block_operands { - 0 => (), - 1 => write!(&mut s, " (destination BlockCall)").unwrap(), - n => write!(&mut s, " (blocks BlockArray{})", n).unwrap(), - } - - for field in &format.imm_fields { - write!( - &mut s, - " ({} {})", - field.member, - field.kind.rust_type.rsplit("::").next().unwrap() - ) - .unwrap(); - } - s.push(')'); - fmt.line(&s); - } - }); - fmt.line(")"); - }); - fmt.line(")"); - fmt.empty_line(); - - // Generate the helper extractors for each opcode's full instruction. - fmtln!( - fmt, - ";;;; Extracting Opcode, Operands, and Immediates from `InstructionData` ;;;;;;;;", - ); - fmt.empty_line(); - let ret_ty = match isle_target { - IsleTarget::Lower => "Inst", - IsleTarget::Opt => "Value", - }; - for inst in instructions { - if isle_target == IsleTarget::Opt - && (inst.format.has_value_list || inst.value_results.len() != 1) - { - continue; - } - - fmtln!( - fmt, - "(decl {} ({}{}) {})", - inst.name, - match isle_target { - IsleTarget::Lower => "", - IsleTarget::Opt => "Type ", - }, - inst.operands_in - .iter() - .map(|o| { - let ty = o.kind.rust_type; - if ty == "&[Value]" { - "ValueSlice" - } else { - ty.rsplit("::").next().unwrap() - } - }) - .collect::>() - .join(" "), - ret_ty - ); - fmtln!(fmt, "(extractor"); - fmt.indent(|fmt| { - fmtln!( - fmt, - "({} {}{})", - inst.name, - match isle_target { - IsleTarget::Lower => "", - IsleTarget::Opt => "ty ", - }, - inst.operands_in - .iter() - .map(|o| { o.name }) - .collect::>() - .join(" ") - ); - - let mut s = format!( - "(inst_data{} (InstructionData.{} (Opcode.{})", - match isle_target { - IsleTarget::Lower => "", - IsleTarget::Opt => " ty", - }, - inst.format.name, - inst.camel_name - ); - - // Value and varargs operands. - if inst.format.has_value_list { - // The instruction format uses a value list, but the - // instruction itself might have not only a `&[Value]` - // varargs operand, but also one or more `Value` operands as - // well. If this is the case, then we need to read them off - // the front of the `ValueList`. - let values: Vec<_> = inst - .operands_in - .iter() - .filter(|o| o.is_value()) - .map(|o| o.name) - .collect(); - let varargs = inst - .operands_in - .iter() - .find(|o| o.is_varargs()) - .unwrap() - .name; - if values.is_empty() { - write!(&mut s, " (value_list_slice {})", varargs).unwrap(); - } else { - write!( - &mut s, - " (unwrap_head_value_list_{} {} {})", - values.len(), - values.join(" "), - varargs - ) - .unwrap(); - } - } else if inst.format.num_value_operands == 1 { - write!( - &mut s, - " {}", - inst.operands_in.iter().find(|o| o.is_value()).unwrap().name - ) - .unwrap(); - } else if inst.format.num_value_operands > 1 { - let values = inst - .operands_in - .iter() - .filter(|o| o.is_value()) - .map(|o| o.name) - .collect::>(); - assert_eq!(values.len(), inst.format.num_value_operands); - let values = values.join(" "); - write!( - &mut s, - " (value_array_{} {})", - inst.format.num_value_operands, values, - ) - .unwrap(); - } - - // Immediates. - let imm_operands: Vec<_> = inst - .operands_in - .iter() - .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block()) - .collect(); - assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),); - for op in imm_operands { - write!(&mut s, " {}", op.name).unwrap(); - } - - // Blocks. - let block_operands: Vec<_> = inst - .operands_in - .iter() - .filter(|o| o.kind.is_block()) - .collect(); - assert_eq!(block_operands.len(), inst.format.num_block_operands); - assert!(block_operands.len() <= 2); - - if !block_operands.is_empty() { - if block_operands.len() == 1 { - write!(&mut s, " {}", block_operands[0].name).unwrap(); - } else { - let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect(); - let blocks = blocks.join(" "); - write!( - &mut s, - " (block_array_{} {})", - inst.format.num_block_operands, blocks, - ) - .unwrap(); - } - } - - s.push_str("))"); - fmt.line(&s); - }); - fmt.line(")"); - - // Generate a constructor if this is the mid-end prelude. - if isle_target == IsleTarget::Opt { - fmtln!( - fmt, - "(rule ({} ty {})", - inst.name, - inst.operands_in - .iter() - .map(|o| o.name) - .collect::>() - .join(" ") - ); - fmt.indent(|fmt| { - let mut s = format!( - "(make_inst ty (InstructionData.{} (Opcode.{})", - inst.format.name, inst.camel_name - ); - - // Handle values. Note that we skip generating - // constructors for any instructions with variadic - // value lists. This is fine for the mid-end because - // in practice only calls and branches (for branch - // args) use this functionality, and neither can - // really be optimized or rewritten in the mid-end - // (currently). - // - // As a consequence, we only have to handle the - // one-`Value` case, in which the `Value` is directly - // in the `InstructionData`, and the multiple-`Value` - // case, in which the `Value`s are in a - // statically-sized array (e.g. `[Value; 2]` for a - // binary op). - assert!(!inst.format.has_value_list); - if inst.format.num_value_operands == 1 { - write!( - &mut s, - " {}", - inst.operands_in.iter().find(|o| o.is_value()).unwrap().name - ) - .unwrap(); - } else if inst.format.num_value_operands > 1 { - // As above, get all bindings together, and pass - // to a sub-term; here we use a constructor to - // build the value array. - let values = inst - .operands_in - .iter() - .filter(|o| o.is_value()) - .map(|o| o.name) - .collect::>(); - assert_eq!(values.len(), inst.format.num_value_operands); - let values = values.join(" "); - write!( - &mut s, - " (value_array_{}_ctor {})", - inst.format.num_value_operands, values - ) - .unwrap(); - } - - if inst.format.num_block_operands > 0 { - let blocks: Vec<_> = inst - .operands_in - .iter() - .filter(|o| o.kind.is_block()) - .map(|o| o.name) - .collect(); - if inst.format.num_block_operands == 1 { - write!(&mut s, " {}", blocks.first().unwrap(),).unwrap(); - } else { - write!( - &mut s, - " (block_array_{} {})", - inst.format.num_block_operands, - blocks.join(" ") - ) - .unwrap(); - } - } - - // Immediates (non-value args). - for o in inst - .operands_in - .iter() - .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block()) - { - write!(&mut s, " {}", o.name).unwrap(); - } - s.push_str("))"); - fmt.line(&s); - }); - fmt.line(")"); - } - - fmt.empty_line(); - } -} - -fn gen_opt_isle( - formats: &[Rc], - instructions: &AllInstructions, - fmt: &mut Formatter, -) { - gen_common_isle(formats, instructions, fmt, IsleTarget::Opt); -} - -fn gen_lower_isle( - formats: &[Rc], - instructions: &AllInstructions, - fmt: &mut Formatter, -) { - gen_common_isle(formats, instructions, fmt, IsleTarget::Lower); -} - -/// Generate an `enum` immediate in ISLE. -fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) { - variants.sort(); - let prefix = format!(";;;; Enumerated Immediate: {} ", name); - fmtln!(fmt, "{:;<80}", prefix); - fmt.empty_line(); - fmtln!(fmt, "(type {} extern", name); - fmt.indent(|fmt| { - fmt.line("(enum"); - fmt.indent(|fmt| { - for variant in variants { - fmtln!(fmt, "{}", variant); - } - }); - fmt.line(")"); - }); - fmt.line(")"); - fmt.empty_line(); -} - /// Generate a Builder trait with methods for all instructions. fn gen_builder( instructions: &AllInstructions, @@ -1748,10 +1251,7 @@ pub(crate) fn generate( all_inst: &AllInstructions, opcode_filename: &str, inst_builder_filename: &str, - isle_opt_filename: &str, - isle_lower_filename: &str, - out_dir: &str, - isle_dir: &str, + out_dir: &std::path::Path, ) -> Result<(), error::Error> { // Opcodes. let mut fmt = Formatter::new(); @@ -1765,16 +1265,6 @@ pub(crate) fn generate( gen_type_constraints(all_inst, &mut fmt); fmt.update_file(opcode_filename, out_dir)?; - // ISLE DSL: mid-end ("opt") generated bindings. - let mut fmt = Formatter::new(); - gen_opt_isle(&formats, all_inst, &mut fmt); - fmt.update_file(isle_opt_filename, isle_dir)?; - - // ISLE DSL: lowering generated bindings. - let mut fmt = Formatter::new(); - gen_lower_isle(&formats, all_inst, &mut fmt); - fmt.update_file(isle_lower_filename, isle_dir)?; - // Instruction builder. let mut fmt = Formatter::new(); gen_builder(all_inst, &formats, &mut fmt); diff --git a/cranelift/codegen/meta/src/gen_isle.rs b/cranelift/codegen/meta/src/gen_isle.rs new file mode 100644 index 000000000000..e07ef89881eb --- /dev/null +++ b/cranelift/codegen/meta/src/gen_isle.rs @@ -0,0 +1,513 @@ +use std::rc::Rc; + +use crate::cdsl::formats::InstructionFormat; +use crate::cdsl::instructions::AllInstructions; +use crate::error; +use crate::srcgen::Formatter; + +/// Which ISLE target are we generating code for? +#[derive(Clone, Copy, PartialEq, Eq)] +enum IsleTarget { + /// Generating code for instruction selection and lowering. + Lower, + /// Generating code for CLIF to CLIF optimizations. + Opt, +} + +fn gen_common_isle( + formats: &[Rc], + instructions: &AllInstructions, + fmt: &mut Formatter, + isle_target: IsleTarget, +) { + use std::collections::{BTreeMap, BTreeSet}; + use std::fmt::Write; + + use crate::cdsl::formats::FormatField; + + fmt.multi_line( + r#" +;; GENERATED BY `gen_isle`. DO NOT EDIT!!! +;; +;; This ISLE file defines all the external type declarations for Cranelift's +;; data structures that ISLE will process, such as `InstructionData` and +;; `Opcode`. + "#, + ); + fmt.empty_line(); + + // Collect and deduplicate the immediate types from the instruction fields. + let rust_name = |f: &FormatField| f.kind.rust_type.rsplit("::").next().unwrap(); + let fields = |f: &FormatField| f.kind.fields.clone(); + let immediate_types: BTreeMap<_, _> = formats + .iter() + .flat_map(|f| { + f.imm_fields + .iter() + .map(|i| (rust_name(i), fields(i))) + .collect::>() + }) + .collect(); + + // Separate the `enum` immediates (e.g., `FloatCC`) from other kinds of + // immediates. + let (enums, others): (BTreeMap<_, _>, BTreeMap<_, _>) = immediate_types + .iter() + .partition(|(_, field)| field.enum_values().is_some()); + + // Generate all the extern type declarations we need for the non-`enum` + // immediates. + fmt.line(";;;; Extern type declarations for immediates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); + fmt.empty_line(); + for ty in others.keys() { + fmtln!(fmt, "(type {} (primitive {}))", ty, ty); + } + fmt.empty_line(); + + // Generate the `enum` immediates, expanding all of the available variants + // into ISLE. + for (name, field) in enums { + let field = field.enum_values().expect("only enums considered here"); + let variants = field.values().cloned().collect(); + gen_isle_enum(name, variants, fmt) + } + + // Generate all of the value arrays we need for `InstructionData` as well as + // the constructors and extractors for them. + fmt.line(";;;; Value Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); + fmt.empty_line(); + let value_array_arities: BTreeSet<_> = formats + .iter() + .filter(|f| f.typevar_operand.is_some() && !f.has_value_list && f.num_value_operands != 1) + .map(|f| f.num_value_operands) + .collect(); + for n in value_array_arities { + fmtln!(fmt, ";; ISLE representation of `[Value; {}]`.", n); + fmtln!(fmt, "(type ValueArray{} extern (enum))", n); + fmt.empty_line(); + + fmtln!( + fmt, + "(decl value_array_{} ({}) ValueArray{})", + n, + (0..n).map(|_| "Value").collect::>().join(" "), + n + ); + fmtln!( + fmt, + "(extern constructor value_array_{} pack_value_array_{})", + n, + n + ); + fmtln!( + fmt, + "(extern extractor infallible value_array_{} unpack_value_array_{})", + n, + n + ); + fmt.empty_line(); + } + + // Generate all of the block arrays we need for `InstructionData` as well as + // the constructors and extractors for them. + fmt.line(";;;; Block Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); + fmt.empty_line(); + let block_array_arities: BTreeSet<_> = formats + .iter() + .filter(|f| f.num_block_operands > 1) + .map(|f| f.num_block_operands) + .collect(); + for n in block_array_arities { + fmtln!(fmt, ";; ISLE representation of `[BlockCall; {}]`.", n); + fmtln!(fmt, "(type BlockArray{} extern (enum))", n); + fmt.empty_line(); + + fmtln!( + fmt, + "(decl block_array_{0} ({1}) BlockArray{0})", + n, + (0..n).map(|_| "BlockCall").collect::>().join(" ") + ); + + fmtln!( + fmt, + "(extern constructor block_array_{0} pack_block_array_{0})", + n + ); + + fmtln!( + fmt, + "(extern extractor infallible block_array_{0} unpack_block_array_{0})", + n + ); + fmt.empty_line(); + } + + // Generate the extern type declaration for `Opcode`. + fmt.line(";;;; `Opcode` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); + fmt.empty_line(); + fmt.line("(type Opcode extern"); + fmt.indent(|fmt| { + fmt.line("(enum"); + fmt.indent(|fmt| { + for inst in instructions { + fmtln!(fmt, "{}", inst.camel_name); + } + }); + fmt.line(")"); + }); + fmt.line(")"); + fmt.empty_line(); + + // Generate the extern type declaration for `InstructionData`. + fmtln!( + fmt, + ";;;; `InstructionData` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;", + ); + fmt.empty_line(); + fmtln!(fmt, "(type InstructionData extern"); + fmt.indent(|fmt| { + fmt.line("(enum"); + fmt.indent(|fmt| { + for format in formats { + let mut s = format!("({} (opcode Opcode)", format.name); + if format.has_value_list { + s.push_str(" (args ValueList)"); + } else if format.num_value_operands == 1 { + s.push_str(" (arg Value)"); + } else if format.num_value_operands > 1 { + write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap(); + } + + match format.num_block_operands { + 0 => (), + 1 => write!(&mut s, " (destination BlockCall)").unwrap(), + n => write!(&mut s, " (blocks BlockArray{n})").unwrap(), + } + + for field in &format.imm_fields { + write!( + &mut s, + " ({} {})", + field.member, + field.kind.rust_type.rsplit("::").next().unwrap() + ) + .unwrap(); + } + s.push(')'); + fmt.line(&s); + } + }); + fmt.line(")"); + }); + fmt.line(")"); + fmt.empty_line(); + + // Generate the helper extractors for each opcode's full instruction. + fmtln!( + fmt, + ";;;; Extracting Opcode, Operands, and Immediates from `InstructionData` ;;;;;;;;", + ); + fmt.empty_line(); + let ret_ty = match isle_target { + IsleTarget::Lower => "Inst", + IsleTarget::Opt => "Value", + }; + for inst in instructions { + if isle_target == IsleTarget::Opt + && (inst.format.has_value_list || inst.value_results.len() != 1) + { + continue; + } + + let has_ty_param = !inst.format.has_value_list && inst.value_results.len() >= 1; + + fmtln!( + fmt, + "(decl {} ({}{}) {})", + inst.name, + if has_ty_param { "Type " } else { "" }, + inst.operands_in + .iter() + .map(|o| { + let ty = o.kind.rust_type; + if ty == "&[Value]" { + "ValueSlice" + } else { + ty.rsplit("::").next().unwrap() + } + }) + .collect::>() + .join(" "), + ret_ty + ); + fmtln!(fmt, "(attr {} (tag clif_{}))", inst.name, inst.name); + fmtln!(fmt, "(extractor"); + fmt.indent(|fmt| { + fmtln!( + fmt, + "({} {}{})", + inst.name, + if has_ty_param { "ty " } else { "" }, + inst.operands_in + .iter() + .map(|o| { o.name }) + .collect::>() + .join(" ") + ); + + let mut s = format!( + "(inst_data {} (InstructionData.{} (Opcode.{})", + if has_ty_param { "ty" } else { "_" }, + inst.format.name, + inst.camel_name + ); + + // Value and varargs operands. + if inst.format.has_value_list { + // The instruction format uses a value list, but the + // instruction itself might have not only a `&[Value]` + // varargs operand, but also one or more `Value` operands as + // well. If this is the case, then we need to read them off + // the front of the `ValueList`. + let values: Vec<_> = inst + .operands_in + .iter() + .filter(|o| o.is_value()) + .map(|o| o.name) + .collect(); + let varargs = inst + .operands_in + .iter() + .find(|o| o.is_varargs()) + .unwrap() + .name; + if values.is_empty() { + write!(&mut s, " (value_list_slice {varargs})").unwrap(); + } else { + write!( + &mut s, + " (unwrap_head_value_list_{} {} {})", + values.len(), + values.join(" "), + varargs + ) + .unwrap(); + } + } else if inst.format.num_value_operands == 1 { + write!( + &mut s, + " {}", + inst.operands_in.iter().find(|o| o.is_value()).unwrap().name + ) + .unwrap(); + } else if inst.format.num_value_operands > 1 { + let values = inst + .operands_in + .iter() + .filter(|o| o.is_value()) + .map(|o| o.name) + .collect::>(); + assert_eq!(values.len(), inst.format.num_value_operands); + let values = values.join(" "); + write!( + &mut s, + " (value_array_{} {})", + inst.format.num_value_operands, values, + ) + .unwrap(); + } + + // Immediates. + let imm_operands: Vec<_> = inst + .operands_in + .iter() + .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block()) + .collect(); + assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),); + for op in imm_operands { + write!(&mut s, " {}", op.name).unwrap(); + } + + // Blocks. + let block_operands: Vec<_> = inst + .operands_in + .iter() + .filter(|o| o.kind.is_block()) + .collect(); + assert_eq!(block_operands.len(), inst.format.num_block_operands); + assert!(block_operands.len() <= 2); + + if !block_operands.is_empty() { + if block_operands.len() == 1 { + write!(&mut s, " {}", block_operands[0].name).unwrap(); + } else { + let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect(); + let blocks = blocks.join(" "); + write!( + &mut s, + " (block_array_{} {})", + inst.format.num_block_operands, blocks, + ) + .unwrap(); + } + } + + s.push_str("))"); + fmt.line(&s); + }); + fmt.line(")"); + + // Generate a constructor if this is the mid-end prelude. + if isle_target == IsleTarget::Opt { + fmtln!( + fmt, + "(rule ({} ty {})", + inst.name, + inst.operands_in + .iter() + .map(|o| o.name) + .collect::>() + .join(" ") + ); + fmt.indent(|fmt| { + let mut s = format!( + "(make_inst ty (InstructionData.{} (Opcode.{})", + inst.format.name, inst.camel_name + ); + + // Handle values. Note that we skip generating + // constructors for any instructions with variadic + // value lists. This is fine for the mid-end because + // in practice only calls and branches (for branch + // args) use this functionality, and neither can + // really be optimized or rewritten in the mid-end + // (currently). + // + // As a consequence, we only have to handle the + // one-`Value` case, in which the `Value` is directly + // in the `InstructionData`, and the multiple-`Value` + // case, in which the `Value`s are in a + // statically-sized array (e.g. `[Value; 2]` for a + // binary op). + assert!(!inst.format.has_value_list); + if inst.format.num_value_operands == 1 { + write!( + &mut s, + " {}", + inst.operands_in.iter().find(|o| o.is_value()).unwrap().name + ) + .unwrap(); + } else if inst.format.num_value_operands > 1 { + // As above, get all bindings together, and pass + // to a sub-term; here we use a constructor to + // build the value array. + let values = inst + .operands_in + .iter() + .filter(|o| o.is_value()) + .map(|o| o.name) + .collect::>(); + assert_eq!(values.len(), inst.format.num_value_operands); + let values = values.join(" "); + write!( + &mut s, + " (value_array_{}_ctor {})", + inst.format.num_value_operands, values + ) + .unwrap(); + } + + if inst.format.num_block_operands > 0 { + let blocks: Vec<_> = inst + .operands_in + .iter() + .filter(|o| o.kind.is_block()) + .map(|o| o.name) + .collect(); + if inst.format.num_block_operands == 1 { + write!(&mut s, " {}", blocks.first().unwrap(),).unwrap(); + } else { + write!( + &mut s, + " (block_array_{} {})", + inst.format.num_block_operands, + blocks.join(" ") + ) + .unwrap(); + } + } + + // Immediates (non-value args). + for o in inst + .operands_in + .iter() + .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block()) + { + write!(&mut s, " {}", o.name).unwrap(); + } + s.push_str("))"); + fmt.line(&s); + }); + fmt.line(")"); + } + + fmt.empty_line(); + } +} + +fn gen_opt_isle( + formats: &[Rc], + instructions: &AllInstructions, + fmt: &mut Formatter, +) { + gen_common_isle(formats, instructions, fmt, IsleTarget::Opt); +} + +fn gen_lower_isle( + formats: &[Rc], + instructions: &AllInstructions, + fmt: &mut Formatter, +) { + gen_common_isle(formats, instructions, fmt, IsleTarget::Lower); +} + +/// Generate an `enum` immediate in ISLE. +fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) { + variants.sort(); + let prefix = format!(";;;; Enumerated Immediate: {name} "); + fmtln!(fmt, "{:;<80}", prefix); + fmt.empty_line(); + fmtln!(fmt, "(type {} extern", name); + fmt.indent(|fmt| { + fmt.line("(enum"); + fmt.indent(|fmt| { + for variant in variants { + fmtln!(fmt, "{}", variant); + } + }); + fmt.line(")"); + }); + fmt.line(")"); + fmt.empty_line(); +} + +pub(crate) fn generate( + formats: &[Rc], + all_inst: &AllInstructions, + isle_opt_filename: &str, + isle_lower_filename: &str, + isle_dir: &std::path::Path, +) -> Result<(), error::Error> { + // ISLE DSL: mid-end ("opt") generated bindings. + let mut fmt = Formatter::new(); + gen_opt_isle(&formats, all_inst, &mut fmt); + fmt.update_file(isle_opt_filename, isle_dir)?; + + // ISLE DSL: lowering generated bindings. + let mut fmt = Formatter::new(); + gen_lower_isle(&formats, all_inst, &mut fmt); + fmt.update_file(isle_lower_filename, isle_dir)?; + + Ok(()) +} diff --git a/cranelift/codegen/meta/src/gen_settings.rs b/cranelift/codegen/meta/src/gen_settings.rs index 097581bef6d8..3d9a4e00cff5 100644 --- a/cranelift/codegen/meta/src/gen_settings.rs +++ b/cranelift/codegen/meta/src/gen_settings.rs @@ -172,7 +172,7 @@ fn gen_enum_types(group: &SettingGroup, fmt: &mut Formatter) { fmtln!(fmt, "pub enum {} {{", name); fmt.indent(|fmt| { for v in values.iter() { - fmt.doc_comment(format!("`{}`.", v)); + fmt.doc_comment(format!("`{v}`.")); fmtln!(fmt, "{},", camel_case(v)); } }); @@ -207,7 +207,7 @@ fn gen_getter(setting: &Setting, fmt: &mut Formatter) { fmt.indent(|fmt| { let mut m = Match::new(format!("self.bytes[{}]", setting.byte_offset)); for (i, v) in values.iter().enumerate() { - m.arm_no_fields(format!("{}", i), format!("{}::{}", ty, camel_case(v))); + m.arm_no_fields(format!("{i}"), format!("{}::{}", ty, camel_case(v))); } m.arm_no_fields("_", "panic!(\"Invalid enum value\")"); fmt.add_match(m); @@ -415,10 +415,7 @@ fn gen_template(group: &SettingGroup, fmt: &mut Formatter) { *default_bytes.get_mut(setting.byte_offset as usize).unwrap() |= setting.default_byte(); } - let default_bytes: Vec = default_bytes - .iter() - .map(|x| format!("{:#04x}", x)) - .collect(); + let default_bytes: Vec = default_bytes.iter().map(|x| format!("{x:#04x}")).collect(); let default_bytes_str = default_bytes.join(", "); fmtln!( @@ -499,7 +496,7 @@ pub(crate) fn generate( settings: &SettingGroup, parent_group: ParentGroup, filename: &str, - out_dir: &str, + out_dir: &std::path::Path, ) -> Result<(), error::Error> { let mut fmt = Formatter::new(); gen_group(settings, parent_group, &mut fmt); diff --git a/cranelift/codegen/meta/src/gen_types.rs b/cranelift/codegen/meta/src/gen_types.rs index 7c7cacda31b0..e0e66a3de9be 100644 --- a/cranelift/codegen/meta/src/gen_types.rs +++ b/cranelift/codegen/meta/src/gen_types.rs @@ -53,11 +53,6 @@ fn emit_types(fmt: &mut srcgen::Formatter) { emit_type(&ty, fmt); } - // Emit all reference types. - for ty in cdsl_types::ValueType::all_reference_types().map(cdsl_types::ValueType::from) { - emit_type(&ty, fmt); - } - // Emit vector definitions for common SIMD sizes. // Emit dynamic vector definitions. for vec_size in &[16_u64, 32, 64, 128, 256, 512] { @@ -67,7 +62,7 @@ fn emit_types(fmt: &mut srcgen::Formatter) { } /// Generate the types file. -pub(crate) fn generate(filename: &str, out_dir: &str) -> Result<(), error::Error> { +pub(crate) fn generate(filename: &str, out_dir: &std::path::Path) -> Result<(), error::Error> { let mut fmt = srcgen::Formatter::new(); emit_types(&mut fmt); fmt.update_file(filename, out_dir)?; diff --git a/cranelift/codegen/meta/src/isa/arm64.rs b/cranelift/codegen/meta/src/isa/arm64.rs index 34d82b8a5520..afad7d15468f 100644 --- a/cranelift/codegen/meta/src/isa/arm64.rs +++ b/cranelift/codegen/meta/src/isa/arm64.rs @@ -18,6 +18,12 @@ pub(crate) fn define() -> TargetIsa { "", false, ); + settings.add_bool( + "has_fp16", + "Use half-precision floating point (FEAT_FP16) instructions.", + "", + false, + ); settings.add_bool( "sign_return_address_all", "If function return address signing is enabled, then apply it to all \ diff --git a/cranelift/codegen/meta/src/isa/mod.rs b/cranelift/codegen/meta/src/isa/mod.rs index ecda9b83d054..655b14a9c5a1 100644 --- a/cranelift/codegen/meta/src/isa/mod.rs +++ b/cranelift/codegen/meta/src/isa/mod.rs @@ -3,6 +3,7 @@ use crate::cdsl::isa::TargetIsa; use std::fmt; mod arm64; +mod pulley; mod riscv64; mod s390x; pub(crate) mod x86; @@ -14,6 +15,8 @@ pub enum Isa { Arm64, S390x, Riscv64, + Pulley32, + Pulley64, } impl Isa { @@ -32,13 +35,22 @@ impl Isa { "s390x" => Some(Isa::S390x), x if ["x86_64", "i386", "i586", "i686"].contains(&x) => Some(Isa::X86), "riscv64" | "riscv64gc" | "riscv64imac" => Some(Isa::Riscv64), + "pulley32" => Some(Isa::Pulley32), + "pulley64" => Some(Isa::Pulley64), _ => None, } } /// Returns all supported isa targets. pub fn all() -> &'static [Isa] { - &[Isa::X86, Isa::Arm64, Isa::S390x, Isa::Riscv64] + &[ + Isa::X86, + Isa::Arm64, + Isa::S390x, + Isa::Riscv64, + Isa::Pulley32, + Isa::Pulley64, + ] } } @@ -50,6 +62,8 @@ impl fmt::Display for Isa { Isa::Arm64 => write!(f, "arm64"), Isa::S390x => write!(f, "s390x"), Isa::Riscv64 => write!(f, "riscv64"), + Isa::Pulley32 => write!(f, "pulley32"), + Isa::Pulley64 => write!(f, "pulley64"), } } } @@ -61,6 +75,7 @@ pub(crate) fn define(isas: &[Isa]) -> Vec { Isa::Arm64 => arm64::define(), Isa::S390x => s390x::define(), Isa::Riscv64 => riscv64::define(), + Isa::Pulley32 | Isa::Pulley64 => pulley::define(), }) .collect() } diff --git a/cranelift/codegen/meta/src/isa/pulley.rs b/cranelift/codegen/meta/src/isa/pulley.rs new file mode 100644 index 000000000000..7ae14ae682a2 --- /dev/null +++ b/cranelift/codegen/meta/src/isa/pulley.rs @@ -0,0 +1,14 @@ +use crate::cdsl::{isa::TargetIsa, settings::SettingGroupBuilder}; + +pub(crate) fn define() -> TargetIsa { + let mut settings = SettingGroupBuilder::new("pulley"); + settings.add_enum( + "pointer_width", + "The width of pointers for this Pulley target", + "Supported values:\n\ + * 'pointer32'\n\ + * 'pointer64'\n", + vec!["pointer32", "pointer64"], + ); + TargetIsa::new("pulley", settings.build()) +} diff --git a/cranelift/codegen/meta/src/isa/riscv64.rs b/cranelift/codegen/meta/src/isa/riscv64.rs index 9a24efd916c4..355a9125abfb 100644 --- a/cranelift/codegen/meta/src/isa/riscv64.rs +++ b/cranelift/codegen/meta/src/isa/riscv64.rs @@ -56,6 +56,21 @@ pub(crate) fn define() -> TargetIsa { "Double-precision floating point", true, ); + + let _has_zfa = setting.add_bool( + "has_zfa", + "has extension Zfa?", + "Zfa: Extension for Additional Floating-Point Instructions", + false, + ); + + let _has_zfh = setting.add_bool( + "has_zfh", + "has extension Zfh?", + "Zfh: Half-Precision Floating-Point Instructions", + false, + ); + let _has_v = setting.add_bool( "has_v", "has extension V?", @@ -118,6 +133,12 @@ pub(crate) fn define() -> TargetIsa { "Zbs: Single-bit instructions", false, ); + let _has_zicond = setting.add_bool( + "has_zicond", + "has extension zicond?", + "ZiCond: Integer Conditional Operations", + false, + ); let has_zicsr = setting.add_bool( "has_zicsr", @@ -133,7 +154,7 @@ pub(crate) fn define() -> TargetIsa { ); // Zvl*: Minimum Vector Length Standard Extensions - // These extension specifiy the minimum number of bits in a vector register. + // These extension specify the minimum number of bits in a vector register. // Since it is a minimum, Zvl64b implies Zvl32b, Zvl128b implies Zvl64b, etc. // The V extension supports a maximum of 64K bits in a single register. // diff --git a/cranelift/codegen/meta/src/isa/x86.rs b/cranelift/codegen/meta/src/isa/x86.rs index 5b59f87a5e64..18f80b067394 100644 --- a/cranelift/codegen/meta/src/isa/x86.rs +++ b/cranelift/codegen/meta/src/isa/x86.rs @@ -284,7 +284,7 @@ pub(crate) fn define() -> TargetIsa { ); let sapphire_rapids = settings.add_preset( "sapphirerapids", - "Saphire Rapids microarchitecture.", + "Sapphire Rapids microarchitecture.", preset!(icelake_server), ); settings.add_preset( @@ -373,11 +373,23 @@ pub(crate) fn define() -> TargetIsa { "Zen (second generation) microarchitecture.", preset!(znver1), ); - settings.add_preset( + let znver3 = settings.add_preset( "znver3", "Zen (third generation) microarchitecture.", preset!(znver2), ); + settings.add_preset( + "znver4", + "Zen (fourth generation) microarchitecture.", + preset!( + znver3 + && has_avx512bitalg + && has_avx512dq + && has_avx512f + && has_avx512vbmi + && has_avx512vl + ), + ); // Generic diff --git a/cranelift/codegen/meta/src/isle.rs b/cranelift/codegen/meta/src/isle.rs new file mode 100644 index 000000000000..84706f4a9401 --- /dev/null +++ b/cranelift/codegen/meta/src/isle.rs @@ -0,0 +1,230 @@ +use std::io::Result; + +/// A list of compilations (transformations from ISLE source to +/// generated Rust source) that exist in the repository. +/// +/// This list is used either to regenerate the Rust source in-tree (if +/// the `rebuild-isle` feature is enabled), or to verify that the ISLE +/// source in-tree corresponds to the ISLE source that was last used +/// to rebuild the Rust source (if the `rebuild-isle` feature is not +/// enabled). +#[derive(Clone, Debug)] +pub struct IsleCompilations { + pub items: Vec, +} + +impl IsleCompilations { + pub fn lookup(&self, name: &str) -> Option<&IsleCompilation> { + for compilation in &self.items { + if compilation.name == name { + return Some(compilation); + } + } + None + } +} + +#[derive(Clone, Debug)] +pub struct IsleCompilation { + pub name: String, + pub output: std::path::PathBuf, + pub tracked_inputs: Vec, + pub untracked_inputs: Vec, + pub rule_trace: bool, +} + +impl IsleCompilation { + /// All inputs to the computation, tracked or untracked. May contain directories. + pub fn inputs(&self) -> Vec { + self.tracked_inputs + .iter() + .chain(self.untracked_inputs.iter()) + .cloned() + .collect() + } + + /// All path inputs to the compilation. Directory inputs are expanded to the + /// list of all ISLE files in the directory. + pub fn paths(&self) -> Result> { + let mut paths = Vec::new(); + for input in self.inputs() { + paths.extend(Self::expand_paths(&input)?); + } + Ok(paths) + } + + fn expand_paths(input: &std::path::PathBuf) -> Result> { + if input.is_file() { + return Ok(vec![input.clone()]); + } + + let mut paths = Vec::new(); + for entry in std::fs::read_dir(input)? { + let path = entry?.path(); + if let Some(ext) = path.extension() { + if ext == "isle" { + paths.push(path); + } + } + } + Ok(paths) + } +} + +/// Construct the list of compilations (transformations from ISLE +/// source to generated Rust source) that exist in the repository. +pub fn get_isle_compilations( + codegen_crate_dir: &std::path::Path, + gen_dir: &std::path::Path, +) -> IsleCompilations { + // Preludes. + let clif_lower_isle = gen_dir.join("clif_lower.isle"); + let clif_opt_isle = gen_dir.join("clif_opt.isle"); + let prelude_isle = codegen_crate_dir.join("src").join("prelude.isle"); + let prelude_opt_isle = codegen_crate_dir.join("src").join("prelude_opt.isle"); + let prelude_lower_isle = codegen_crate_dir.join("src").join("prelude_lower.isle"); + let prelude_spec_isle = codegen_crate_dir.join("src").join("prelude_spec.isle"); + let inst_specs_isle = codegen_crate_dir.join("src").join("inst_specs.isle"); + let inst_tags_isle = codegen_crate_dir.join("src").join("inst_tags.isle"); + let fpconst_isle = codegen_crate_dir.join("src").join("fpconst.isle"); + let state_isle = codegen_crate_dir.join("src").join("state.isle"); + + // Directory for mid-end optimizations. + let src_opts = codegen_crate_dir.join("src").join("opts"); + + // Directories for lowering backends. + let src_isa_x64 = codegen_crate_dir.join("src").join("isa").join("x64"); + let src_isa_aarch64 = codegen_crate_dir.join("src").join("isa").join("aarch64"); + let src_isa_s390x = codegen_crate_dir.join("src").join("isa").join("s390x"); + let src_isa_risc_v = codegen_crate_dir.join("src").join("isa").join("riscv64"); + let src_isa_pulley_shared = codegen_crate_dir + .join("src") + .join("isa") + .join("pulley_shared"); + + // This is a set of ISLE compilation units. + // + // The format of each entry is: + // + // (output Rust code file, input ISLE source files) + // + // There should be one entry for each backend that uses ISLE for lowering, + // and if/when we replace our peephole optimization passes with ISLE, there + // should be an entry for each of those as well. + // + // N.B.: add any new compilation outputs to + // `scripts/force-rebuild-isle.sh` if they do not fit the pattern + // `cranelift/codegen/src/isa/*/lower/isle/generated_code.rs`! + IsleCompilations { + items: vec![ + // The mid-end optimization rules. + IsleCompilation { + name: "opt".to_string(), + output: gen_dir.join("isle_opt.rs"), + tracked_inputs: vec![ + prelude_isle.clone(), + prelude_opt_isle, + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), + src_opts.join("arithmetic.isle"), + src_opts.join("bitops.isle"), + src_opts.join("cprop.isle"), + src_opts.join("extends.isle"), + src_opts.join("icmp.isle"), + src_opts.join("remat.isle"), + src_opts.join("selects.isle"), + src_opts.join("shifts.isle"), + src_opts.join("spaceship.isle"), + src_opts.join("spectre.isle"), + src_opts.join("vector.isle"), + ], + untracked_inputs: vec![clif_opt_isle], + rule_trace: false, + }, + // The x86-64 instruction selector. + IsleCompilation { + name: "x64".to_string(), + output: gen_dir.join("isle_x64.rs"), + tracked_inputs: vec![ + prelude_isle.clone(), + prelude_lower_isle.clone(), + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), + src_isa_x64.join("inst.isle"), + src_isa_x64.join("lower.isle"), + ], + untracked_inputs: vec![clif_lower_isle.clone()], + rule_trace: false, + }, + // The aarch64 instruction selector. + IsleCompilation { + name: "aarch64".to_string(), + output: gen_dir.join("isle_aarch64.rs"), + tracked_inputs: vec![ + prelude_isle.clone(), + prelude_lower_isle.clone(), + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), + fpconst_isle.clone(), + state_isle.clone(), + src_isa_aarch64.join("inst.isle"), + src_isa_aarch64.join("inst_neon.isle"), + src_isa_aarch64.join("spec"), + src_isa_aarch64.join("lower.isle"), + src_isa_aarch64.join("lower_dynamic_neon.isle"), + ], + untracked_inputs: vec![clif_lower_isle.clone()], + rule_trace: true, + }, + // The s390x instruction selector. + IsleCompilation { + name: "s390x".to_string(), + output: gen_dir.join("isle_s390x.rs"), + tracked_inputs: vec![ + prelude_isle.clone(), + prelude_lower_isle.clone(), + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), + src_isa_s390x.join("inst.isle"), + src_isa_s390x.join("lower.isle"), + ], + untracked_inputs: vec![clif_lower_isle.clone()], + rule_trace: false, + }, + // The risc-v instruction selector. + IsleCompilation { + name: "riscv64".to_string(), + output: gen_dir.join("isle_riscv64.rs"), + tracked_inputs: vec![ + prelude_isle.clone(), + prelude_lower_isle.clone(), + prelude_spec_isle.clone(), + inst_specs_isle.clone(), + inst_tags_isle.clone(), + src_isa_risc_v.join("inst.isle"), + src_isa_risc_v.join("inst_vector.isle"), + src_isa_risc_v.join("lower.isle"), + ], + untracked_inputs: vec![clif_lower_isle.clone()], + rule_trace: false, + }, + // The Pulley instruction selector. + IsleCompilation { + name: "pulley".to_string(), + output: gen_dir.join("isle_pulley_shared.rs"), + tracked_inputs: vec![ + prelude_isle.clone(), + prelude_lower_isle.clone(), + src_isa_pulley_shared.join("inst.isle"), + src_isa_pulley_shared.join("lower.isle"), + ], + untracked_inputs: vec![clif_lower_isle.clone()], + rule_trace: false, + }, + ], + } +} diff --git a/cranelift/codegen/meta/src/lib.rs b/cranelift/codegen/meta/src/lib.rs index 689d3508b06c..480719d045d1 100644 --- a/cranelift/codegen/meta/src/lib.rs +++ b/cranelift/codegen/meta/src/lib.rs @@ -1,14 +1,18 @@ //! This crate generates Rust sources for use by //! [`cranelift_codegen`](../cranelift_codegen/index.html). +use shared::Definitions; + #[macro_use] mod cdsl; mod srcgen; pub mod error; pub mod isa; +pub mod isle; mod gen_inst; +mod gen_isle; mod gen_settings; mod gen_types; @@ -18,20 +22,27 @@ mod unique_table; /// Generate an ISA from an architecture string (e.g. "x86_64"). pub fn isa_from_arch(arch: &str) -> Result { - isa::Isa::from_arch(arch).ok_or_else(|| format!("no supported isa found for arch `{}`", arch)) + isa::Isa::from_arch(arch).ok_or_else(|| format!("no supported isa found for arch `{arch}`")) } /// Generates all the Rust source files used in Cranelift from the meta-language. -pub fn generate(isas: &[isa::Isa], out_dir: &str, isle_dir: &str) -> Result<(), error::Error> { - // Common definitions. +pub fn generate_rust(isas: &[isa::Isa], out_dir: &std::path::Path) -> Result<(), error::Error> { let shared_defs = shared::define(); + generate_rust_for_shared_defs(&shared_defs, isas, out_dir) +} +fn generate_rust_for_shared_defs( + shared_defs: &Definitions, + isas: &[isa::Isa], + out_dir: &std::path::Path, +) -> Result<(), error::Error> { gen_settings::generate( &shared_defs.settings, gen_settings::ParentGroup::None, "settings.rs", out_dir, )?; + gen_types::generate("types.rs", out_dir)?; gen_inst::generate( @@ -39,10 +50,7 @@ pub fn generate(isas: &[isa::Isa], out_dir: &str, isle_dir: &str) -> Result<(), &shared_defs.all_instructions, "opcodes.rs", "inst_builder.rs", - "clif_opt.isle", - "clif_lower.isle", out_dir, - isle_dir, )?; // Per ISA definitions. @@ -57,3 +65,34 @@ pub fn generate(isas: &[isa::Isa], out_dir: &str, isle_dir: &str) -> Result<(), Ok(()) } + +/// Generates all the ISLE source files used in Cranelift from the meta-language. +pub fn generate_isle(isle_dir: &std::path::Path) -> Result<(), error::Error> { + let shared_defs = shared::define(); + generate_isle_for_shared_defs(&shared_defs, isle_dir) +} + +fn generate_isle_for_shared_defs( + shared_defs: &Definitions, + isle_dir: &std::path::Path, +) -> Result<(), error::Error> { + gen_isle::generate( + &shared_defs.all_formats, + &shared_defs.all_instructions, + "clif_opt.isle", + "clif_lower.isle", + isle_dir, + ) +} + +/// Generates all the source files used in Cranelift from the meta-language. +pub fn generate( + isas: &[isa::Isa], + out_dir: &std::path::Path, + isle_dir: &std::path::Path, +) -> Result<(), error::Error> { + let shared_defs = shared::define(); + generate_rust_for_shared_defs(&shared_defs, isas, out_dir)?; + generate_isle_for_shared_defs(&shared_defs, isle_dir)?; + Ok(()) +} diff --git a/cranelift/codegen/meta/src/shared/entities.rs b/cranelift/codegen/meta/src/shared/entities.rs index 374e61f4167b..3df9ecaa171f 100644 --- a/cranelift/codegen/meta/src/shared/entities.rs +++ b/cranelift/codegen/meta/src/shared/entities.rs @@ -43,9 +43,6 @@ pub(crate) struct EntityRefs { /// A reference to a jump table declared in the function preamble. pub(crate) jump_table: OperandKind, - /// A reference to a table declared in the function preamble. - pub(crate) table: OperandKind, - /// A variable-sized list of value operands. Use for Block and function call arguments. pub(crate) varargs: OperandKind, } @@ -87,8 +84,6 @@ impl EntityRefs { jump_table: new("table", "ir::JumpTable", "A jump table."), - table: new("table", "ir::Table", "A table."), - varargs: OperandKind::new( "", "&[Value]", diff --git a/cranelift/codegen/meta/src/shared/formats.rs b/cranelift/codegen/meta/src/shared/formats.rs index 35ea2e7f8ff4..86d54338d63d 100644 --- a/cranelift/codegen/meta/src/shared/formats.rs +++ b/cranelift/codegen/meta/src/shared/formats.rs @@ -30,13 +30,13 @@ pub(crate) struct Formats { pub(crate) dynamic_stack_store: Rc, pub(crate) store: Rc, pub(crate) store_no_offset: Rc, - pub(crate) table_addr: Rc, pub(crate) ternary: Rc, pub(crate) ternary_imm8: Rc, pub(crate) trap: Rc, pub(crate) unary: Rc, pub(crate) unary_const: Rc, pub(crate) unary_global_value: Rc, + pub(crate) unary_ieee16: Rc, pub(crate) unary_ieee32: Rc, pub(crate) unary_ieee64: Rc, pub(crate) unary_imm: Rc, @@ -49,6 +49,8 @@ impl Formats { unary_imm: Builder::new("UnaryImm").imm(&imm.imm64).build(), + unary_ieee16: Builder::new("UnaryIeee16").imm(&imm.ieee16).build(), + unary_ieee32: Builder::new("UnaryIeee32").imm(&imm.ieee32).build(), unary_ieee64: Builder::new("UnaryIeee64").imm(&imm.ieee64).build(), @@ -192,13 +194,6 @@ impl Formats { .imm(&entities.dynamic_stack_slot) .build(), - // Accessing a WebAssembly table. - table_addr: Builder::new("TableAddr") - .imm(&entities.table) - .value() - .imm(&imm.offset32) - .build(), - trap: Builder::new("Trap").imm(&imm.trapcode).build(), cond_trap: Builder::new("CondTrap").value().imm(&imm.trapcode).build(), diff --git a/cranelift/codegen/meta/src/shared/immediates.rs b/cranelift/codegen/meta/src/shared/immediates.rs index 9f908c93da48..8e30c6b5f87d 100644 --- a/cranelift/codegen/meta/src/shared/immediates.rs +++ b/cranelift/codegen/meta/src/shared/immediates.rs @@ -31,6 +31,11 @@ pub(crate) struct Immediates { /// This is used to represent an immediate address offset in load/store instructions. pub offset32: OperandKind, + /// A 16-bit immediate floating point operand. + /// + /// IEEE 754-2008 binary16 interchange format. + pub ieee16: OperandKind, + /// A 32-bit immediate floating point operand. /// /// IEEE 754-2008 binary32 interchange format. @@ -119,6 +124,11 @@ impl Immediates { "ir::immediates::Offset32", "A 32-bit immediate signed offset.", ), + ieee16: new_imm( + "imm", + "ir::immediates::Ieee16", + "A 16-bit immediate floating point number.", + ), ieee32: new_imm( "imm", "ir::immediates::Ieee32", @@ -181,6 +191,7 @@ impl Immediates { trapcode_values.insert("heap_oob", "HeapOutOfBounds"); trapcode_values.insert("int_ovf", "IntegerOverflow"); trapcode_values.insert("int_divz", "IntegerDivisionByZero"); + trapcode_values.insert("bad_toint", "BadConversionToInteger"); new_enum( "code", "ir::TrapCode", diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs index 6727d9c8c345..4348a7b7168c 100644 --- a/cranelift/codegen/meta/src/shared/instructions.rs +++ b/cranelift/codegen/meta/src/shared/instructions.rs @@ -98,7 +98,7 @@ fn define_control_flow( let iAddr = &TypeVar::new( "iAddr", "An integer address type", - TypeSetBuilder::new().ints(32..64).refs(32..64).build(), + TypeSetBuilder::new().ints(32..64).build(), ); ig.push( @@ -144,20 +144,6 @@ fn define_control_flow( .can_trap(), ); - ig.push( - Inst::new( - "resumable_trap", - r#" - A resumable trap. - - This instruction allows non-conditional traps to be used as non-terminal instructions. - "#, - &formats.trap, - ) - .operands_in(vec![Operand::new("code", &imm.trapcode)]) - .can_trap(), - ); - ig.push( Inst::new( "trapnz", @@ -175,23 +161,6 @@ fn define_control_flow( .can_trap(), ); - ig.push( - Inst::new( - "resumable_trapnz", - r#" - A resumable trap to be called when the passed condition is non-zero. - - If ``c`` is zero, execution continues at the following instruction. - "#, - &formats.cond_trap, - ) - .operands_in(vec![ - Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"), - Operand::new("code", &imm.trapcode), - ]) - .can_trap(), - ); - ig.push( Inst::new( "return", @@ -630,8 +599,10 @@ pub(crate) fn define( // Operand kind shorthands. let i8: &TypeVar = &ValueType::from(LaneType::from(types::Int::I8)).into(); + let f16_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F16)).into(); let f32_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F32)).into(); let f64_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F64)).into(); + let f128_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F128)).into(); // Starting definitions. let Int = &TypeVar::new( @@ -671,13 +642,7 @@ pub(crate) fn define( let iAddr = &TypeVar::new( "iAddr", "An integer address type", - TypeSetBuilder::new().ints(32..64).refs(32..64).build(), - ); - - let Ref = &TypeVar::new( - "Ref", - "A scalar reference type", - TypeSetBuilder::new().refs(Interval::All).build(), + TypeSetBuilder::new().ints(32..64).build(), ); let TxN = &TypeVar::new( @@ -696,7 +661,6 @@ pub(crate) fn define( TypeSetBuilder::new() .ints(Interval::All) .floats(Interval::All) - .refs(Interval::All) .simd_lanes(Interval::All) .includes_scalars(true) .build(), @@ -709,7 +673,6 @@ pub(crate) fn define( .ints(Interval::All) .floats(Interval::All) .simd_lanes(Interval::All) - .refs(Interval::All) .dynamic_simd_lanes(Interval::All) .build(), ); @@ -944,6 +907,66 @@ pub(crate) fn define( ]) .can_store(), ); + ig.push( + Inst::new( + "stack_switch", + r#" + Suspends execution of the current stack and resumes execution of another + one. + + The target stack to switch to is identified by the data stored at + ``load_context_ptr``. Before switching, this instruction stores + analogous information about the + current (i.e., original) stack at ``store_context_ptr``, to + enabled switching back to the original stack at a later point. + + The size, alignment and layout of the information stored at + ``load_context_ptr`` and ``store_context_ptr`` is platform-dependent. + The instruction assumes that ``load_context_ptr`` and + ``store_context_ptr`` are valid pointers to memory with said layout and + alignment, and does not perform any checks on these pointers or the data + stored there. + + The instruction is experimental and only supported on x64 Linux at the + moment. + + When switching from a stack A to a stack B, one of the following cases + must apply: + 1. Stack B was previously suspended using a ``stack_switch`` instruction. + 2. Stack B is a newly initialized stack. The necessary initialization is + platform-dependent and will generally involve running some kind of + trampoline to start execution of a function on the new stack. + + In both cases, the ``in_payload`` argument of the ``stack_switch`` + instruction executed on A is passed to stack B. In the first case above, + it will be the result value of the earlier ``stack_switch`` instruction + executed on stack B. In the second case, the value will be accessible to + the trampoline in a platform-dependent register. + + The pointers ``load_context_ptr`` and ``store_context_ptr`` are allowed + to be equal; the instruction ensures that all data is loaded from the + former before writing to the latter. + + Stack switching is one-shot in the sense that each ``stack_switch`` + operation effectively consumes the context identified by + ``load_context_ptr``. In other words, performing two ``stack_switches`` + using the same ``load_context_ptr`` causes undefined behavior, unless + the context at ``load_context_ptr`` is overwritten by another + `stack_switch` in between. + "#, + &formats.ternary, + ) + .operands_in(vec![ + Operand::new("store_context_ptr", iAddr), + Operand::new("load_context_ptr", iAddr), + Operand::new("in_payload0", iAddr), + ]) + .operands_out(vec![Operand::new("out_payload0", iAddr)]) + .other_side_effects() + .can_load() + .can_store() + .call(), + ); let I16x8 = &TypeVar::new( "I16x8", @@ -1299,52 +1322,36 @@ pub(crate) fn define( .operands_out(vec![Operand::new("addr", iAddr)]), ); - let TableOffset = &TypeVar::new( - "TableOffset", - "An unsigned table offset", - TypeSetBuilder::new().ints(32..64).build(), - ); - ig.push( Inst::new( - "table_addr", + "iconst", r#" - Bounds check and compute absolute address of a table entry. - - Verify that the offset ``p`` is in bounds for the table T, and generate - an absolute address that is safe to dereference. - - ``Offset`` must be less than the size of a table element. + Integer constant. - 1. If ``p`` is not greater than the table bound, return an absolute - address corresponding to a byte offset of ``p`` from the table's - base address. - 2. If ``p`` is greater than the table bound, generate a trap. + Create a scalar integer SSA value with an immediate constant value, or + an integer vector where all the lanes have the same value. "#, - &formats.table_addr, + &formats.unary_imm, ) - .operands_in(vec![ - Operand::new("T", &entities.table), - Operand::new("p", TableOffset), - Operand::new("Offset", &imm.offset32).with_doc("Byte offset from element address"), - ]) - .operands_out(vec![Operand::new("addr", iAddr)]), + .operands_in(vec![Operand::new("N", &imm.imm64)]) + .operands_out(vec![ + Operand::new("a", NarrowInt).with_doc("A constant integer scalar or vector value") + ]), ); ig.push( Inst::new( - "iconst", + "f16const", r#" - Integer constant. + Floating point constant. - Create a scalar integer SSA value with an immediate constant value, or - an integer vector where all the lanes have the same value. + Create a `f16` SSA value with an immediate constant value. "#, - &formats.unary_imm, + &formats.unary_ieee16, ) - .operands_in(vec![Operand::new("N", &imm.imm64)]) + .operands_in(vec![Operand::new("N", &imm.ieee16)]) .operands_out(vec![ - Operand::new("a", NarrowInt).with_doc("A constant integer scalar or vector value") + Operand::new("a", f16_).with_doc("A constant f16 scalar value") ]), ); @@ -1380,6 +1387,22 @@ pub(crate) fn define( ]), ); + ig.push( + Inst::new( + "f128const", + r#" + Floating point constant. + + Create a `f128` SSA value with an immediate constant value. + "#, + &formats.unary_const, + ) + .operands_in(vec![Operand::new("N", &imm.pool_constant)]) + .operands_out(vec![ + Operand::new("a", f128_).with_doc("A constant f128 scalar value") + ]), + ); + ig.push( Inst::new( "vconst", @@ -1430,21 +1453,6 @@ pub(crate) fn define( .operands_out(vec![Operand::new("a", Tx16).with_doc("A vector value")]), ); - ig.push( - Inst::new( - "null", - r#" - Null constant value for reference types. - - Create a scalar reference SSA value with a constant null value. - "#, - &formats.nullary, - ) - .operands_out(vec![ - Operand::new("a", Ref).with_doc("A constant reference null value") - ]), - ); - ig.push(Inst::new( "nop", r#" @@ -1481,19 +1489,36 @@ pub(crate) fn define( Conditional select intended for Spectre guards. This operation is semantically equivalent to a select instruction. - However, it is guaranteed to not be removed or otherwise altered by any - optimization pass, and is guaranteed to result in a conditional-move - instruction, not a branch-based lowering. As such, it is suitable - for use when producing Spectre guards. For example, a bounds-check - may guard against unsafe speculation past a bounds-check conditional - branch by passing the address or index to be accessed through a - conditional move, also gated on the same condition. Because no - Spectre-vulnerable processors are known to perform speculation on - conditional move instructions, this is guaranteed to pick the - correct input. If the selected input in case of overflow is a "safe" - value, for example a null pointer that causes an exception in the - speculative path, this ensures that no Spectre vulnerability will - exist. + However, this instruction prohibits all speculation on the + controlling value when determining which input to use as the result. + As such, it is suitable for use in Spectre guards. + + For example, on a target which may speculatively execute branches, + the lowering of this instruction is guaranteed to not conditionally + branch. Instead it will typically lower to a conditional move + instruction. (No Spectre-vulnerable processors are known to perform + value speculation on conditional move instructions.) + + Ensure that the instruction you're trying to protect from Spectre + attacks has a data dependency on the result of this instruction. + That prevents an out-of-order CPU from evaluating that instruction + until the result of this one is known, which in turn will be blocked + until the controlling value is known. + + Typical usage is to use a bounds-check as the controlling value, + and select between either a null pointer if the bounds-check + fails, or an in-bounds address otherwise, so that dereferencing + the resulting address with a load or store instruction will trap if + the bounds-check failed. When this instruction is used in this way, + any microarchitectural side effects of the memory access will only + occur after the bounds-check finishes, which ensures that no Spectre + vulnerability will exist. + + Optimization opportunities for this instruction are limited compared + to a normal select instruction, but it is allowed to be replaced + by other values which are functionally equivalent as long as doing + so does not introduce any new opportunities to speculate on the + controlling value. "#, &formats.ternary, ) @@ -1502,11 +1527,7 @@ pub(crate) fn define( Operand::new("x", Any).with_doc("Value to use when `c` is true"), Operand::new("y", Any).with_doc("Value to use when `c` is false"), ]) - .operands_out(vec![Operand::new("a", Any)]) - .other_side_effects() - // We can de-duplicate spectre selects since the side effect is - // idempotent. - .side_effects_idempotent(), + .operands_out(vec![Operand::new("a", Any)]), ); ig.push( @@ -3006,36 +3027,6 @@ pub(crate) fn define( ]), ); - ig.push( - Inst::new( - "is_null", - r#" - Reference verification. - - The condition code determines if the reference type in question is - null or not. - "#, - &formats.unary, - ) - .operands_in(vec![Operand::new("x", Ref)]) - .operands_out(vec![Operand::new("a", i8)]), - ); - - ig.push( - Inst::new( - "is_invalid", - r#" - Reference verification. - - The condition code determines if the reference type in question is - invalid or not. - "#, - &formats.unary, - ) - .operands_in(vec![Operand::new("x", Ref)]) - .operands_out(vec![Operand::new("a", i8)]), - ); - ig.push( Inst::new( "bitcast", diff --git a/cranelift/codegen/meta/src/shared/mod.rs b/cranelift/codegen/meta/src/shared/mod.rs index 4c1f7763071d..77b248ec55f9 100644 --- a/cranelift/codegen/meta/src/shared/mod.rs +++ b/cranelift/codegen/meta/src/shared/mod.rs @@ -16,7 +16,6 @@ use crate::shared::formats::Formats; use crate::shared::immediates::Immediates; use std::collections::HashMap; -use std::iter::FromIterator; use std::rc::Rc; pub(crate) struct Definitions { @@ -44,9 +43,9 @@ pub(crate) fn define() -> Definitions { /// Verifies certain properties of formats. /// /// - Formats must be uniquely named: if two formats have the same name, they must refer to the -/// same data. Otherwise, two format variants in the codegen crate would have the same name. +/// same data. Otherwise, two format variants in the codegen crate would have the same name. /// - Formats must be structurally different from each other. Otherwise, this would lead to -/// code duplicate in the codegen crate. +/// code duplicate in the codegen crate. /// /// Returns a list of all the instruction formats effectively used. fn verify_instruction_formats(all_instructions: &AllInstructions) -> Vec> { diff --git a/cranelift/codegen/meta/src/shared/settings.rs b/cranelift/codegen/meta/src/shared/settings.rs index e4e511b5e8cb..b6aa2c02d2ab 100644 --- a/cranelift/codegen/meta/src/shared/settings.rs +++ b/cranelift/codegen/meta/src/shared/settings.rs @@ -63,6 +63,20 @@ 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 // `colocated` flag on external functions and global values. @@ -140,6 +154,19 @@ pub(crate) fn define() -> SettingGroup { vec!["none", "elf_gd", "macho", "coff"], ); + settings.add_enum( + "stack_switch_model", + "Defines the model used to performing stack switching.", + r#" + This determines the compilation of `stack_switch` instructions. If + set to `basic`, we simply save all registers, update stack pointer + and frame pointer (if needed), and jump to the target IP. + If set to `update_windows_tib`, we *additionally* update information + about the active stack in Windows' Thread Information Block. + "#, + vec!["none", "basic", "update_windows_tib"], + ); + settings.add_enum( "libcall_call_conv", "Defines the calling convention to use for LibCalls call expansion.", @@ -230,13 +257,6 @@ pub(crate) fn define() -> SettingGroup { false, ); - settings.add_bool( - "probestack_func_adjusts_sp", - "Enable if the stack probe adjusts the stack pointer.", - "", - false, - ); - settings.add_num( "probestack_size_log2", "The log2 of the size of the stack guard region.", diff --git a/cranelift/codegen/meta/src/shared/types.rs b/cranelift/codegen/meta/src/shared/types.rs index 33efd108014b..d09abe179dde 100644 --- a/cranelift/codegen/meta/src/shared/types.rs +++ b/cranelift/codegen/meta/src/shared/types.rs @@ -43,8 +43,10 @@ impl Iterator for IntIterator { #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub(crate) enum Float { + F16 = 16, F32 = 32, F64 = 64, + F128 = 128, } /// Iterator through the variants of the Float enum. @@ -63,40 +65,10 @@ impl Iterator for FloatIterator { type Item = Float; fn next(&mut self) -> Option { let res = match self.index { - 0 => Some(Float::F32), - 1 => Some(Float::F64), - _ => return None, - }; - self.index += 1; - res - } -} - -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub(crate) enum Reference { - /// 32-bit reference. - R32 = 32, - /// 64-bit reference. - R64 = 64, -} - -/// This provides an iterator through all of the supported reference variants. -pub(crate) struct ReferenceIterator { - index: u8, -} - -impl ReferenceIterator { - pub fn new() -> Self { - Self { index: 0 } - } -} - -impl Iterator for ReferenceIterator { - type Item = Reference; - fn next(&mut self) -> Option { - let res = match self.index { - 0 => Some(Reference::R32), - 1 => Some(Reference::R64), + 0 => Some(Float::F16), + 1 => Some(Float::F32), + 2 => Some(Float::F64), + 3 => Some(Float::F128), _ => return None, }; self.index += 1; @@ -122,16 +94,10 @@ mod iter_tests { #[test] fn float_iter_works() { let mut float_iter = FloatIterator::new(); + assert_eq!(float_iter.next(), Some(Float::F16)); assert_eq!(float_iter.next(), Some(Float::F32)); assert_eq!(float_iter.next(), Some(Float::F64)); + assert_eq!(float_iter.next(), Some(Float::F128)); assert_eq!(float_iter.next(), None); } - - #[test] - fn reference_iter_works() { - let mut reference_iter = ReferenceIterator::new(); - assert_eq!(reference_iter.next(), Some(Reference::R32)); - assert_eq!(reference_iter.next(), Some(Reference::R64)); - assert_eq!(reference_iter.next(), None); - } } diff --git a/cranelift/codegen/meta/src/srcgen.rs b/cranelift/codegen/meta/src/srcgen.rs index b1be70240746..d3c321e5bc51 100644 --- a/cranelift/codegen/meta/src/srcgen.rs +++ b/cranelift/codegen/meta/src/srcgen.rs @@ -9,7 +9,6 @@ use std::cmp; use std::collections::{BTreeMap, BTreeSet}; use std::fs; use std::io::Write; -use std::path; use crate::error; @@ -91,15 +90,10 @@ impl Formatter { /// Write `self.lines` to a file. pub fn update_file( &self, - filename: impl AsRef, - directory: &str, + filename: impl AsRef, + directory: &std::path::Path, ) -> Result<(), error::Error> { - #[cfg(target_family = "windows")] - let path_str = format!("{}\\{}", directory, filename.as_ref()); - #[cfg(not(target_family = "windows"))] - let path_str = format!("{}/{}", directory, filename.as_ref()); - - let path = path::Path::new(&path_str); + let path = directory.join(&filename); eprintln!("Writing generated file: {}", path.display()); let mut f = fs::File::create(path)?; @@ -128,7 +122,7 @@ impl Formatter { if l.is_empty() { "///".into() } else { - format!("/// {}", l) + format!("/// {l}") } }) .for_each(|s| self.line(s.as_str())); @@ -314,7 +308,7 @@ mod srcgen_tests { .trim() .split("\n") .into_iter() - .map(|x| format!("{}\n", x)) + .map(|x| format!("{x}\n")) .collect() } diff --git a/cranelift/codegen/shared/Cargo.toml b/cranelift/codegen/shared/Cargo.toml index 3575420fa117..7628ba46abb1 100644 --- a/cranelift/codegen/shared/Cargo.toml +++ b/cranelift/codegen/shared/Cargo.toml @@ -1,12 +1,13 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-codegen-shared" -version = "0.101.0" +version = "0.112.0" description = "For code shared between cranelift-codegen-meta and cranelift-codegen" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" readme = "README.md" edition.workspace = true +rust-version.workspace = true [dependencies] # Since this is a shared dependency of several packages, please strive to keep this dependency-free diff --git a/cranelift/codegen/shared/src/constants.rs b/cranelift/codegen/shared/src/constants.rs index d9aa3916ca7f..4fa5897dca96 100644 --- a/cranelift/codegen/shared/src/constants.rs +++ b/cranelift/codegen/shared/src/constants.rs @@ -13,7 +13,7 @@ // Vector types are encoded with the lane type in the low 4 bits and log2(lanes) // in the next highest 4 bits, giving a range of 2-256 lanes. -// Dynamic vector types are encoded similarily. +// Dynamic vector types are encoded similarly. /// Start of the lane types. pub const LANE_BASE: u16 = 0x70; diff --git a/cranelift/codegen/shared/src/lib.rs b/cranelift/codegen/shared/src/lib.rs index 76504f77f136..2a03a8323439 100644 --- a/cranelift/codegen/shared/src/lib.rs +++ b/cranelift/codegen/shared/src/lib.rs @@ -1,9 +1,7 @@ //! This library contains code that is common to both the `cranelift-codegen` and //! `cranelift-codegen-meta` libraries. -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces)] -#![cfg_attr(feature = "std", deny(unstable_features))] +#![deny(missing_docs)] pub mod constant_hash; pub mod constants; diff --git a/cranelift/codegen/src/alias_analysis.rs b/cranelift/codegen/src/alias_analysis.rs index f3b6339f9938..6a6e9f2748ba 100644 --- a/cranelift/codegen/src/alias_analysis.rs +++ b/cranelift/codegen/src/alias_analysis.rs @@ -64,14 +64,14 @@ use crate::{ cursor::{Cursor, FuncCursor}, dominator_tree::DominatorTree, - fx::{FxHashMap, FxHashSet}, inst_predicates::{ has_memory_fence_semantics, inst_addr_offset_type, inst_store_data, visit_block_succs, }, - ir::{immediates::Offset32, Block, Function, Inst, Opcode, Type, Value}, + ir::{immediates::Offset32, AliasRegion, Block, Function, Inst, Opcode, Type, Value}, trace, }; use cranelift_entity::{packed_option::PackedOption, EntityRef}; +use rustc_hash::{FxHashMap, FxHashSet}; /// For a given program point, the vector of last-store instruction /// indices for each disjoint category of abstract state. @@ -93,14 +93,11 @@ impl LastStores { self.other = inst.into(); } else if opcode.can_store() { if let Some(memflags) = func.dfg.insts[inst].memflags() { - if memflags.heap() { - self.heap = inst.into(); - } else if memflags.table() { - self.table = inst.into(); - } else if memflags.vmctx() { - self.vmctx = inst.into(); - } else { - self.other = inst.into(); + match memflags.alias_region() { + None => self.other = inst.into(), + Some(AliasRegion::Heap) => self.heap = inst.into(), + Some(AliasRegion::Table) => self.table = inst.into(), + Some(AliasRegion::Vmctx) => self.vmctx = inst.into(), } } else { self.heap = inst.into(); @@ -113,14 +110,11 @@ impl LastStores { fn get_last_store(&self, func: &Function, inst: Inst) -> PackedOption { if let Some(memflags) = func.dfg.insts[inst].memflags() { - if memflags.heap() { - self.heap - } else if memflags.table() { - self.table - } else if memflags.vmctx() { - self.vmctx - } else { - self.other + match memflags.alias_region() { + None => self.other, + Some(AliasRegion::Heap) => self.heap, + Some(AliasRegion::Table) => self.table, + Some(AliasRegion::Vmctx) => self.vmctx, } } else if func.dfg.insts[inst].opcode().can_load() || func.dfg.insts[inst].opcode().can_store() @@ -218,11 +212,10 @@ impl<'a> AliasAnalysis<'a> { while let Some(block) = queue.pop() { queue_set.remove(&block); - let mut state = self + let mut state = *self .block_input .entry(block) - .or_insert_with(|| LastStores::default()) - .clone(); + .or_insert_with(|| LastStores::default()); trace!( "alias analysis: input to block{} is {:?}", @@ -239,12 +232,12 @@ impl<'a> AliasAnalysis<'a> { let succ_first_inst = func.layout.block_insts(succ).into_iter().next().unwrap(); let updated = match self.block_input.get_mut(&succ) { Some(succ_state) => { - let old = succ_state.clone(); + let old = *succ_state; succ_state.meet_from(&state, succ_first_inst); *succ_state != old } None => { - self.block_input.insert(succ, state.clone()); + self.block_input.insert(succ, state); true } }; @@ -391,7 +384,7 @@ impl<'a> AliasAnalysis<'a> { while let Some(inst) = pos.next_inst() { if let Some(replaced_result) = self.process_inst(pos.func, &mut state, inst) { let result = pos.func.dfg.inst_results(inst)[0]; - pos.func.dfg.detach_results(inst); + pos.func.dfg.clear_results(inst); pos.func.dfg.change_to_alias(result, replaced_result); pos.remove_inst_and_step_back(); } diff --git a/cranelift/codegen/src/binemit/mod.rs b/cranelift/codegen/src/binemit/mod.rs index d5582d87f3be..edd4d7d7c904 100644 --- a/cranelift/codegen/src/binemit/mod.rs +++ b/cranelift/codegen/src/binemit/mod.rs @@ -3,9 +3,6 @@ //! The `binemit` module contains code for translating Cranelift's intermediate representation into //! binary machine code. -mod stack_map; - -pub use self::stack_map::StackMap; use core::fmt; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; @@ -64,18 +61,24 @@ pub enum Reloc { /// Offset within page of TLVP slot. MachOAarch64TlsAdrPageOff12, - /// Aarch64 TLS GD - /// Set an ADRP immediate field to the top 21 bits of the final address. Checks for overflow. - /// This is equivalent to `R_AARCH64_TLSGD_ADR_PAGE21` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#relocations-for-thread-local-storage) - Aarch64TlsGdAdrPage21, + /// Aarch64 TLSDESC Adr Page21 + /// This is equivalent to `R_AARCH64_TLSDESC_ADR_PAGE21` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors) + Aarch64TlsDescAdrPage21, + + /// Aarch64 TLSDESC Ld64 Lo12 + /// This is equivalent to `R_AARCH64_TLSDESC_LD64_LO12` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors) + Aarch64TlsDescLd64Lo12, - /// Aarch64 TLS GD - /// Set the add immediate field to the low 12 bits of the final address. Does not check for overflow. - /// This is equivalent to `R_AARCH64_TLSGD_ADD_LO12_NC` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#relocations-for-thread-local-storage) - Aarch64TlsGdAddLo12Nc, + /// Aarch64 TLSDESC Add Lo12 + /// This is equivalent to `R_AARCH64_TLSGD_ADD_LO12` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors) + Aarch64TlsDescAddLo12, + + /// Aarch64 TLSDESC Call + /// This is equivalent to `R_AARCH64_TLSDESC_CALL` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors) + Aarch64TlsDescCall, /// AArch64 GOT Page - /// Set the immediate value of an ADRP to bits 32:12 of X; check that –232 <= X < 232 + /// Set the immediate value of an ADRP to bits 32:12 of X; check that –2^32 <= X < 2^32 /// This is equivalent to `R_AARCH64_ADR_GOT_PAGE` (311) in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#static-aarch64-relocations) Aarch64AdrGotPage21, @@ -85,25 +88,33 @@ pub enum Reloc { /// This is equivalent to `R_AARCH64_LD64_GOT_LO12_NC` (312) in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#static-aarch64-relocations) Aarch64Ld64GotLo12Nc, - /// procedure call. - /// call symbol - /// expands to the following assembly and relocation: - /// auipc ra, 0 - /// jalr ra, ra, 0 - RiscvCall, + /// RISC-V Call PLT: 32-bit PC-relative function call, macros call, tail (PIC) + /// + /// Despite having PLT in the name, this relocation is also used for normal calls. + /// The non-PLT version of this relocation has been deprecated. + /// + /// This is the `R_RISCV_CALL_PLT` relocation from the RISC-V ELF psABI document. + /// + RiscvCallPlt, /// RISC-V TLS GD: High 20 bits of 32-bit PC-relative TLS GD GOT reference, /// /// This is the `R_RISCV_TLS_GD_HI20` relocation from the RISC-V ELF psABI document. - /// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#global-dynamic + /// RiscvTlsGdHi20, /// Low 12 bits of a 32-bit PC-relative relocation (I-Type instruction) /// /// This is the `R_RISCV_PCREL_LO12_I` relocation from the RISC-V ELF psABI document. - /// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses + /// RiscvPCRelLo12I, + /// High 20 bits of a 32-bit PC-relative GOT offset relocation + /// + /// This is the `R_RISCV_GOT_HI20` relocation from the RISC-V ELF psABI document. + /// + RiscvGotHi20, + /// s390x TLS GD64 - 64-bit offset of tls_index for GD symbol in GOT S390xTlsGd64, /// s390x TLS GDCall - marker to enable optimization of TLS calls @@ -125,15 +136,18 @@ impl fmt::Display for Reloc { Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"), Self::X86SecRel => write!(f, "SecRel"), Self::Arm32Call | Self::Arm64Call => write!(f, "Call"), - Self::RiscvCall => write!(f, "RiscvCall"), + Self::RiscvCallPlt => write!(f, "RiscvCallPlt"), Self::RiscvTlsGdHi20 => write!(f, "RiscvTlsGdHi20"), + Self::RiscvGotHi20 => write!(f, "RiscvGotHi20"), Self::RiscvPCRelLo12I => write!(f, "RiscvPCRelLo12I"), Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"), Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"), Self::MachOAarch64TlsAdrPage21 => write!(f, "MachOAarch64TlsAdrPage21"), Self::MachOAarch64TlsAdrPageOff12 => write!(f, "MachOAarch64TlsAdrPageOff12"), - Self::Aarch64TlsGdAdrPage21 => write!(f, "Aarch64TlsGdAdrPage21"), - Self::Aarch64TlsGdAddLo12Nc => write!(f, "Aarch64TlsGdAddLo12Nc"), + Self::Aarch64TlsDescAdrPage21 => write!(f, "Aarch64TlsDescAdrPage21"), + Self::Aarch64TlsDescLd64Lo12 => write!(f, "Aarch64TlsDescLd64Lo12"), + Self::Aarch64TlsDescAddLo12 => write!(f, "Aarch64TlsDescAddLo12"), + Self::Aarch64TlsDescCall => write!(f, "Aarch64TlsDescCall"), Self::Aarch64AdrGotPage21 => write!(f, "Aarch64AdrGotPage21"), Self::Aarch64Ld64GotLo12Nc => write!(f, "Aarch64AdrGotLo12Nc"), Self::S390xTlsGd64 => write!(f, "TlsGd64"), diff --git a/cranelift/codegen/src/binemit/stack_map.rs b/cranelift/codegen/src/binemit/stack_map.rs deleted file mode 100644 index 037611e6ea9f..000000000000 --- a/cranelift/codegen/src/binemit/stack_map.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::bitset::BitSet; -use alloc::vec::Vec; - -type Num = u32; -const NUM_BITS: usize = core::mem::size_of::() * 8; - -/// Stack maps record which words in a stack frame contain live GC references at -/// a given instruction pointer. -/// -/// Logically, a set of stack maps for a function record a table of the form: -/// -/// ```text -/// +---------------------+-------------------------------------------+ -/// | Instruction Pointer | SP-Relative Offsets of Live GC References | -/// +---------------------+-------------------------------------------+ -/// | 0x12345678 | 2, 6, 12 | -/// | 0x1234abcd | 2, 6 | -/// | ... | ... | -/// +---------------------+-------------------------------------------+ -/// ``` -/// -/// Where "instruction pointer" is an instruction pointer within the function, -/// and "offsets of live GC references" contains the offsets (in units of words) -/// from the frame's stack pointer where live GC references are stored on the -/// stack. Instruction pointers within the function that do not have an entry in -/// this table are not GC safepoints. -/// -/// Because -/// -/// * offsets of live GC references are relative from the stack pointer, and -/// * stack frames grow down from higher addresses to lower addresses, -/// -/// to get a pointer to a live reference at offset `x` within a stack frame, you -/// add `x` from the frame's stack pointer. -/// -/// For example, to calculate the pointer to the live GC reference inside "frame -/// 1" below, you would do `frame_1_sp + x`: -/// -/// ```text -/// Stack -/// +-------------------+ -/// | Frame 0 | -/// | | -/// | | | -/// | +-------------------+ <--- Frame 0's SP -/// | | Frame 1 | -/// Grows | | -/// down | | -/// | | Live GC reference | --+-- -/// | | | | -/// | | | | -/// V | | x = offset of live GC reference -/// | | | -/// | | | -/// +-------------------+ --+-- <--- Frame 1's SP -/// | Frame 2 | -/// | ... | -/// ``` -/// -/// An individual `StackMap` is associated with just one instruction pointer -/// within the function, contains the size of the stack frame, and represents -/// the stack frame as a bitmap. There is one bit per word in the stack frame, -/// and if the bit is set, then the word contains a live GC reference. -/// -/// Note that a caller's `OutgoingArg` stack slots and callee's `IncomingArg` -/// stack slots overlap, so we must choose which function's stack maps record -/// live GC references in these slots. We record the `IncomingArg`s in the -/// callee's stack map. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr( - feature = "enable-serde", - derive(serde_derive::Deserialize, serde_derive::Serialize) -)] -pub struct StackMap { - bitmap: Vec>, - mapped_words: u32, -} - -impl StackMap { - /// Create a vec of Bitsets from a slice of bools. - pub fn from_slice(vec: &[bool]) -> Self { - let len = vec.len(); - let num_word = len / NUM_BITS + (len % NUM_BITS != 0) as usize; - let mut bitmap = Vec::with_capacity(num_word); - - for segment in vec.chunks(NUM_BITS) { - let mut curr_word = 0; - for (i, set) in segment.iter().enumerate() { - if *set { - curr_word |= 1 << i; - } - } - bitmap.push(BitSet(curr_word)); - } - Self { - mapped_words: len as u32, - bitmap, - } - } - - /// Returns a specified bit. - pub fn get_bit(&self, bit_index: usize) -> bool { - assert!(bit_index < NUM_BITS * self.bitmap.len()); - let word_index = bit_index / NUM_BITS; - let word_offset = bit_index % NUM_BITS; - self.bitmap[word_index].contains(word_offset as u32) - } - - /// Returns the raw bitmap that represents this stack map. - pub fn as_slice(&self) -> &[BitSet] { - &self.bitmap - } - - /// Returns the number of words represented by this stack map. - pub fn mapped_words(&self) -> u32 { - self.mapped_words - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn stack_maps() { - let vec: Vec = Vec::new(); - assert!(StackMap::from_slice(&vec).bitmap.is_empty()); - - let mut vec: [bool; NUM_BITS] = Default::default(); - let set_true_idx = [5, 7, 24, 31]; - - for &idx in &set_true_idx { - vec[idx] = true; - } - - let mut vec = vec.to_vec(); - assert_eq!( - vec![BitSet::(2164261024)], - StackMap::from_slice(&vec).bitmap - ); - - vec.push(false); - vec.push(true); - let res = StackMap::from_slice(&vec); - assert_eq!( - vec![BitSet::(2164261024), BitSet::(2)], - res.bitmap - ); - - assert!(res.get_bit(5)); - assert!(res.get_bit(31)); - assert!(res.get_bit(33)); - assert!(!res.get_bit(1)); - } -} diff --git a/cranelift/codegen/src/bitset.rs b/cranelift/codegen/src/bitset.rs deleted file mode 100644 index 855d585d7c61..000000000000 --- a/cranelift/codegen/src/bitset.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! Small Bitset -//! -//! This module defines a struct `BitSet` encapsulating a bitset built over the type T. -//! T is intended to be a primitive unsigned type. Currently it can be any type between u8 and u32 -//! -//! If you would like to add support for larger bitsets in the future, you need to change the trait -//! bound `Into` and the `u32` in the implementation of `max_bits()`. - -use core::convert::{From, Into}; -use core::mem::size_of; -use core::ops::{Add, BitOr, Shl, Sub}; - -/// A small bitset built on a single primitive integer type -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -#[cfg_attr( - feature = "enable-serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) -)] -pub struct BitSet(pub T); - -impl BitSet -where - T: Into - + From - + BitOr - + Shl - + Sub - + Add - + PartialEq - + Copy, -{ - /// Maximum number of bits supported by this BitSet instance - pub fn bits() -> usize { - size_of::() * 8 - } - - /// Maximum number of bits supported by any bitset instance atm. - pub fn max_bits() -> usize { - size_of::() * 8 - } - - /// Check if this BitSet contains the number num - pub fn contains(&self, num: u32) -> bool { - debug_assert!((num as usize) < Self::bits()); - debug_assert!((num as usize) < Self::max_bits()); - self.0.into() & (1 << num) != 0 - } - - /// Return the smallest number contained in the bitset or None if empty - pub fn min(&self) -> Option { - if self.0.into() == 0 { - None - } else { - Some(self.0.into().trailing_zeros() as u8) - } - } - - /// Return the largest number contained in the bitset or None if empty - pub fn max(&self) -> Option { - if self.0.into() == 0 { - None - } else { - let leading_zeroes = self.0.into().leading_zeros() as usize; - Some((Self::max_bits() - leading_zeroes - 1) as u8) - } - } - - /// Construct a BitSet with the half-open range [lo,hi) filled in - pub fn from_range(lo: u8, hi: u8) -> Self { - debug_assert!(lo <= hi); - debug_assert!((hi as usize) <= Self::bits()); - let one: T = T::from(1); - // I can't just do (one << hi) - one here as the shift may overflow - let hi_rng = if hi >= 1 { - (one << (hi - 1)) + ((one << (hi - 1)) - one) - } else { - T::from(0) - }; - - let lo_rng = (one << lo) - one; - - Self(hi_rng - lo_rng) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn contains() { - let s = BitSet::(255); - for i in 0..7 { - assert!(s.contains(i)); - } - - let s1 = BitSet::(0); - for i in 0..7 { - assert!(!s1.contains(i)); - } - - let s2 = BitSet::(127); - for i in 0..6 { - assert!(s2.contains(i)); - } - assert!(!s2.contains(7)); - - let s3 = BitSet::(2 | 4 | 64); - assert!(!s3.contains(0) && !s3.contains(3) && !s3.contains(4)); - assert!(!s3.contains(5) && !s3.contains(7)); - assert!(s3.contains(1) && s3.contains(2) && s3.contains(6)); - - let s4 = BitSet::(4 | 8 | 256 | 1024); - assert!( - !s4.contains(0) - && !s4.contains(1) - && !s4.contains(4) - && !s4.contains(5) - && !s4.contains(6) - && !s4.contains(7) - && !s4.contains(9) - && !s4.contains(11) - ); - assert!(s4.contains(2) && s4.contains(3) && s4.contains(8) && s4.contains(10)); - } - - #[test] - fn minmax() { - let s = BitSet::(255); - assert_eq!(s.min(), Some(0)); - assert_eq!(s.max(), Some(7)); - assert!(s.min() == Some(0) && s.max() == Some(7)); - let s1 = BitSet::(0); - assert!(s1.min() == None && s1.max() == None); - let s2 = BitSet::(127); - assert!(s2.min() == Some(0) && s2.max() == Some(6)); - let s3 = BitSet::(2 | 4 | 64); - assert!(s3.min() == Some(1) && s3.max() == Some(6)); - let s4 = BitSet::(4 | 8 | 256 | 1024); - assert!(s4.min() == Some(2) && s4.max() == Some(10)); - } - - #[test] - fn from_range() { - let s = BitSet::::from_range(5, 5); - assert!(s.0 == 0); - - let s = BitSet::::from_range(0, 8); - assert!(s.0 == 255); - - let s = BitSet::::from_range(0, 8); - assert!(s.0 == 255u16); - - let s = BitSet::::from_range(0, 16); - assert!(s.0 == 65535u16); - - let s = BitSet::::from_range(5, 6); - assert!(s.0 == 32u8); - - let s = BitSet::::from_range(3, 7); - assert!(s.0 == 8 | 16 | 32 | 64); - - let s = BitSet::::from_range(5, 11); - assert!(s.0 == 32 | 64 | 128 | 256 | 512 | 1024); - } -} diff --git a/cranelift/codegen/src/cfg_printer.rs b/cranelift/codegen/src/cfg_printer.rs index 7b906d19e78c..ef71b63ca712 100644 --- a/cranelift/codegen/src/cfg_printer.rs +++ b/cranelift/codegen/src/cfg_printer.rs @@ -35,7 +35,7 @@ impl<'a> CFGPrinter<'a> { fn header(&self, w: &mut dyn Write) -> Result { writeln!(w, "digraph \"{}\" {{", self.func.name)?; if let Some(entry) = self.func.layout.entry_block() { - writeln!(w, " {{rank=min; {}}}", entry)?; + writeln!(w, " {{rank=min; {entry}}}")?; } Ok(()) } @@ -50,11 +50,11 @@ impl<'a> CFGPrinter<'a> { } for block in &self.func.layout { - write!(w, " {} [shape=record, label=\"{{", block)?; + write!(w, " {block} [shape=record, label=\"{{")?; crate::write::write_block_header(w, self.func, block, 4)?; // Add all outgoing branch instructions to the label. if let Some(inst) = self.func.layout.last_inst(block) { - write!(w, " | <{}>", inst)?; + write!(w, " | <{inst}>")?; PlainWriter.write_instruction(w, self.func, &aliases, inst, 0)?; } writeln!(w, "}}\"]")? @@ -69,7 +69,7 @@ impl<'a> CFGPrinter<'a> { inst, } in self.cfg.pred_iter(block) { - writeln!(w, " {}:{} -> {}", parent, inst, block)?; + writeln!(w, " {parent}:{inst} -> {block}")?; } } Ok(()) diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs index 2dc62ee77fd5..d95e985a4854 100644 --- a/cranelift/codegen/src/context.rs +++ b/cranelift/codegen/src/context.rs @@ -10,7 +10,6 @@ //! single ISA instance. use crate::alias_analysis::AliasAnalysis; -use crate::dce::do_dce; use crate::dominator_tree::DominatorTree; use crate::egraph::EgraphPass; use crate::flowgraph::ControlFlowGraph; @@ -31,6 +30,7 @@ use crate::{timing, CompileError}; use alloc::string::String; use alloc::vec::Vec; use cranelift_control::ControlPlane; +use target_lexicon::Architecture; #[cfg(feature = "souper-harvest")] use crate::souper_harvest::do_souper_harvest; @@ -96,6 +96,12 @@ impl Context { self.compiled_code.as_ref() } + /// Returns the compilation result for this function, available after any `compile` function + /// has been called. + pub fn take_compiled_code(&mut self) -> Option { + self.compiled_code.take() + } + /// Set the flag to request a disassembly when compiling with a /// `MachBackend` backend. pub fn set_disasm(&mut self, val: bool) { @@ -141,7 +147,7 @@ impl Context { self.verify_if(isa)?; - self.optimize(isa)?; + self.optimize(isa, ctrl_plane)?; isa.compile_function(&self.func, &self.domtree, self.want_disasm, ctrl_plane) } @@ -151,7 +157,11 @@ impl Context { /// allocation. /// /// Public only for testing purposes. - pub fn optimize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { + pub fn optimize( + &mut self, + isa: &dyn TargetIsa, + ctrl_plane: &mut ControlPlane, + ) -> CodegenResult<()> { log::debug!( "Number of CLIF instructions to optimize: {}", self.func.dfg.num_insts() @@ -177,15 +187,12 @@ impl Context { self.compute_domtree(); self.eliminate_unreachable_code(isa)?; - - if opt_level != OptLevel::None { - self.dce(isa)?; - } - self.remove_constant_phis(isa)?; + self.func.dfg.resolve_all_aliases(); + if opt_level != OptLevel::None { - self.egraph_pass(isa)?; + self.egraph_pass(isa, ctrl_plane)?; } Ok(()) @@ -237,6 +244,8 @@ impl Context { /// Run the verifier on the function. /// /// Also check that the dominator tree and control flow graph are consistent with the function. + /// + /// TODO: rename to "CLIF validate" or similar. pub fn verify<'a, FOI: Into>>(&self, fisa: FOI) -> VerifierResult<()> { let mut errors = VerifierErrors::default(); let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors); @@ -257,13 +266,6 @@ impl Context { Ok(()) } - /// Perform dead-code elimination on the function. - pub fn dce<'a, FOI: Into>>(&mut self, fisa: FOI) -> CodegenResult<()> { - do_dce(&mut self.func, &mut self.domtree); - self.verify_if(fisa)?; - Ok(()) - } - /// Perform constant-phi removal on the function. pub fn remove_constant_phis<'a, FOI: Into>>( &mut self, @@ -276,7 +278,15 @@ impl Context { /// Perform NaN canonicalizing rewrites on the function. pub fn canonicalize_nans(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { - do_nan_canonicalization(&mut self.func); + // Currently only RiscV64 is the only arch that may not have vector support. + let has_vector_support = match isa.triple().architecture { + Architecture::Riscv64(_) => match isa.isa_flags().iter().find(|f| f.name == "has_v") { + Some(value) => value.as_bool().unwrap_or(false), + None => false, + }, + _ => true, + }; + do_nan_canonicalization(&mut self.func, has_vector_support); self.verify_if(isa) } @@ -345,7 +355,11 @@ impl Context { } /// Run optimizations via the egraph infrastructure. - pub fn egraph_pass<'a, FOI>(&mut self, fisa: FOI) -> CodegenResult<()> + pub fn egraph_pass<'a, FOI>( + &mut self, + fisa: FOI, + ctrl_plane: &mut ControlPlane, + ) -> CodegenResult<()> where FOI: Into>, { @@ -355,6 +369,7 @@ impl Context { "About to optimize with egraph phase:\n{}", self.func.display() ); + let fisa = fisa.into(); self.compute_loop_analysis(); let mut alias_analysis = AliasAnalysis::new(&self.func, &self.domtree); let mut pass = EgraphPass::new( @@ -362,9 +377,12 @@ impl Context { &self.domtree, &self.loop_analysis, &mut alias_analysis, + &fisa.flags, + ctrl_plane, ); pass.run(); log::debug!("egraph stats: {:?}", pass.stats); + trace!("pinned_union_count: {}", pass.eclasses.pinned_union_count); trace!("After egraph optimization:\n{}", self.func.display()); self.verify_if(fisa) diff --git a/cranelift/codegen/src/ctxhash.rs b/cranelift/codegen/src/ctxhash.rs index e172d46c127a..74290877bbcf 100644 --- a/cranelift/codegen/src/ctxhash.rs +++ b/cranelift/codegen/src/ctxhash.rs @@ -76,7 +76,7 @@ fn compute_hash(ctx: &Ctx, k: &K) -> u32 where Ctx: CtxHash, { - let mut hasher = crate::fx::FxHasher::default(); + let mut hasher = rustc_hash::FxHasher::default(); ctx.ctx_hash(&mut hasher, k); hasher.finish() as u32 } @@ -125,7 +125,6 @@ impl CtxHashMap { #[cfg(test)] mod test { use super::*; - use std::hash::Hash; #[derive(Clone, Copy, Debug)] struct Key { diff --git a/cranelift/codegen/src/data_value.rs b/cranelift/codegen/src/data_value.rs index af067b367128..523182f07d9e 100644 --- a/cranelift/codegen/src/data_value.rs +++ b/cranelift/codegen/src/data_value.rs @@ -1,8 +1,8 @@ //! This module gives users to instantiate values that Cranelift understands. These values are used, //! for example, during interpretation and for wrapping immediates. -use crate::ir::immediates::{Ieee32, Ieee64, Offset32}; +use crate::ir::immediates::{Ieee128, Ieee16, Ieee32, Ieee64, Offset32}; use crate::ir::{types, ConstantData, Type}; -use core::convert::TryInto; +use core::cmp::Ordering; use core::fmt::{self, Display, Formatter}; /// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value @@ -17,8 +17,10 @@ pub enum DataValue { I32(i32), I64(i64), I128(i128), + F16(Ieee16), F32(Ieee32), F64(Ieee64), + F128(Ieee128), V128([u8; 16]), V64([u8; 8]), } @@ -37,10 +39,14 @@ impl PartialEq for DataValue { (I64(_), _) => false, (I128(l), I128(r)) => l == r, (I128(_), _) => false, + (F16(l), F16(r)) => l.partial_cmp(&r) == Some(Ordering::Equal), + (F16(_), _) => false, (F32(l), F32(r)) => l.as_f32() == r.as_f32(), (F32(_), _) => false, (F64(l), F64(r)) => l.as_f64() == r.as_f64(), (F64(_), _) => false, + (F128(l), F128(r)) => l.partial_cmp(&r) == Some(Ordering::Equal), + (F128(_), _) => false, (V128(l), V128(r)) => l == r, (V128(_), _) => false, (V64(l), V64(r)) => l == r, @@ -71,8 +77,10 @@ impl DataValue { DataValue::I32(_) => types::I32, DataValue::I64(_) => types::I64, DataValue::I128(_) => types::I128, + DataValue::F16(_) => types::F16, DataValue::F32(_) => types::F32, DataValue::F64(_) => types::F64, + DataValue::F128(_) => types::F128, DataValue::V128(_) => types::I8X16, // A default type. DataValue::V64(_) => types::I8X8, // A default type. } @@ -93,8 +101,10 @@ impl DataValue { DataValue::I32(i) => DataValue::I32(i.swap_bytes()), DataValue::I64(i) => DataValue::I64(i.swap_bytes()), DataValue::I128(i) => DataValue::I128(i.swap_bytes()), + DataValue::F16(f) => DataValue::F16(Ieee16::with_bits(f.bits().swap_bytes())), DataValue::F32(f) => DataValue::F32(Ieee32::with_bits(f.bits().swap_bytes())), DataValue::F64(f) => DataValue::F64(Ieee64::with_bits(f.bits().swap_bytes())), + DataValue::F128(f) => DataValue::F128(Ieee128::with_bits(f.bits().swap_bytes())), DataValue::V128(mut v) => { v.reverse(); DataValue::V128(v) @@ -136,8 +146,10 @@ impl DataValue { DataValue::I32(i) => dst[..4].copy_from_slice(&i.to_ne_bytes()[..]), DataValue::I64(i) => dst[..8].copy_from_slice(&i.to_ne_bytes()[..]), DataValue::I128(i) => dst[..16].copy_from_slice(&i.to_ne_bytes()[..]), + DataValue::F16(f) => dst[..2].copy_from_slice(&f.bits().to_ne_bytes()[..]), DataValue::F32(f) => dst[..4].copy_from_slice(&f.bits().to_ne_bytes()[..]), DataValue::F64(f) => dst[..8].copy_from_slice(&f.bits().to_ne_bytes()[..]), + DataValue::F128(f) => dst[..16].copy_from_slice(&f.bits().to_ne_bytes()[..]), DataValue::V128(v) => dst[..16].copy_from_slice(&v[..]), DataValue::V64(v) => dst[..8].copy_from_slice(&v[..]), }; @@ -173,12 +185,18 @@ impl DataValue { types::I32 => DataValue::I32(i32::from_ne_bytes(src[..4].try_into().unwrap())), types::I64 => DataValue::I64(i64::from_ne_bytes(src[..8].try_into().unwrap())), types::I128 => DataValue::I128(i128::from_ne_bytes(src[..16].try_into().unwrap())), + types::F16 => DataValue::F16(Ieee16::with_bits(u16::from_ne_bytes( + src[..2].try_into().unwrap(), + ))), types::F32 => DataValue::F32(Ieee32::with_bits(u32::from_ne_bytes( src[..4].try_into().unwrap(), ))), types::F64 => DataValue::F64(Ieee64::with_bits(u64::from_ne_bytes( src[..8].try_into().unwrap(), ))), + types::F128 => DataValue::F128(Ieee128::with_bits(u128::from_ne_bytes( + src[..16].try_into().unwrap(), + ))), _ if ty.is_vector() => { if ty.bytes() == 16 { DataValue::V128(src[..16].try_into().unwrap()) @@ -234,8 +252,10 @@ impl DataValue { // We need to bit compare the floats to ensure that we produce the correct values // on NaN's. The test suite expects to assert the precise bit pattern on NaN's or // works around it in the tests themselves. + (DataValue::F16(a), DataValue::F16(b)) => a.bits() == b.bits(), (DataValue::F32(a), DataValue::F32(b)) => a.bits() == b.bits(), (DataValue::F64(a), DataValue::F64(b)) => a.bits() == b.bits(), + (DataValue::F128(a), DataValue::F128(b)) => a.bits() == b.bits(), // We don't need to worry about F32x4 / F64x2 Since we compare V128 which is already the // raw bytes anyway @@ -260,18 +280,10 @@ impl Display for DataValueCastFailure { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { DataValueCastFailure::TryInto(from, to) => { - write!( - f, - "unable to cast data value of type {} to type {}", - from, to - ) + write!(f, "unable to cast data value of type {from} to type {to}") } DataValueCastFailure::FromInteger(val, to) => { - write!( - f, - "unable to cast i64({}) to a data value of type {}", - val, to - ) + write!(f, "unable to cast i64({val}) to a data value of type {to}") } } } @@ -306,8 +318,10 @@ build_conversion_impl!(i16, I16, I16); build_conversion_impl!(i32, I32, I32); build_conversion_impl!(i64, I64, I64); build_conversion_impl!(i128, I128, I128); +build_conversion_impl!(Ieee16, F16, F16); build_conversion_impl!(Ieee32, F32, F32); build_conversion_impl!(Ieee64, F64, F64); +build_conversion_impl!(Ieee128, F128, F128); build_conversion_impl!([u8; 16], V128, I8X16); build_conversion_impl!([u8; 8], V64, I8X8); impl From for DataValue { @@ -319,14 +333,16 @@ impl From for DataValue { impl Display for DataValue { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - DataValue::I8(dv) => write!(f, "{}", dv), - DataValue::I16(dv) => write!(f, "{}", dv), - DataValue::I32(dv) => write!(f, "{}", dv), - DataValue::I64(dv) => write!(f, "{}", dv), - DataValue::I128(dv) => write!(f, "{}", dv), + DataValue::I8(dv) => write!(f, "{dv}"), + DataValue::I16(dv) => write!(f, "{dv}"), + DataValue::I32(dv) => write!(f, "{dv}"), + DataValue::I64(dv) => write!(f, "{dv}"), + DataValue::I128(dv) => write!(f, "{dv}"), // The Ieee* wrappers here print the expected syntax. - DataValue::F32(dv) => write!(f, "{}", dv), - DataValue::F64(dv) => write!(f, "{}", dv), + DataValue::F16(dv) => write!(f, "{dv}"), + DataValue::F32(dv) => write!(f, "{dv}"), + DataValue::F64(dv) => write!(f, "{dv}"), + DataValue::F128(dv) => write!(f, "{dv}"), // Again, for syntax consistency, use ConstantData, which in this case displays as hex. DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])), DataValue::V64(dv) => write!(f, "{}", ConstantData::from(&dv[..])), @@ -360,7 +376,7 @@ pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt:: _ => { write!(f, "{}", list[0])?; for dv in list.iter().skip(1) { - write!(f, ", {}", dv)?; + write!(f, ", {dv}")?; } Ok(()) } diff --git a/cranelift/codegen/src/dbg.rs b/cranelift/codegen/src/dbg.rs index 1d814ceedb40..4796f3967b91 100644 --- a/cranelift/codegen/src/dbg.rs +++ b/cranelift/codegen/src/dbg.rs @@ -17,9 +17,9 @@ where match self.0.split_first() { None => write!(f, "[]"), Some((first, rest)) => { - write!(f, "[{}", first)?; + write!(f, "[{first}")?; for x in rest { - write!(f, ", {}", x)?; + write!(f, ", {x}")?; } write!(f, "]") } diff --git a/cranelift/codegen/src/dce.rs b/cranelift/codegen/src/dce.rs deleted file mode 100644 index 2b52f92bcf3a..000000000000 --- a/cranelift/codegen/src/dce.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! A Dead-Code Elimination (DCE) pass. -//! -//! Dead code here means instructions that have no side effects and have no -//! result values used by other instructions. - -use crate::cursor::{Cursor, FuncCursor}; -use crate::dominator_tree::DominatorTree; -use crate::entity::EntityRef; -use crate::inst_predicates::{any_inst_results_used, has_side_effect}; -use crate::ir::Function; -use crate::timing; - -/// Perform DCE on `func`. -pub fn do_dce(func: &mut Function, domtree: &DominatorTree) { - let _tt = timing::dce(); - debug_assert!(domtree.is_valid()); - - let mut live = vec![false; func.dfg.num_values()]; - for &block in domtree.cfg_postorder() { - let mut pos = FuncCursor::new(func).at_bottom(block); - while let Some(inst) = pos.prev_inst() { - { - if has_side_effect(pos.func, inst) - || any_inst_results_used(inst, &live, &pos.func.dfg) - { - for arg in pos.func.dfg.inst_values(inst) { - let v = pos.func.dfg.resolve_aliases(arg); - live[v.index()] = true; - } - - continue; - } - } - pos.remove_inst(); - } - } -} diff --git a/cranelift/codegen/src/dominator_tree.rs b/cranelift/codegen/src/dominator_tree.rs index c549e6ab0874..12288362b48b 100644 --- a/cranelift/codegen/src/dominator_tree.rs +++ b/cranelift/codegen/src/dominator_tree.rs @@ -5,6 +5,7 @@ use crate::flowgraph::{BlockPredecessor, ControlFlowGraph}; use crate::ir::{Block, Function, Inst, Layout, ProgramPoint}; use crate::packed_option::PackedOption; use crate::timing; +use crate::traversals::Dfs; use alloc::vec::Vec; use core::cmp; use core::cmp::Ordering; @@ -14,9 +15,6 @@ use core::mem; /// room for modifications of the dominator tree. const STRIDE: u32 = 4; -/// Special RPO numbers used during `compute_postorder`. -const SEEN: u32 = 1; - /// Dominator tree node. We keep one of these per block. #[derive(Clone, Default)] struct DomNode { @@ -34,12 +32,6 @@ struct DomNode { idom: PackedOption, } -/// DFT stack state marker for computing the cfg postorder. -enum Visit { - First, - Last, -} - /// The dominator tree for a single function. pub struct DominatorTree { nodes: SecondaryMap, @@ -47,8 +39,8 @@ pub struct DominatorTree { /// CFG post-order of all reachable blocks. postorder: Vec, - /// Scratch memory used by `compute_postorder()`. - stack: Vec<(Visit, Block)>, + /// Scratch traversal state used by `compute_postorder()`. + dfs: Dfs, valid: bool, } @@ -225,7 +217,7 @@ impl DominatorTree { Self { nodes: SecondaryMap::new(), postorder: Vec::new(), - stack: Vec::new(), + dfs: Dfs::new(), valid: false, } } @@ -236,7 +228,7 @@ impl DominatorTree { let mut domtree = Self { nodes: SecondaryMap::with_capacity(block_capacity), postorder: Vec::with_capacity(block_capacity), - stack: Vec::new(), + dfs: Dfs::new(), valid: false, }; domtree.compute(func, cfg); @@ -257,7 +249,6 @@ impl DominatorTree { pub fn clear(&mut self) { self.nodes.clear(); self.postorder.clear(); - debug_assert!(self.stack.is_empty()); self.valid = false; } @@ -276,73 +267,7 @@ impl DominatorTree { fn compute_postorder(&mut self, func: &Function) { self.clear(); self.nodes.resize(func.dfg.num_blocks()); - - // This algorithm is a depth first traversal (DFT) of the control flow graph, computing a - // post-order of the blocks that are reachable form the entry block. A DFT post-order is not - // unique. The specific order we get is controlled by the order each node's children are - // visited. - // - // We view the CFG as a graph where each `BlockCall` value of a terminating branch - // instruction is an edge. A consequence of this is that we visit successor nodes in the - // reverse order specified by the branch instruction that terminates the basic block. - // (Reversed because we are using a stack to control traversal, and push the successors in - // the order the branch instruction specifies -- there's no good reason for this particular - // order.) - // - // During this algorithm only, use `rpo_number` to hold the following state: - // - // 0: block has not yet had its first visit - // SEEN: block has been visited at least once, implying that all of its successors are on - // the stack - - match func.layout.entry_block() { - Some(block) => { - self.stack.push((Visit::First, block)); - } - None => return, - } - - while let Some((visit, block)) = self.stack.pop() { - match visit { - Visit::First => { - if self.nodes[block].rpo_number == 0 { - // This is the first time we pop the block, so we need to scan its - // successors and then revisit it. - self.nodes[block].rpo_number = SEEN; - self.stack.push((Visit::Last, block)); - if let Some(inst) = func.stencil.layout.last_inst(block) { - // Heuristic: chase the children in reverse. This puts the first - // successor block first in the postorder, all other things being - // equal, which tends to prioritize loop backedges over out-edges, - // putting the edge-block closer to the loop body and minimizing - // live-ranges in linear instruction space. This heuristic doesn't have - // any effect on the computation of dominators, and is purely for other - // consumers of the postorder we cache here. - for block in func.stencil.dfg.insts[inst] - .branch_destination(&func.stencil.dfg.jump_tables) - .iter() - .rev() - { - let succ = block.block(&func.stencil.dfg.value_lists); - - // This is purely an optimization to avoid additional iterations of - // the loop, and is not required; it's merely inlining the check - // from the outer conditional of this case to avoid the extra loop - // iteration. - if self.nodes[succ].rpo_number == 0 { - self.stack.push((Visit::First, succ)) - } - } - } - } - } - - Visit::Last => { - // We've finished all this node's successors. - self.postorder.push(block); - } - } - } + self.postorder.extend(self.dfs.post_order_iter(func)); } /// Build a dominator tree from a control flow graph using Keith D. Cooper's @@ -467,7 +392,6 @@ impl DominatorTreePreorder { /// Recompute this data structure to match `domtree`. pub fn compute(&mut self, domtree: &DominatorTree, layout: &Layout) { self.nodes.clear(); - debug_assert_eq!(self.stack.len(), 0); // Step 1: Populate the child and sibling links. // @@ -586,9 +510,8 @@ impl DominatorTreePreorder { mod tests { use super::*; use crate::cursor::{Cursor, FuncCursor}; - use crate::flowgraph::ControlFlowGraph; use crate::ir::types::*; - use crate::ir::{Function, InstBuilder, TrapCode}; + use crate::ir::{InstBuilder, TrapCode}; #[test] fn empty() { diff --git a/cranelift/codegen/src/egraph.rs b/cranelift/codegen/src/egraph.rs index e12b6fed39f2..5d4d7c22fd1d 100644 --- a/cranelift/codegen/src/egraph.rs +++ b/cranelift/codegen/src/egraph.rs @@ -3,26 +3,29 @@ use crate::alias_analysis::{AliasAnalysis, LastStores}; use crate::ctxhash::{CtxEq, CtxHash, CtxHashMap}; use crate::cursor::{Cursor, CursorPosition, FuncCursor}; -use crate::dominator_tree::DominatorTree; -use crate::egraph::domtree::DomTreeWithChildren; +use crate::dominator_tree::{DominatorTree, DominatorTreePreorder}; use crate::egraph::elaborate::Elaborator; -use crate::fx::FxHashSet; 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, Type, Value, ValueDef, ValueListPool, + Block, DataFlowGraph, Function, Inst, InstructionData, Opcode, Type, Value, ValueDef, + ValueListPool, }; use crate::loop_analysis::LoopAnalysis; -use crate::opts::generated_code::ContextIter; use crate::opts::IsleContext; use crate::scoped_hash_map::{Entry as ScopedEntry, ScopedHashMap}; +use crate::settings::Flags; use crate::trace; use crate::unionfind::UnionFind; +use core::cmp::Ordering; +use cranelift_control::ControlPlane; use cranelift_entity::packed_option::ReservedValue; use cranelift_entity::SecondaryMap; +use rustc_hash::FxHashSet; +use smallvec::SmallVec; use std::hash::Hasher; mod cost; -mod domtree; mod elaborate; /// Pass over a Function that does the whole aegraph thing. @@ -46,16 +49,20 @@ mod elaborate; pub struct EgraphPass<'a> { /// The function we're operating on. func: &'a mut Function, - /// Dominator tree, used for elaboration pass. - domtree: &'a DominatorTree, + /// Dominator tree for the CFG, used to visit blocks in pre-order + /// so we see value definitions before their uses, and also used for + /// O(1) dominance checks. + domtree: DominatorTreePreorder, /// Alias analysis, used during optimization. alias_analysis: &'a mut AliasAnalysis<'a>, - /// "Domtree with children": like `domtree`, but with an explicit - /// list of children, rather than just parent pointers. - domtree_children: DomTreeWithChildren, /// 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, /// Which canonical Values do we want to rematerialize in each /// block where they're used? /// @@ -66,9 +73,12 @@ pub struct EgraphPass<'a> { pub(crate) stats: Stats, /// Union-find that maps all members of a Union tree (eclass) back /// to the *oldest* (lowest-numbered) `Value`. - eclasses: UnionFind, + pub(crate) eclasses: UnionFind, } +// The maximum number of rewrites we will take from a single call into ISLE. +const MATCHES_LIMIT: usize = 5; + /// Context passed through node insertion and optimization. pub(crate) struct OptimizeCtx<'opt, 'analysis> where @@ -79,14 +89,19 @@ where pub(crate) value_to_opt_value: &'opt mut SecondaryMap, pub(crate) gvn_map: &'opt mut CtxHashMap<(Type, InstructionData), Value>, pub(crate) effectful_gvn_map: &'opt mut ScopedHashMap<(Type, InstructionData), Value>, + available_block: &'opt mut SecondaryMap, pub(crate) eclasses: &'opt mut UnionFind, pub(crate) remat_values: &'opt mut FxHashSet, pub(crate) stats: &'opt mut Stats, + domtree: &'opt DominatorTreePreorder, 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, pub(crate) subsume_values: FxHashSet, + optimized_values: SmallVec<[Value; MATCHES_LIMIT]>, } /// For passing to `insert_pure_enode`. Sometimes the enode already @@ -104,7 +119,7 @@ impl NewOrExistingInst { NewOrExistingInst::New(data, ty) => (*ty, *data), NewOrExistingInst::Existing(inst) => { let ty = dfg.ctrl_typevar(*inst); - (ty, dfg.insts[*inst].clone()) + (ty, dfg.insts[*inst]) } } } @@ -160,13 +175,19 @@ where if let NewOrExistingInst::Existing(inst) = inst { debug_assert_eq!(self.func.dfg.inst_results(inst).len(), 1); let result = self.func.dfg.first_result(inst); + debug_assert!( + self.domtree.dominates( + self.available_block[orig_result], + self.get_available_block(inst) + ), + "GVN shouldn't replace {result} (available in {}) with non-dominating {orig_result} (available in {})", + self.get_available_block(inst), + self.available_block[orig_result], + ); self.value_to_opt_value[result] = orig_result; - self.eclasses.union(result, orig_result); - self.stats.union += 1; - result - } else { - orig_result + self.func.dfg.merge_facts(result, orig_result); } + orig_result } else { // Now actually insert the InstructionData and attach // result value (exactly one). @@ -188,21 +209,65 @@ 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); + + for &argument in self.func.dfg.inst_args(inst) { + self.eclasses.pin_index(argument); + } + let gvn_context = GVNContext { union_find: self.eclasses, value_lists: &self.func.dfg.value_lists, }; - self.gvn_map.insert( - (ty, self.func.dfg.insts[inst].clone()), - opt_value, - &gvn_context, - ); + self.gvn_map + .insert((ty, self.func.dfg.insts[inst]), opt_value, &gvn_context); self.value_to_opt_value[result] = opt_value; opt_value } } + /// Find the block where a pure instruction first becomes available, + /// defined as the block that is closest to the root where all of + /// its arguments are available. In the unusual case where a pure + /// instruction has no arguments (e.g. get_return_address), we can + /// place it anywhere, so it is available in the entry block. + /// + /// This function does not compute available blocks recursively. + /// All of the instruction's arguments must have had their available + /// blocks assigned already. + fn get_available_block(&self, inst: Inst) -> Block { + // Side-effecting instructions have different rules for where + // they become available, so this function does not apply. + debug_assert!(is_pure_for_egraph(self.func, inst)); + + // Note that the def-point of all arguments to an instruction + // in SSA lie on a line of direct ancestors in the domtree, and + // so do their available-blocks. This means that for any pair of + // arguments, their available blocks are either the same or one + // strictly dominates the other. We just need to find any argument + // whose available block is deepest in the domtree. + self.func.dfg.insts[inst] + .arguments(&self.func.dfg.value_lists) + .iter() + .map(|&v| { + let block = self.available_block[v]; + debug_assert!(!block.is_reserved_value()); + block + }) + .max_by(|&x, &y| { + if self.domtree.dominates(x, y) { + Ordering::Less + } else { + debug_assert!(self.domtree.dominates(y, x)); + Ordering::Greater + } + }) + .unwrap_or(self.func.layout.entry_block().unwrap()) + } + /// Optimizes an enode by applying any matching mid-end rewrite /// rules (or store-to-load forwarding, which is a special case), /// unioning together all possible optimized (or rewritten) forms @@ -212,7 +277,7 @@ where // A pure node always has exactly one result. let orig_value = self.func.dfg.first_result(inst); - let mut isle_ctx = IsleContext { ctx: self }; + let mut optimized_values = std::mem::take(&mut self.optimized_values); // Limit rewrite depth. When we apply optimization rules, they // may create new nodes (values) and those are, recursively, @@ -224,24 +289,80 @@ where // infinite or problematic recursion, we bound the rewrite // depth to a small constant here. const REWRITE_LIMIT: usize = 5; - if isle_ctx.ctx.rewrite_depth > REWRITE_LIMIT { - isle_ctx.ctx.stats.rewrite_depth_limit += 1; + if self.rewrite_depth > REWRITE_LIMIT { + self.stats.rewrite_depth_limit += 1; return orig_value; } - isle_ctx.ctx.rewrite_depth += 1; + self.rewrite_depth += 1; + trace!("Incrementing rewrite depth; now {}", self.rewrite_depth); // Invoke the ISLE toplevel constructor, getting all new // values produced as equivalents to this value. trace!("Calling into ISLE with original value {}", orig_value); - isle_ctx.ctx.stats.rewrite_rule_invoked += 1; - let mut optimized_values = - crate::opts::generated_code::constructor_simplify(&mut isle_ctx, orig_value); + self.stats.rewrite_rule_invoked += 1; + debug_assert!(optimized_values.is_empty()); + crate::opts::generated_code::constructor_simplify( + &mut IsleContext { ctx: self }, + orig_value, + &mut optimized_values, + ); + + optimized_values.push(orig_value); + + // Remove any values from optimized_values that do not have + // the highest possible available block in the domtree, in + // O(n) time. This loop scans in reverse, establishing the + // loop invariant that all values at indices >= idx have the + // same available block, which is the best available block + // seen so far. Note that orig_value must also be removed if + // it isn't in the best block, so we push it above, which means + // optimized_values is never empty: there's always at least one + // value in best_block. + let mut best_block = self.available_block[*optimized_values.last().unwrap()]; + for idx in (0..optimized_values.len() - 1).rev() { + // At the beginning of each iteration, there is a non-empty + // collection of values after idx, which are all available + // at best_block. + let this_block = self.available_block[optimized_values[idx]]; + if this_block != best_block { + if self.domtree.dominates(this_block, best_block) { + // If the available block for this value dominates + // the best block we've seen so far, discard all + // the values we already checked and leave only this + // value in the tail of the vector. + optimized_values.truncate(idx + 1); + best_block = this_block; + } else { + // Otherwise the tail of the vector contains values + // which are all better than this value, so we can + // swap any of them in place of this value to delete + // this one in O(1) time. + debug_assert!(self.domtree.dominates(best_block, this_block)); + optimized_values.swap_remove(idx); + debug_assert!(optimized_values.len() > idx); + } + } + } + + // It's not supposed to matter what order `simplify` returns values in. + self.ctrl_plane.shuffle(&mut optimized_values); + + let num_matches = optimized_values.len(); + if num_matches > MATCHES_LIMIT { + trace!( + "Reached maximum matches limit; too many optimized values \ + ({num_matches} > {MATCHES_LIMIT}); ignoring rest.", + ); + optimized_values.truncate(MATCHES_LIMIT); + } + + trace!(" -> returned from ISLE: {orig_value} -> {optimized_values:?}"); // Create a union of all new values with the original (or // maybe just one new value marked as "subsuming" the // original, if present.) - let mut union_value = orig_value; - while let Some(optimized_value) = optimized_values.next(&mut isle_ctx) { + let mut union_value = optimized_values.pop().unwrap(); + for optimized_value in optimized_values.drain(..) { trace!( "Returned from ISLE for {}, got {:?}", orig_value, @@ -251,32 +372,32 @@ where trace!(" -> same as orig value; skipping"); continue; } - if isle_ctx.ctx.subsume_values.contains(&optimized_value) { + if self.subsume_values.contains(&optimized_value) { // Merge in the unionfind so canonicalization // still works, but take *only* the subsuming // value, and break now. - isle_ctx.ctx.eclasses.union(optimized_value, union_value); + self.eclasses.union(optimized_value, union_value); + self.func.dfg.merge_facts(optimized_value, union_value); union_value = optimized_value; break; } let old_union_value = union_value; - union_value = isle_ctx - .ctx - .func - .dfg - .union(old_union_value, optimized_value); - isle_ctx.ctx.stats.union += 1; + union_value = self.func.dfg.union(old_union_value, optimized_value); + self.available_block[union_value] = best_block; + self.stats.union += 1; trace!(" -> union: now {}", union_value); - isle_ctx.ctx.eclasses.add(union_value); - isle_ctx - .ctx - .eclasses - .union(old_union_value, optimized_value); - isle_ctx.ctx.eclasses.union(old_union_value, union_value); + self.eclasses.add(union_value); + self.eclasses.union(old_union_value, optimized_value); + self.func.dfg.merge_facts(old_union_value, optimized_value); + self.eclasses.union(old_union_value, union_value); } - isle_ctx.ctx.rewrite_depth -= 1; + self.rewrite_depth -= 1; + trace!("Decrementing rewrite depth; now {}", self.rewrite_depth); + + debug_assert!(self.optimized_values.is_empty()); + self.optimized_values = optimized_values; union_value } @@ -287,6 +408,10 @@ where fn optimize_skeleton_inst(&mut self, inst: Inst) -> bool { self.stats.skeleton_inst += 1; + for &result in self.func.dfg.inst_results(inst) { + self.available_block[result] = self.func.layout.inst_block(inst).unwrap(); + } + // First, can we try to deduplicate? We need to keep some copy // of the instruction around because it's side-effecting, but // we may be able to reuse an earlier instance of it. @@ -307,13 +432,12 @@ where let ty = self.func.dfg.ctrl_typevar(inst); match self .effectful_gvn_map - .entry((ty, self.func.dfg.insts[inst].clone())) + .entry((ty, self.func.dfg.insts[inst])) { ScopedEntry::Occupied(o) => { let orig_result = *o.get(); // Hit in GVN map -- reuse value. self.value_to_opt_value[result] = orig_result; - self.eclasses.union(orig_result, result); trace!(" -> merges result {} to {}", result, orig_result); true } @@ -342,6 +466,7 @@ where new_result ); self.value_to_opt_value[result] = new_result; + self.func.dfg.merge_facts(result, new_result); true } // Otherwise, generic side-effecting op -- always keep it, and @@ -356,24 +481,45 @@ where false } } + + /// 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> { /// Create a new EgraphPass. pub fn new( func: &'a mut Function, - domtree: &'a DominatorTree, + raw_domtree: &'a DominatorTree, loop_analysis: &'a LoopAnalysis, alias_analysis: &'a mut AliasAnalysis<'a>, + flags: &'a Flags, + ctrl_plane: &'a mut ControlPlane, ) -> Self { let num_values = func.dfg.num_values(); - let domtree_children = DomTreeWithChildren::new(func, domtree); + let mut domtree = DominatorTreePreorder::new(); + domtree.compute(raw_domtree, &func.layout); Self { func, domtree, - domtree_children, loop_analysis, alias_analysis, + flags, + ctrl_plane, stats: Stats::default(), eclasses: UnionFind::with_capacity(num_values), remat_values: FxHashSet::default(), @@ -396,7 +542,8 @@ impl<'a> EgraphPass<'a> { } } } - trace!("stats: {:?}", self.stats); + trace!("stats: {:#?}", self.stats); + trace!("pinned_union_count: {}", self.eclasses.pinned_union_count); self.elaborate(); } @@ -455,9 +602,23 @@ impl<'a> EgraphPass<'a> { let mut effectful_gvn_map: ScopedHashMap<(Type, InstructionData), Value> = ScopedHashMap::new(); + // We assign an "available block" to every value. Values tied to + // the side-effecting skeleton are available in the block where + // they're defined. Results from pure instructions could legally + // float up the domtree so they are available as soon as all + // their arguments are available. Values which identify union + // nodes are available in the same block as all values in the + // eclass, enforced by optimize_pure_enode. + let mut available_block: SecondaryMap = + SecondaryMap::with_default(Block::reserved_value()); + // This is an initial guess at the size we'll need, but we add + // more values as we build simplified alternative expressions so + // this is likely to realloc again later. + available_block.resize(cursor.func.dfg.num_values()); + // In domtree preorder, visit blocks. (TODO: factor out an // iterator from this and elaborator.) - let root = self.domtree_children.root(); + let root = cursor.layout().entry_block().unwrap(); enum StackEntry { Visit(Block), Pop, @@ -469,8 +630,11 @@ impl<'a> EgraphPass<'a> { // We popped this block; push children // immediately, then process this block. block_stack.push(StackEntry::Pop); - block_stack - .extend(self.domtree_children.children(block).map(StackEntry::Visit)); + block_stack.extend( + self.ctrl_plane + .shuffled(self.domtree.children(block)) + .map(StackEntry::Visit), + ); effectful_gvn_map.increment_depth(); trace!("Processing block {}", block); @@ -482,6 +646,7 @@ impl<'a> EgraphPass<'a> { trace!("creating initial singleton eclass for blockparam {}", param); self.eclasses.add(param); value_to_opt_value[param] = param; + available_block[param] = block; } while let Some(inst) = cursor.next_inst() { trace!("Processing inst {}", inst); @@ -497,8 +662,7 @@ impl<'a> EgraphPass<'a> { // Rewrite args of *all* instructions using the // value-to-opt-value map. - cursor.func.dfg.resolve_aliases_in_arguments(inst); - cursor.func.dfg.map_inst_values(inst, |_, arg| { + cursor.func.dfg.map_inst_values(inst, |arg| { let new_value = value_to_opt_value[arg]; trace!("rewriting arg {} of inst {} to {}", arg, inst, new_value); debug_assert_ne!(new_value, Value::reserved_value()); @@ -515,13 +679,18 @@ impl<'a> EgraphPass<'a> { value_to_opt_value: &mut value_to_opt_value, gvn_map: &mut gvn_map, effectful_gvn_map: &mut effectful_gvn_map, + available_block: &mut available_block, eclasses: &mut self.eclasses, rewrite_depth: 0, subsume_values: FxHashSet::default(), remat_values: &mut self.remat_values, stats: &mut self.stats, + 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(), }; if is_pure_for_egraph(ctx.func, inst) { @@ -574,12 +743,11 @@ impl<'a> EgraphPass<'a> { fn elaborate(&mut self) { let mut elaborator = Elaborator::new( self.func, - self.domtree, - &self.domtree_children, + &self.domtree, self.loop_analysis, &mut self.remat_values, - &mut self.eclasses, &mut self.stats, + self.ctrl_plane, ); elaborator.elaborate(); @@ -601,7 +769,7 @@ impl<'a> EgraphPass<'a> { debug_assert!(self.func.layout.inst_block(i).is_some()); } ValueDef::Union(..) => { - panic!("egraph union node {} still reachable at {}!", arg, inst); + panic!("egraph union node {arg} still reachable at {inst}!"); } _ => {} }) @@ -658,9 +826,10 @@ pub(crate) struct Stats { pub(crate) elaborate_visit_node: u64, pub(crate) elaborate_memoize_hit: u64, pub(crate) elaborate_memoize_miss: u64, - pub(crate) elaborate_memoize_miss_remat: u64, + pub(crate) elaborate_remat: u64, pub(crate) elaborate_licm_hoist: u64, pub(crate) elaborate_func: u64, pub(crate) elaborate_func_pre_insts: u64, pub(crate) elaborate_func_post_insts: u64, + pub(crate) elaborate_best_cost_fixpoint_iters: u64, } diff --git a/cranelift/codegen/src/egraph/cost.rs b/cranelift/codegen/src/egraph/cost.rs index 9cfb0894ca74..28aab40e9740 100644 --- a/cranelift/codegen/src/egraph/cost.rs +++ b/cranelift/codegen/src/egraph/cost.rs @@ -30,14 +30,51 @@ use crate::ir::Opcode; /// `finite()` method.) An infinite cost is used to represent a value /// that cannot be computed, or otherwise serve as a sentinel when /// performing search for the lowest-cost representation of a value. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, PartialEq, Eq)] pub(crate) struct Cost(u32); -impl Cost { - pub(crate) fn at_level(&self, loop_level: usize) -> Cost { - let loop_level = std::cmp::min(2, loop_level); - let multiplier = 1u32 << ((10 * loop_level) as u32); - Cost(self.0.saturating_mul(multiplier)).finite() + +impl core::fmt::Debug for Cost { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + if *self == Cost::infinity() { + write!(f, "Cost::Infinite") + } else { + f.debug_struct("Cost::Finite") + .field("op_cost", &self.op_cost()) + .field("depth", &self.depth()) + .finish() + } + } +} + +impl Ord for Cost { + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + // We make sure that the high bits are the op cost and the low bits are + // the depth. This means that we can use normal integer comparison to + // order by op cost and then depth. + // + // We want to break op cost ties with depth (rather than the other way + // around). When the op cost is the same, we prefer shallow and wide + // expressions to narrow and deep expressions and breaking ties with + // `depth` gives us that. For example, `(a + b) + (c + d)` is preferred + // to `((a + b) + c) + d`. This is beneficial because it exposes more + // instruction-level parallelism and shortens live ranges. + self.0.cmp(&other.0) } +} + +impl PartialOrd for Cost { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Cost { + const DEPTH_BITS: u8 = 8; + const DEPTH_MASK: u32 = (1 << Self::DEPTH_BITS) - 1; + const OP_COST_MASK: u32 = !Self::DEPTH_MASK; + const MAX_OP_COST: u32 = Self::OP_COST_MASK >> Self::DEPTH_BITS; pub(crate) fn infinity() -> Cost { // 2^32 - 1 is, uh, pretty close to infinite... (we use `Cost` @@ -49,11 +86,40 @@ impl Cost { Cost(0) } - /// Clamp this cost at a "finite" value. Can be used in - /// conjunction with saturating ops to avoid saturating into - /// `infinity()`. - fn finite(self) -> Cost { - Cost(std::cmp::min(u32::MAX - 1, self.0)) + /// Construct a new `Cost` from the given parts. + /// + /// If the opcode cost is greater than or equal to the maximum representable + /// opcode cost, then the resulting `Cost` saturates to infinity. + fn new(opcode_cost: u32, depth: u8) -> Cost { + if opcode_cost >= Self::MAX_OP_COST { + Self::infinity() + } else { + Cost(opcode_cost << Self::DEPTH_BITS | u32::from(depth)) + } + } + + fn depth(&self) -> u8 { + let depth = self.0 & Self::DEPTH_MASK; + u8::try_from(depth).unwrap() + } + + fn op_cost(&self) -> u32 { + (self.0 & Self::OP_COST_MASK) >> Self::DEPTH_BITS + } + + /// Compute the cost of the operation and its given operands. + /// + /// Caller is responsible for checking that the opcode came from an instruction + /// that satisfies `inst_predicates::is_pure_for_egraph()`. + pub(crate) fn of_pure_op(op: Opcode, operand_costs: impl IntoIterator) -> Self { + let c = pure_op_cost(op) + operand_costs.into_iter().sum(); + Cost::new(c.op_cost(), c.depth().saturating_add(1)) + } +} + +impl std::iter::Sum for Cost { + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), |a, b| a + b) } } @@ -65,22 +131,28 @@ impl std::default::Default for Cost { impl std::ops::Add for Cost { type Output = Cost; + fn add(self, other: Cost) -> Cost { - Cost(self.0.saturating_add(other.0)).finite() + let op_cost = self.op_cost().saturating_add(other.op_cost()); + let depth = std::cmp::max(self.depth(), other.depth()); + Cost::new(op_cost, depth) } } -/// Return the cost of a *pure* opcode. Caller is responsible for -/// checking that the opcode came from an instruction that satisfies -/// `inst_predicates::is_pure_for_egraph()`. -pub(crate) fn pure_op_cost(op: Opcode) -> Cost { +/// Return the cost of a *pure* opcode. +/// +/// Caller is responsible for checking that the opcode came from an instruction +/// that satisfies `inst_predicates::is_pure_for_egraph()`. +fn pure_op_cost(op: Opcode) -> Cost { match op { // Constants. - Opcode::Iconst | Opcode::F32const | Opcode::F64const => Cost(0), + Opcode::Iconst | Opcode::F32const | Opcode::F64const => Cost::new(1, 0), + // Extends/reduces. Opcode::Uextend | Opcode::Sextend | Opcode::Ireduce | Opcode::Iconcat | Opcode::Isplit => { - Cost(1) + Cost::new(2, 0) } + // "Simple" arithmetic. Opcode::Iadd | Opcode::Isub @@ -90,8 +162,52 @@ pub(crate) fn pure_op_cost(op: Opcode) -> Cost { | Opcode::Bnot | Opcode::Ishl | Opcode::Ushr - | Opcode::Sshr => Cost(2), + | Opcode::Sshr => Cost::new(3, 0), + // Everything else (pure.) - _ => Cost(3), + _ => Cost::new(4, 0), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn add_cost() { + let a = Cost::new(5, 2); + let b = Cost::new(37, 3); + assert_eq!(a + b, Cost::new(42, 3)); + assert_eq!(b + a, Cost::new(42, 3)); + } + + #[test] + fn add_infinity() { + let a = Cost::new(5, 2); + let b = Cost::infinity(); + assert_eq!(a + b, Cost::infinity()); + assert_eq!(b + a, Cost::infinity()); + } + + #[test] + fn op_cost_saturates_to_infinity() { + let a = Cost::new(Cost::MAX_OP_COST - 10, 2); + let b = Cost::new(11, 2); + assert_eq!(a + b, Cost::infinity()); + assert_eq!(b + a, Cost::infinity()); + } + + #[test] + fn depth_saturates_to_max_depth() { + let a = Cost::new(10, u8::MAX); + let b = Cost::new(10, 1); + assert_eq!( + Cost::of_pure_op(Opcode::Iconst, [a, b]), + Cost::new(21, u8::MAX) + ); + assert_eq!( + Cost::of_pure_op(Opcode::Iconst, [b, a]), + Cost::new(21, u8::MAX) + ); } } diff --git a/cranelift/codegen/src/egraph/domtree.rs b/cranelift/codegen/src/egraph/domtree.rs deleted file mode 100644 index f0af89e2a244..000000000000 --- a/cranelift/codegen/src/egraph/domtree.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! Extended domtree with various traversal support. - -use crate::dominator_tree::DominatorTree; -use crate::ir::{Block, Function}; -use cranelift_entity::{packed_option::PackedOption, SecondaryMap}; - -#[derive(Clone, Debug)] -pub(crate) struct DomTreeWithChildren { - nodes: SecondaryMap, - root: Block, -} - -#[derive(Clone, Copy, Debug, Default)] -struct DomTreeNode { - children: PackedOption, - next: PackedOption, -} - -impl DomTreeWithChildren { - pub(crate) fn new(func: &Function, domtree: &DominatorTree) -> DomTreeWithChildren { - let mut nodes: SecondaryMap = - SecondaryMap::with_capacity(func.dfg.num_blocks()); - - for block in func.layout.blocks() { - let idom_inst = match domtree.idom(block) { - Some(idom_inst) => idom_inst, - None => continue, - }; - let idom = func - .layout - .inst_block(idom_inst) - .expect("Dominating instruction should be part of a block"); - - nodes[block].next = nodes[idom].children; - nodes[idom].children = block.into(); - } - - let root = func.layout.entry_block().unwrap(); - - Self { nodes, root } - } - - pub(crate) fn root(&self) -> Block { - self.root - } - - pub(crate) fn children<'a>(&'a self, block: Block) -> DomTreeChildIter<'a> { - let block = self.nodes[block].children; - DomTreeChildIter { - domtree: self, - block, - } - } -} - -pub(crate) struct DomTreeChildIter<'a> { - domtree: &'a DomTreeWithChildren, - block: PackedOption, -} - -impl<'a> Iterator for DomTreeChildIter<'a> { - type Item = Block; - fn next(&mut self) -> Option { - self.block.expand().map(|block| { - self.block = self.domtree.nodes[block].next; - block - }) - } -} diff --git a/cranelift/codegen/src/egraph/elaborate.rs b/cranelift/codegen/src/egraph/elaborate.rs index 6acf83cba9ae..a35c2ac25734 100644 --- a/cranelift/codegen/src/egraph/elaborate.rs +++ b/cranelift/codegen/src/egraph/elaborate.rs @@ -1,26 +1,25 @@ //! Elaboration phase: lowers EGraph back to sequences of operations //! in CFG nodes. -use super::cost::{pure_op_cost, Cost}; -use super::domtree::DomTreeWithChildren; +use super::cost::Cost; use super::Stats; -use crate::dominator_tree::DominatorTree; -use crate::fx::FxHashSet; +use crate::dominator_tree::DominatorTreePreorder; +use crate::hash_map::Entry as HashEntry; +use crate::inst_predicates::is_pure_for_egraph; use crate::ir::{Block, Function, Inst, Value, ValueDef}; -use crate::loop_analysis::{Loop, LoopAnalysis, LoopLevel}; +use crate::loop_analysis::{Loop, LoopAnalysis}; use crate::scoped_hash_map::ScopedHashMap; use crate::trace; -use crate::unionfind::UnionFind; use alloc::vec::Vec; +use cranelift_control::ControlPlane; use cranelift_entity::{packed_option::ReservedValue, SecondaryMap}; +use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; pub(crate) struct Elaborator<'a> { func: &'a mut Function, - domtree: &'a DominatorTree, - domtree_children: &'a DomTreeWithChildren, + domtree: &'a DominatorTreePreorder, loop_analysis: &'a LoopAnalysis, - eclasses: &'a mut UnionFind, /// Map from Value that is produced by a pure Inst (and was thus /// not in the side-effecting skeleton) to the value produced by /// an elaborated inst (placed in the layout) to whose results we @@ -38,10 +37,15 @@ pub(crate) struct Elaborator<'a> { /// is already placed in the Layout. If so, we duplicate, and /// insert non-identity mappings from the original inst's results /// to the cloned inst's results. + /// + /// Note that as values may refer to unions that represent a subset + /// of a larger eclass, it's not valid to walk towards the root of a + /// union tree: doing so would potentially equate values that fall + /// on different branches of the dominator tree. value_to_elaborated_value: ScopedHashMap, /// Map from Value to the best (lowest-cost) Value in its eclass /// (tree of union value-nodes). - value_to_best_value: SecondaryMap, + value_to_best_value: SecondaryMap, /// Stack of blocks and loops in current elaboration path. loop_stack: SmallVec<[LoopStackEntry; 8]>, /// The current block into which we are elaborating. @@ -56,9 +60,36 @@ pub(crate) struct Elaborator<'a> { elab_result_stack: Vec, /// Explicitly-unrolled block elaboration stack. block_stack: Vec, + /// Copies of values that have been rematerialized. + remat_copies: FxHashMap<(Block, Value), Value>, /// Stats for various events during egraph processing, to help /// with optimization of this infrastructure. stats: &'a mut Stats, + /// 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, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct BestEntry(Cost, Value); + +impl PartialOrd for BestEntry { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for BestEntry { + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.cmp(&other.0).then_with(|| { + // Note that this comparison is reversed. When costs are equal, + // prefer the value with the bigger index. This is a heuristic that + // prefers results of rewrites to the original value, since we + // expect that our rewrites are generally improvements. + self.1.cmp(&other.1).reverse() + }) + } } #[derive(Clone, Copy, Debug)] @@ -95,7 +126,6 @@ enum ElabStackEntry { inst: Inst, result_idx: usize, num_args: usize, - remat: bool, before: Inst, }, } @@ -109,23 +139,20 @@ enum BlockStackEntry { impl<'a> Elaborator<'a> { pub(crate) fn new( func: &'a mut Function, - domtree: &'a DominatorTree, - domtree_children: &'a DomTreeWithChildren, + domtree: &'a DominatorTreePreorder, loop_analysis: &'a LoopAnalysis, remat_values: &'a FxHashSet, - eclasses: &'a mut UnionFind, stats: &'a mut Stats, + ctrl_plane: &'a mut ControlPlane, ) -> Self { let num_values = func.dfg.num_values(); let mut value_to_best_value = - SecondaryMap::with_default((Cost::infinity(), Value::reserved_value())); + SecondaryMap::with_default(BestEntry(Cost::infinity(), Value::reserved_value())); value_to_best_value.resize(num_values); Self { func, domtree, - domtree_children, loop_analysis, - eclasses, value_to_elaborated_value: ScopedHashMap::with_capacity(num_values), value_to_best_value, loop_stack: smallvec![], @@ -134,7 +161,9 @@ impl<'a> Elaborator<'a> { elab_stack: vec![], elab_result_stack: vec![], block_stack: vec![], + remat_copies: FxHashMap::default(), stats, + ctrl_plane, } } @@ -191,51 +220,137 @@ impl<'a> Elaborator<'a> { fn compute_best_values(&mut self) { let best = &mut self.value_to_best_value; - for (value, def) in self.func.dfg.values_and_defs() { - trace!("computing best for value {:?} def {:?}", value, def); - match def { - ValueDef::Union(x, y) => { - // Pick the best of the two options based on - // min-cost. This works because each element of `best` - // is a `(cost, value)` tuple; `cost` comes first so - // the natural comparison works based on cost, and - // breaks ties based on value number. - trace!(" -> best of {:?} and {:?}", best[x], best[y]); - best[value] = std::cmp::min(best[x], best[y]); - trace!(" -> {:?}", best[value]); - } - ValueDef::Param(_, _) => { - best[value] = (Cost::zero(), value); - } - // If the Inst is inserted into the layout (which is, - // at this point, only the side-effecting skeleton), - // then it must be computed and thus we give it zero - // cost. - ValueDef::Result(inst, _) if self.func.layout.inst_block(inst).is_some() => { - best[value] = (Cost::zero(), value); - } - ValueDef::Result(inst, _) => { - trace!(" -> value {}: result, computing cost", value); - let inst_data = &self.func.dfg.insts[inst]; - let loop_level = self - .func - .layout - .inst_block(inst) - .map(|block| self.loop_analysis.loop_level(block)) - .unwrap_or(LoopLevel::root()); - // N.B.: at this point we know that the opcode is - // pure, so `pure_op_cost`'s precondition is - // satisfied. - let cost = self.func.dfg.inst_values(inst).fold( - pure_op_cost(inst_data.opcode()).at_level(loop_level.level()), - |cost, value| cost + best[value].0, - ); - best[value] = (cost, value); - } - }; - debug_assert_ne!(best[value].0, Cost::infinity()); - debug_assert_ne!(best[value].1, Value::reserved_value()); - trace!("best for eclass {:?}: {:?}", value, best[value]); + + // We can't make random decisions inside the fixpoint loop below because + // that could cause values to change on every iteration of the loop, + // which would make the loop never terminate. So in chaos testing + // mode we need a form of making suboptimal decisions that is fully + // deterministic. We choose to simply make the worst decision we know + // how to do instead of the best. + let use_worst = self.ctrl_plane.get_decision(); + + // Do a fixpoint loop to compute the best value for each eclass. + // + // The maximum number of iterations is the length of the longest chain + // of `vNN -> vMM` edges in the dataflow graph where `NN < MM`, so this + // is *technically* quadratic, but `cranelift-frontend` won't construct + // any such edges. NaN canonicalization will introduce some of these + // edges, but they are chains of only two or three edges. So in + // practice, we *never* do more than a handful of iterations here unless + // (a) we parsed the CLIF from text and the text was funkily numbered, + // which we don't really care about, or (b) the CLIF producer did + // something weird, in which case it is their responsibility to stop + // doing that. + trace!( + "Entering fixpoint loop to compute the {} values for each eclass", + if use_worst { + "worst (chaos mode)" + } else { + "best" + } + ); + let mut keep_going = true; + while keep_going { + keep_going = false; + trace!( + "fixpoint iteration {}", + self.stats.elaborate_best_cost_fixpoint_iters + ); + self.stats.elaborate_best_cost_fixpoint_iters += 1; + + for (value, def) in self.func.dfg.values_and_defs() { + trace!("computing best for value {:?} def {:?}", value, def); + let orig_best_value = best[value]; + + match def { + ValueDef::Union(x, y) => { + // Pick the best of the two options based on + // min-cost. This works because each element of `best` + // is a `(cost, value)` tuple; `cost` comes first so + // the natural comparison works based on cost, and + // breaks ties based on value number. + best[value] = if use_worst { + if best[x].1.is_reserved_value() { + best[y] + } else if best[y].1.is_reserved_value() { + best[x] + } else { + std::cmp::max(best[x], best[y]) + } + } else { + std::cmp::min(best[x], best[y]) + }; + trace!( + " -> best of union({:?}, {:?}) = {:?}", + best[x], + best[y], + best[value] + ); + } + ValueDef::Param(_, _) => { + best[value] = BestEntry(Cost::zero(), value); + } + // If the Inst is inserted into the layout (which is, + // at this point, only the side-effecting skeleton), + // then it must be computed and thus we give it zero + // cost. + ValueDef::Result(inst, _) => { + if let Some(_) = self.func.layout.inst_block(inst) { + best[value] = BestEntry(Cost::zero(), value); + } else { + let inst_data = &self.func.dfg.insts[inst]; + // N.B.: at this point we know that the opcode is + // pure, so `pure_op_cost`'s precondition is + // satisfied. + let cost = Cost::of_pure_op( + inst_data.opcode(), + self.func.dfg.inst_values(inst).map(|value| best[value].0), + ); + best[value] = BestEntry(cost, value); + trace!(" -> cost of value {} = {:?}", value, cost); + } + } + }; + + // Keep on iterating the fixpoint loop while we are finding new + // best values. + keep_going |= orig_best_value != best[value]; + } + } + + if cfg!(any(feature = "trace-log", debug_assertions)) { + trace!("finished fixpoint loop to compute best value for each eclass"); + for value in self.func.dfg.values() { + trace!("-> best for eclass {:?}: {:?}", value, best[value]); + debug_assert_ne!(best[value].1, Value::reserved_value()); + // You might additionally be expecting an assert that the best + // cost is not infinity, however infinite cost *can* happen in + // practice. First, note that our cost function doesn't know + // about any shared structure in the dataflow graph, it only + // sums operand costs. (And trying to avoid that by deduping a + // single operation's operands is a losing game because you can + // always just add one indirection and go from `add(x, x)` to + // `add(foo(x), bar(x))` to hide the shared structure.) Given + // that blindness to sharing, we can make cost grow + // exponentially with a linear sequence of operations: + // + // v0 = iconst.i32 1 ;; cost = 1 + // v1 = iadd v0, v0 ;; cost = 3 + 1 + 1 + // v2 = iadd v1, v1 ;; cost = 3 + 5 + 5 + // v3 = iadd v2, v2 ;; cost = 3 + 13 + 13 + // v4 = iadd v3, v3 ;; cost = 3 + 29 + 29 + // v5 = iadd v4, v4 ;; cost = 3 + 61 + 61 + // v6 = iadd v5, v5 ;; cost = 3 + 125 + 125 + // ;; etc... + // + // Such a chain can cause cost to saturate to infinity. How do + // we choose which e-node is best when there are multiple that + // have saturated to infinity? It doesn't matter. As long as + // invariant (2) for optimization rules is upheld by our rule + // set (see `cranelift/codegen/src/opts/README.md`) it is safe + // to choose *any* e-node in the e-class. At worst we will + // produce suboptimal code, but never an incorrectness. + } } } @@ -258,64 +373,69 @@ impl<'a> Elaborator<'a> { self.elab_result_stack.pop().unwrap() } + /// Possibly rematerialize the instruction producing the value in + /// `arg` and rewrite `arg` to refer to it, if needed. Returns + /// `true` if a rewrite occurred. + fn maybe_remat_arg( + remat_values: &FxHashSet, + func: &mut Function, + remat_copies: &mut FxHashMap<(Block, Value), Value>, + insert_block: Block, + before: Inst, + arg: &mut ElaboratedValue, + stats: &mut Stats, + ) -> bool { + // TODO (#7313): we may want to consider recursive + // rematerialization as well. We could process the arguments of + // the rematerialized instruction up to a certain depth. This + // would affect, e.g., adds-with-one-constant-arg, which are + // currently rematerialized. Right now we don't do this, to + // avoid the need for another fixpoint loop here. + if arg.in_block != insert_block && remat_values.contains(&arg.value) { + let new_value = match remat_copies.entry((insert_block, arg.value)) { + HashEntry::Occupied(o) => *o.get(), + HashEntry::Vacant(v) => { + let inst = func.dfg.value_def(arg.value).inst().unwrap(); + debug_assert_eq!(func.dfg.inst_results(inst).len(), 1); + let new_inst = func.dfg.clone_inst(inst); + func.layout.insert_inst(new_inst, before); + let new_result = func.dfg.inst_results(new_inst)[0]; + *v.insert(new_result) + } + }; + trace!("rematerialized {} as {}", arg.value, new_value); + arg.value = new_value; + stats.elaborate_remat += 1; + true + } else { + false + } + } + fn process_elab_stack(&mut self) { - while let Some(entry) = self.elab_stack.last() { + while let Some(entry) = self.elab_stack.pop() { match entry { - &ElabStackEntry::Start { value, before } => { - // We always replace the Start entry, so pop it now. - self.elab_stack.pop(); - - debug_assert_ne!(value, Value::reserved_value()); - let value = self.func.dfg.resolve_aliases(value); + ElabStackEntry::Start { value, before } => { + debug_assert!(self.func.dfg.value_is_real(value)); self.stats.elaborate_visit_node += 1; - let canonical_value = self.eclasses.find_and_update(value); - debug_assert_ne!(canonical_value, Value::reserved_value()); - trace!( - "elaborate: value {} canonical {} before {}", - value, - canonical_value, - before - ); // Get the best option; we use `value` (latest // value) here so we have a full view of the // eclass. trace!("looking up best value for {}", value); - let (_, best_value) = self.value_to_best_value[value]; - debug_assert_ne!(best_value, Value::reserved_value()); + let BestEntry(_, best_value) = self.value_to_best_value[value]; trace!("elaborate: value {} -> best {}", value, best_value); + debug_assert_ne!(best_value, Value::reserved_value()); + + if let Some(elab_val) = self.value_to_elaborated_value.get(&best_value) { + // Value is available; use it. + trace!("elaborate: value {} -> {:?}", value, elab_val); + self.stats.elaborate_memoize_hit += 1; + self.elab_result_stack.push(*elab_val); + continue; + } - let remat = if let Some(elab_val) = - self.value_to_elaborated_value.get(&canonical_value) - { - // Value is available. Look at the defined - // block, and determine whether this node kind - // allows rematerialization if the value comes - // from another block. If so, ignore the hit - // and recompute below. - let remat = elab_val.in_block != self.cur_block - && self.remat_values.contains(&best_value); - if !remat { - trace!("elaborate: value {} -> {:?}", value, elab_val); - self.stats.elaborate_memoize_hit += 1; - self.elab_result_stack.push(*elab_val); - continue; - } - trace!("elaborate: value {} -> remat", canonical_value); - self.stats.elaborate_memoize_miss_remat += 1; - // The op is pure at this point, so it is always valid to - // remove from this map. - self.value_to_elaborated_value.remove(&canonical_value); - true - } else { - // Value not available; but still look up - // whether it's been flagged for remat because - // this affects placement. - let remat = self.remat_values.contains(&best_value); - trace!(" -> not present in map; remat = {}", remat); - remat - }; self.stats.elaborate_memoize_miss += 1; // Now resolve the value to its definition to see @@ -363,7 +483,6 @@ impl<'a> Elaborator<'a> { inst, result_idx, num_args, - remat, before, }); @@ -376,21 +495,17 @@ impl<'a> Elaborator<'a> { } } - &ElabStackEntry::PendingInst { + ElabStackEntry::PendingInst { inst, result_idx, num_args, - remat, before, } => { - self.elab_stack.pop(); - trace!( - "PendingInst: {} result {} args {} remat {} before {}", + "PendingInst: {} result {} args {} before {}", inst, result_idx, num_args, - remat, before ); @@ -398,7 +513,7 @@ impl<'a> Elaborator<'a> { // point. Grab them and drain them out, removing // them. let arg_idx = self.elab_result_stack.len() - num_args; - let arg_values = &self.elab_result_stack[arg_idx..]; + let arg_values = &mut self.elab_result_stack[arg_idx..]; // Compute max loop depth. // @@ -444,16 +559,15 @@ impl<'a> Elaborator<'a> { // We know that this is a pure inst, because // non-pure roots have already been placed in the - // value-to-elab'd-value map and are never subject - // to remat, so they will not reach this stage of - // processing. + // value-to-elab'd-value map, so they will not + // reach this stage of processing. // // We now must determine the location at which we // place the instruction. This is the current // block *unless* we hoist above a loop when all // args are loop-invariant (and this op is pure). let (scope_depth, before, insert_block) = - if loop_hoist_level == self.loop_stack.len() || remat { + if loop_hoist_level == self.loop_stack.len() { // Depends on some value at the current // loop depth, or remat forces it here: // place it at the current location. @@ -469,11 +583,7 @@ impl<'a> Elaborator<'a> { let data = &self.loop_stack[loop_hoist_level]; // `data.hoist_block` should dominate `before`'s block. let before_block = self.func.layout.inst_block(before).unwrap(); - debug_assert!(self.domtree.dominates( - data.hoist_block, - before_block, - &self.func.layout - )); + debug_assert!(self.domtree.dominates(data.hoist_block, before_block)); // Determine the instruction at which we // insert in `data.hoist_block`. let before = self.func.layout.last_inst(data.hoist_block).unwrap(); @@ -486,16 +596,39 @@ impl<'a> Elaborator<'a> { insert_block ); - // Now we need to place `inst` at the computed - // location (just before `before`). Note that - // `inst` may already have been placed somewhere - // else, because a pure node may be elaborated at - // more than one place. In this case, we need to - // duplicate the instruction (and return the - // `Value`s for that duplicated instance - // instead). + // Now that we have the location for the + // instruction, check if any of its args are remat + // values. If so, and if we don't have a copy of + // the rematerializing instruction for this block + // yet, create one. + let mut remat_arg = false; + for arg_value in arg_values.iter_mut() { + if Self::maybe_remat_arg( + &self.remat_values, + &mut self.func, + &mut self.remat_copies, + insert_block, + before, + arg_value, + &mut self.stats, + ) { + remat_arg = true; + } + } + + // Now we need to place `inst` at the computed + // location (just before `before`). Note that + // `inst` may already have been placed somewhere + // else, because a pure node may be elaborated at + // more than one place. In this case, we need to + // duplicate the instruction (and return the + // `Value`s for that duplicated instance instead). + // + // Also clone if we rematerialized, because we + // don't want to rewrite the args in the original + // copy. trace!("need inst {} before {}", inst, before); - let inst = if self.func.layout.inst_block(inst).is_some() { + let inst = if self.func.layout.inst_block(inst).is_some() || remat_arg { // Clone the inst! let new_inst = self.func.dfg.clone_inst(inst); trace!( @@ -517,16 +650,14 @@ impl<'a> Elaborator<'a> { value: new_result, in_block: insert_block, }; - let canonical_result = self.eclasses.find_and_update(result); + let best_result = self.value_to_best_value[result]; self.value_to_elaborated_value.insert_if_absent_with_depth( - canonical_result, + best_result.1, elab_value, scope_depth, ); - self.eclasses.add(new_result); - self.eclasses.union(result, new_result); - self.value_to_best_value[new_result] = self.value_to_best_value[result]; + self.value_to_best_value[new_result] = best_result; trace!( " -> cloned inst has new result {} for orig {}", @@ -545,9 +676,9 @@ impl<'a> Elaborator<'a> { value: result, in_block: insert_block, }; - let canonical_result = self.eclasses.find_and_update(result); + let best_result = self.value_to_best_value[result]; self.value_to_elaborated_value.insert_if_absent_with_depth( - canonical_result, + best_result.1, elab_value, scope_depth, ); @@ -555,7 +686,13 @@ impl<'a> Elaborator<'a> { } inst }; + // Place the inst just before `before`. + assert!( + is_pure_for_egraph(self.func, inst), + "something has gone very wrong if we are elaborating effectful \ + instructions, they should have remained in the skeleton" + ); self.func.layout.insert_inst(inst, before); // Update the inst's arguments. @@ -612,7 +749,16 @@ impl<'a> Elaborator<'a> { // Elaborate the arg, placing any newly-inserted insts // before `before`. Get the updated value, which may // be different than the original. - let new_arg = self.elaborate_eclass_use(*arg, before); + let mut new_arg = self.elaborate_eclass_use(*arg, before); + Self::maybe_remat_arg( + &self.remat_values, + &mut self.func, + &mut self.remat_copies, + block, + inst, + &mut new_arg, + &mut self.stats, + ); trace!(" -> rewrote arg to {:?}", new_arg); *arg = new_arg.value; } @@ -624,9 +770,9 @@ impl<'a> Elaborator<'a> { // map now. for &result in self.func.dfg.inst_results(inst) { trace!(" -> result {}", result); - let canonical_result = self.eclasses.find_and_update(result); + let best_result = self.value_to_best_value[result]; self.value_to_elaborated_value.insert_if_absent( - canonical_result, + best_result.1, ElaboratedValue { in_block: block, value: result, @@ -638,10 +784,9 @@ impl<'a> Elaborator<'a> { } } - fn elaborate_domtree(&mut self, domtree: &DomTreeWithChildren) { - let root = domtree.root(); + fn elaborate_domtree(&mut self, domtree: &DominatorTreePreorder) { self.block_stack.push(BlockStackEntry::Elaborate { - block: root, + block: self.func.layout.entry_block().unwrap(), idom: None, }); @@ -661,7 +806,7 @@ impl<'a> Elaborator<'a> { // traversal so we do this after processing this // block above. let block_stack_end = self.block_stack.len(); - for child in domtree.children(block) { + for child in self.ctrl_plane.shuffled(domtree.children(block)) { self.block_stack.push(BlockStackEntry::Elaborate { block: child, idom: Some(block), @@ -684,7 +829,7 @@ impl<'a> Elaborator<'a> { self.stats.elaborate_func += 1; self.stats.elaborate_func_pre_insts += self.func.dfg.num_insts() as u64; self.compute_best_values(); - self.elaborate_domtree(&self.domtree_children); + self.elaborate_domtree(&self.domtree); self.stats.elaborate_func_post_insts += self.func.dfg.num_insts() as u64; } } diff --git a/cranelift/codegen/src/flowgraph.rs b/cranelift/codegen/src/flowgraph.rs index fa62d3caebda..b8bc848ad0c6 100644 --- a/cranelift/codegen/src/flowgraph.rs +++ b/cranelift/codegen/src/flowgraph.rs @@ -197,7 +197,7 @@ pub type SuccIter<'a> = bforest::SetIter<'a, Block>; mod tests { use super::*; use crate::cursor::{Cursor, FuncCursor}; - use crate::ir::{types, Function, InstBuilder}; + use crate::ir::{types, InstBuilder}; use alloc::vec::Vec; #[test] diff --git a/cranelift/codegen/src/fpconst.isle b/cranelift/codegen/src/fpconst.isle new file mode 100644 index 000000000000..799951b7d3f9 --- /dev/null +++ b/cranelift/codegen/src/fpconst.isle @@ -0,0 +1,81 @@ +;; GENERATED BY `fpconst`. DO NOT EDIT!!! + +; Build one as a floating-point of the given width. +(macro (fp_one w) + (conv_to w + (switch w + (32 #x000000003f800000) + (64 #x3ff0000000000000) + ) + ) +) + +; Build negative one as a floating-point of the given width. +(macro (fp_minus_one w) + (conv_to w + (switch w + (32 #x00000000bf800000) + (64 #xbff0000000000000) + ) + ) +) + +; Build half as a floating-point of the given width. +(macro (fp_half w) + (conv_to w + (switch w + (32 #x000000003f000000) + (64 #x3fe0000000000000) + ) + ) +) + +; Build negative half as a floating-point of the given width. +(macro (fp_minus_half w) + (conv_to w + (switch w + (32 #x00000000bf000000) + (64 #xbfe0000000000000) + ) + ) +) + +; Build 32-bit integer minimum as a floating-point of the given width. +(macro (fp_i32_min w) + (conv_to w + (switch w + (32 #x00000000cf000000) + (64 #xc1e0000000000000) + ) + ) +) + +; Build negative 32-bit integer minimum as a floating-point of the given width. +(macro (fp_minus_i32_min w) + (conv_to w + (switch w + (32 #x000000004f000000) + (64 #x41e0000000000000) + ) + ) +) + +; Build 64-bit integer minimum as a floating-point of the given width. +(macro (fp_i64_min w) + (conv_to w + (switch w + (32 #x00000000df000000) + (64 #xc3e0000000000000) + ) + ) +) + +; Build negative 64-bit integer minimum as a floating-point of the given width. +(macro (fp_minus_i64_min w) + (conv_to w + (switch w + (32 #x000000005f000000) + (64 #x43e0000000000000) + ) + ) +) diff --git a/cranelift/codegen/src/fx.rs b/cranelift/codegen/src/fx.rs deleted file mode 100644 index bb1a9e59e6c6..000000000000 --- a/cranelift/codegen/src/fx.rs +++ /dev/null @@ -1,106 +0,0 @@ -// This file is taken from the Rust compiler: src/librustc_data_structures/fx.rs - -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::{HashMap, HashSet}; -use core::default::Default; -use core::hash::{BuildHasherDefault, Hash, Hasher}; -use core::ops::BitXor; - -pub type FxHashMap = HashMap>; -pub type FxHashSet = HashSet>; - -#[allow(non_snake_case)] -pub fn FxHashMap() -> FxHashMap { - HashMap::default() -} - -/// A speedy hash algorithm for use within rustc. The hashmap in liballoc -/// by default uses SipHash which isn't quite as speedy as we want. In the -/// compiler we're not really worried about DOS attempts, so we use a fast -/// non-cryptographic hash. -/// -/// This is the same as the algorithm used by Firefox -- which is a homespun -/// one not based on any widely-known algorithm -- though modified to produce -/// 64-bit hash values instead of 32-bit hash values. It consistently -/// out-performs an FNV-based hash within rustc itself -- the collision rate is -/// similar or slightly worse than FNV, but the speed of the hash function -/// itself is much higher because it works on up to 8 bytes at a time. -pub struct FxHasher { - hash: usize, -} - -#[cfg(target_pointer_width = "32")] -const K: usize = 0x9e3779b9; -#[cfg(target_pointer_width = "64")] -const K: usize = 0x517cc1b727220a95; - -impl Default for FxHasher { - #[inline] - fn default() -> Self { - Self { hash: 0 } - } -} - -impl FxHasher { - #[inline] - fn add_to_hash(&mut self, i: usize) { - self.hash = self.hash.rotate_left(5).bitxor(i).wrapping_mul(K); - } -} - -impl Hasher for FxHasher { - #[inline] - fn write(&mut self, bytes: &[u8]) { - for byte in bytes { - let i = *byte; - self.add_to_hash(i as usize); - } - } - - #[inline] - fn write_u8(&mut self, i: u8) { - self.add_to_hash(i as usize); - } - - #[inline] - fn write_u16(&mut self, i: u16) { - self.add_to_hash(i as usize); - } - - #[inline] - fn write_u32(&mut self, i: u32) { - self.add_to_hash(i as usize); - } - - #[cfg(target_pointer_width = "32")] - #[inline] - fn write_u64(&mut self, i: u64) { - self.add_to_hash(i as usize); - self.add_to_hash((i >> 32) as usize); - } - - #[cfg(target_pointer_width = "64")] - #[inline] - fn write_u64(&mut self, i: u64) { - self.add_to_hash(i as usize); - } - - #[inline] - fn write_usize(&mut self, i: usize) { - self.add_to_hash(i); - } - - #[inline] - fn finish(&self) -> u64 { - self.hash as u64 - } -} diff --git a/cranelift/codegen/src/incremental_cache.rs b/cranelift/codegen/src/incremental_cache.rs index 6290e55f84b1..89e42cf242a0 100644 --- a/cranelift/codegen/src/incremental_cache.rs +++ b/cranelift/codegen/src/incremental_cache.rs @@ -10,11 +10,11 @@ //! //! The three main primitives are the following: //! - `compute_cache_key` is used to compute the cache key associated to a `Function`. This is -//! basically the content of the function, modulo a few things the caching system is resilient to. +//! basically the content of the function, modulo a few things the caching system is resilient to. //! - `serialize_compiled` is used to serialize the result of a compilation, so it can be reused -//! later on by... +//! later on by... //! - `try_finish_recompile`, which reads binary blobs serialized with `serialize_compiled`, -//! re-creating the compilation artifact from those. +//! re-creating the compilation artifact from those. //! //! The `CacheStore` trait and `Context::compile_with_cache` method are provided as //! high-level, easy-to-use facilities to make use of that cache, and show an example of how to use @@ -208,12 +208,12 @@ pub fn compute_cache_key(isa: &dyn TargetIsa, func: &Function) -> CacheKeyHash { /// of the function call. The value is left untouched. pub fn serialize_compiled( result: CompiledCodeStencil, -) -> (CompiledCodeStencil, Result, bincode::Error>) { +) -> (CompiledCodeStencil, Result, postcard::Error>) { let cached = CachedFunc { version_marker: VersionMarker, stencil: result, }; - let result = bincode::serialize(&cached); + let result = postcard::to_allocvec(&cached); (cached.stencil, result) } @@ -223,7 +223,7 @@ pub enum RecompileError { /// The version embedded in the cache entry isn't the same as cranelift's current version. VersionMismatch, /// An error occurred while deserializing the cache entry. - Deserialize(bincode::Error), + Deserialize(postcard::Error), } impl fmt::Display for RecompileError { @@ -231,7 +231,7 @@ impl fmt::Display for RecompileError { match self { RecompileError::VersionMismatch => write!(f, "cranelift version mismatch",), RecompileError::Deserialize(err) => { - write!(f, "bincode failed during deserialization: {err}") + write!(f, "postcard failed during deserialization: {err}") } } } @@ -243,7 +243,7 @@ impl fmt::Display for RecompileError { /// Precondition: the bytes must have retrieved from a cache store entry which hash value /// is strictly the same as the `Function`'s computed hash retrieved from `compute_cache_key`. pub fn try_finish_recompile(func: &Function, bytes: &[u8]) -> Result { - match bincode::deserialize::(bytes) { + match postcard::from_bytes::(bytes) { Ok(result) => { if result.version_marker != func.stencil.version_marker { Err(RecompileError::VersionMismatch) diff --git a/cranelift/codegen/src/inst_predicates.rs b/cranelift/codegen/src/inst_predicates.rs index 707a01c1a5eb..e0000b726ee5 100644 --- a/cranelift/codegen/src/inst_predicates.rs +++ b/cranelift/codegen/src/inst_predicates.rs @@ -1,12 +1,6 @@ //! Instruction predicates/properties, shared by various analyses. use crate::ir::immediates::Offset32; -use crate::ir::{self, Block, DataFlowGraph, Function, Inst, InstructionData, Opcode, Type, Value}; -use cranelift_entity::EntityRef; - -/// Preserve instructions with used result values. -pub fn any_inst_results_used(inst: Inst, live: &[bool], dfg: &DataFlowGraph) -> bool { - dfg.inst_results(inst).iter().any(|v| live[v.index()]) -} +use crate::ir::{self, Block, Function, Inst, InstructionData, Opcode, Type, Value}; /// Test whether the given opcode is unsafe to even consider as side-effect-free. #[inline(always)] @@ -38,7 +32,7 @@ fn is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool /// Does the given instruction have any side-effect that would preclude it from being removed when /// its value is unused? #[inline(always)] -pub fn has_side_effect(func: &Function, inst: Inst) -> bool { +fn has_side_effect(func: &Function, inst: Inst) -> bool { let data = &func.dfg.insts[inst]; let opcode = data.opcode(); trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data) @@ -58,6 +52,7 @@ pub fn is_pure_for_egraph(func: &Function, inst: Inst) -> bool { } => flags.readonly() && flags.notrap(), _ => false, }; + // Multi-value results do not play nicely with much of the egraph // infrastructure. They are in practice used only for multi-return // calls and some other odd instructions (e.g. uadd_overflow) which, @@ -102,12 +97,9 @@ pub fn has_lowering_side_effect(func: &Function, inst: Inst) -> bool { /// Is the given instruction a constant value (`iconst`, `fconst`) that can be /// represented in 64 bits? pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option { - let data = &func.dfg.insts[inst]; - if data.opcode() == Opcode::Null { - return Some(0); - } - match data { + match &func.dfg.insts[inst] { &InstructionData::UnaryImm { imm, .. } => Some(imm.bits() as u64), + &InstructionData::UnaryIeee16 { imm, .. } => Some(imm.bits() as u64), &InstructionData::UnaryIeee32 { imm, .. } => Some(imm.bits() as u64), &InstructionData::UnaryIeee64 { imm, .. } => Some(imm.bits()), _ => None, @@ -116,8 +108,7 @@ pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option { /// Get the address, offset, and access type from the given instruction, if any. pub fn inst_addr_offset_type(func: &Function, inst: Inst) -> Option<(Value, Offset32, Type)> { - let data = &func.dfg.insts[inst]; - match data { + match &func.dfg.insts[inst] { InstructionData::Load { arg, offset, .. } => { let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]); Some((*arg, *offset, ty)) @@ -140,8 +131,7 @@ pub fn inst_addr_offset_type(func: &Function, inst: Inst) -> Option<(Value, Offs /// Get the store data, if any, from an instruction. pub fn inst_store_data(func: &Function, inst: Inst) -> Option { - let data = &func.dfg.insts[inst]; - match data { + match &func.dfg.insts[inst] { InstructionData::Store { args, .. } | InstructionData::StoreNoOffset { args, .. } => { Some(args[0]) } diff --git a/cranelift/codegen/src/inst_specs.isle b/cranelift/codegen/src/inst_specs.isle new file mode 100644 index 000000000000..7fac40d5e3cc --- /dev/null +++ b/cranelift/codegen/src/inst_specs.isle @@ -0,0 +1,1404 @@ +;;;; Verification Type Models ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(model Imm64 (type (bv 64))) + +(model Ieee32 (type (bv 32))) +(model Ieee64 (type (bv 64))) + +; REVIEW(mbm): do we need to model MemFlags? +(model MemFlags (type + (struct + (aligned Bool) + (trapcode (bv 4)) + ) +)) + +(model Offset32 (type (bv 32))) + +;;;; State Definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Value loaded from memory. +; +; We deliberately do not attempt to model the entire state of memory. Modeling a +; loaded value this way allows us to express the fact that loaded values on CLIF +; and ISA side will be equivalent (combined with an assertion on address +; equality). +(state loaded_value + (type (bv 64)) + (default true) +) + +; Parameters of a CLIF load operation. +(state clif_load + (type + (struct + (active Bool) + (size_bits Int) + (addr (bv 64)) + ) + ) + (default + (not (:active clif_load)) + ) +) + +; Parameters of a CLIF store operation. +(state clif_store + (type + (struct + (active Bool) + (size_bits Int) + (addr (bv 64)) + (value (bv 64)) + ) + ) + (default + (and + ; Store is not active. + (not (:active clif_store)) + + ; Must provide a fixed size in the default case, otherwise type + ; inference is underconstrained. + (= (:size_bits clif_store) 1) + ) + ) +) + +; Whether a trap is expected according to CLIF semantics. +(state clif_trap + (type Bool) + (default (not clif_trap)) +) + +;;;; Common Term Forms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(form + bv_unary_8_to_64 + ((args (named Type) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64))) +) + +(form + bv_binary_8_to_64 + ((args (named Type) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) +) + +(form + bv_ternary_8_to_64 + ((args (named Type) (bv 8) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16) (bv 16) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64) (bv 64)) (ret (bv 64))) +) + +;;;; i128 bit cases ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(form + bv_binary_8_to_128 + ((args (named Type) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) + ((args (named Type) (bv 128) (bv 128)) (ret (bv 128))) +) + +;;;; CLIF Instruction Specifications ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Integer Instructions + +(spec (iadd ty x y) + (provide (= result (bvadd x y)))) +(instantiate iadd + ((args (named Type) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 16) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) + ((args (named Type) (bv 128) (bv 128)) (ret (bv 128))) +) + +(spec (isub ty x y) + (provide (= result (bvsub x y)))) +(instantiate isub bv_binary_8_to_64) + +(spec (ineg ty x) + (provide (= result (bvneg x)))) +(instantiate ineg bv_unary_8_to_64) + +(spec (iabs ty x) + (provide (= result (if (bvsge x (zero_ext (widthof x) #b0)) x (bvneg x))))) +(instantiate iabs bv_unary_8_to_64) + +(spec (imul ty x y) + (provide (= result (bvmul x y)))) +(instantiate imul bv_binary_8_to_64) + +(spec (smulhi ty x y) + (provide + (let + ( + (double (concat x x)) + (double_width (widthof double)) + (xwide (sign_ext double_width x)) + (ywide (sign_ext double_width y)) + ) + (with (low) + (= (concat result low) (bvmul xwide ywide)) + ) + ) + ) +) +(instantiate smulhi bv_binary_8_to_64) + +(spec (umulhi ty x y) + (provide + (let + ( + (double (concat x x)) + (double_width (widthof double)) + (xwide (zero_ext double_width x)) + (ywide (zero_ext double_width y)) + ) + (with (low) + (= (concat result low) (bvmul xwide ywide)) + ) + ) + ) +) +(instantiate umulhi bv_binary_8_to_64) + +(spec (udiv ty x y) + (modifies clif_trap) + (provide + (= result (bvudiv x y)) + (= clif_trap (bv_is_zero! y)) + ) +) +(instantiate udiv bv_binary_8_to_64) + +(spec (sdiv ty x y) + (modifies clif_trap) + (provide + ; If j2 is 0, then the result is undefined. + (if (bv_is_zero! y) + clif_trap + ; Else if j1 divided by j2 is 2^{N−1}, then the result is undefined. + ; + ; Note: the only way this can happen is the case (−2^{N−1})/(−1). + (if (and + ; x is -2^{N-1} + (= x (bv_top_bit_set! (widthof x))) + ; y is -1 + (bv_is_zero! (bvnot y)) + ) + clif_trap + ; Else, return the result of dividing j1 by j2, truncated toward zero. + (and + (not clif_trap) + (= result (bvsdiv x y)) + ) + )) + ) +) + +(instantiate sdiv bv_binary_8_to_64) +(spec (urem ty x y) + (modifies clif_trap) + (provide + ; If i2 is 0, then the result is undefined. + (if (bv_is_zero! y) + clif_trap + ; Else, return the remainder of dividing i1 by i2. + (and + (not clif_trap) + (= result (bvurem x y)) + ) + ) + ) +) +(instantiate urem bv_binary_8_to_64) + +(spec (srem ty x y) + (modifies clif_trap) + (provide + ; Let j1 be the signed interpretation of i1. + ; Let j2 be the signed interpretation of i2. + ; If i2 is 0, then the result is undefined. + (if (bv_is_zero! y) + clif_trap + ; Else, return the remainder of dividing j1 by j2, with the sign of the dividend j1. + (and + (not clif_trap) + ; REVIEW(mbm): does bvsrem have the same semantics as wasm? + (= result (bvsrem x y)) + + ) + ) + ) +) +(instantiate srem bv_binary_8_to_64) + +;; "Unsigned addition of x and y, trapping if the result overflows." +;; "Accepts 32 or 64-bit integers, and does not support vector types." +(spec (uadd_overflow_trap ty x y trap_code) + (modifies clif_trap) + (provide + (let + ( + (N (widthof x)) + ;; Use at least 1 extra bit for unsigned overflow + (sum (bvadd (zero_ext 65 x) (zero_ext 65 y))) + ;; Unsigned overflow if some carry out. + (carry + (switch N + (32 (extract 32 32 sum)) + (64 (extract 64 64 sum)))) + ) + (if (= carry #b1) + clif_trap + (and + (not clif_trap) + (= result (conv_to N sum)) + ) + ) + ) + ) +) + +(instantiate uadd_overflow_trap + ((args (named Type) (bv 32) (bv 32) (named TrapCode)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64) (named TrapCode)) (ret (bv 64))) +) + + +(spec (iconst ty arg) + (provide (= arg (zero_ext 64 result)))) +(instantiate iconst + ((args (named Type) (bv 64)) (ret (bv 8))) + ((args (named Type) (bv 64)) (ret (bv 16))) + ((args (named Type) (bv 64)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64))) + ) + +(spec (ishl ty x y) + (provide + (= result + (bvshl + x + (bvand y (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) #x0000000000000001))) + ) + ) + ) +) +(instantiate ishl bv_binary_8_to_64) + +(spec (ushr ty x y) + (provide + (= result + (bvlshr + x + (bvand y (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) #x0000000000000001))) + ) + ) + ) +) +(instantiate ushr bv_binary_8_to_64) + +(spec (sshr ty x y) + (provide + (= result + (bvashr + x + (bvand y (conv_to (widthof y) (bvsub (int2bv 64 (widthof y)) #x0000000000000001))) + ) + ) + ) +) +(instantiate sshr bv_binary_8_to_64) + +(spec (band ty x y) + (provide (= result (bvand x y)))) +(instantiate band bv_binary_8_to_64) + +(spec (bxor ty x y) + (provide (= result (bvxor x y)))) +(instantiate bxor bv_binary_8_to_64) + +(spec (bor ty x y) + (provide (= result (bvor x y)))) +(instantiate bor bv_binary_8_to_64) + +(spec (bnot ty x) + (provide (= result (bvnot x)))) +(instantiate bnot bv_unary_8_to_64) + +(spec (rotl ty x y) + (provide (= result (rotl x y)))) +(instantiate rotl bv_binary_8_to_64) + +(spec (rotr ty x y) + (provide (= result (rotr x y)))) +(instantiate rotr bv_binary_8_to_64) + +(spec (bitselect ty c x y) + (provide (= result (bvor (bvand c x) (bvand (bvnot c) y))))) +(instantiate bitselect bv_ternary_8_to_64) + +(spec (cls ty x) (provide (= result (cls x)))) +(instantiate cls bv_unary_8_to_64) + +(spec (clz ty x) + (provide (= result (clz x)))) +(instantiate clz bv_unary_8_to_64) + +(spec (ctz ty x) + (provide (= result (clz (rev x))))) +(instantiate ctz bv_unary_8_to_64) + +(spec (popcnt ty x) + (provide (= result (popcnt x)))) +(instantiate popcnt bv_unary_8_to_64) + +(spec (ireduce ty x) + (provide (= result (conv_to (widthof result) x)))) +(instantiate ireduce + ((args (named Type) (bv 16)) (ret (bv 8))) + ((args (named Type) (bv 32)) (ret (bv 8))) + ((args (named Type) (bv 64)) (ret (bv 8))) + ((args (named Type) (bv 32)) (ret (bv 16))) + ((args (named Type) (bv 64)) (ret (bv 16))) + ((args (named Type) (bv 64)) (ret (bv 32))) +) + + + +(form extend + ((args (named Type) (bv 8)) (ret (bv 8))) + ((args (named Type) (bv 8)) (ret (bv 16))) + ((args (named Type) (bv 8)) (ret (bv 32))) + ((args (named Type) (bv 8)) (ret (bv 64))) + ((args (named Type) (bv 16)) (ret (bv 16))) + ((args (named Type) (bv 16)) (ret (bv 32))) + ((args (named Type) (bv 16)) (ret (bv 64))) + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 32)) (ret (bv 64))) + ;; Note: (bv 64) -> (bv 64) not accepted in clif +) + +(spec (uextend ty x) + (provide (= result (zero_ext (widthof result) x)))) +(instantiate uextend extend) + +(spec (sextend ty x) + (provide (= result (sign_ext (widthof result) x)))) +(instantiate sextend extend) + +(spec (smin ty x y) + (provide (= result (if (bvsle x y) x y)))) +(instantiate smin bv_binary_8_to_64) + +(spec (umin ty x y) + (provide (= result (if (bvule x y) x y)))) +(instantiate umin bv_binary_8_to_64) + +(spec (smax ty x y) + (provide (= result (if (bvsge x y) x y)))) +(instantiate smax bv_binary_8_to_64) + +(spec (umax ty x y) + (provide (= result (if (bvuge x y) x y)))) +(instantiate umax bv_binary_8_to_64) + +(spec (icmp ty cc x y) + (provide + (= result + (if + (match cc + ((Equal) (= x y)) + ((NotEqual) (not (= x y))) + ((SignedGreaterThan) (bvsgt x y)) + ((SignedGreaterThanOrEqual) (bvsge x y)) + ((SignedLessThan) (bvslt x y)) + ((SignedLessThanOrEqual) (bvsle x y)) + ((UnsignedGreaterThan) (bvugt x y)) + ((UnsignedGreaterThanOrEqual) (bvuge x y)) + ((UnsignedLessThan) (bvult x y)) + ((UnsignedLessThanOrEqual) (bvule x y)) + ) + #x01 + #x00 + ) + ) + ) +) +(instantiate icmp + ((args (named Type) (named IntCC) (bv 8) (bv 8)) (ret (bv 8))) + ((args (named Type) (named IntCC) (bv 16) (bv 16)) (ret (bv 8))) + ((args (named Type) (named IntCC) (bv 32) (bv 32)) (ret (bv 8))) + ((args (named Type) (named IntCC) (bv 64) (bv 64)) (ret (bv 8))) +) + +;; Load Instructions + +; Compute the effective address of a base pointer p and fixed offset. +(macro (effective_address p offset) + (bvadd p (sign_ext 64 offset)) +) + +; Activate and set parameters of a CLIF load effect. +(macro (clif_load_activate clif_load size_bits p offset) + (and + (:active clif_load) + (= (:size_bits clif_load) size_bits) + (= (:addr clif_load) (effective_address! p offset)) + ) +) + +; Load from memory +(spec (load ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide + ; Activate the CLIF load effect. + (clif_load_activate! clif_load (widthof result) p offset) + + ; Result of the load is represented by low bits of the loaded value state register. + (= result (conv_to (widthof result) loaded_value)) + ) +) +(instantiate load + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 8))) + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 16))) + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 64))) +) + +; Unsigned N-bit load +(macro (uloadN clif_load size_bits p offset loaded_value result) + (and + ; Activate the CLIF load effect. + (clif_load_activate! clif_load size_bits p offset) + + ; Loaded value is zero-extended. + (= result (zero_ext (widthof result) (conv_to size_bits loaded_value))) + ) +) + +; Unsigned 8-bit load +(spec (uload8 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (uloadN! clif_load 8 p offset loaded_value result)) +) +(instantiate uload8 + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 16))) + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 64))) +) + +; Unsigned 16-bit load +(spec (uload16 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (uloadN! clif_load 16 p offset loaded_value result)) +) +(instantiate uload16 + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 64))) +) + +; Unsigned 32-bit load +(spec (uload32 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (uloadN! clif_load 32 p offset loaded_value result)) +) +(instantiate uload32 + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 64))) +) + +; Signed N-bit load +(macro (sloadN clif_load size_bits p offset loaded_value result) + (and + ; Activate the CLIF load effect. + (clif_load_activate! clif_load size_bits p offset) + + ; Loaded value is sign-extended. + (= result (sign_ext (widthof result) (conv_to size_bits loaded_value))) + ) +) + +; Signed 8-bit load +(spec (sload8 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (sloadN! clif_load 8 p offset loaded_value result)) +) +(instantiate sload8 + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 16))) + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 64))) +) + +; Signed 16-bit load +(spec (sload16 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (sloadN! clif_load 16 p offset loaded_value result)) +) +(instantiate sload16 + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 32))) + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 64))) +) + +; Signed 32-bit load +(spec (sload32 ty flags p offset) + (modifies clif_load) + (modifies loaded_value) + (provide (sloadN! clif_load 32 p offset loaded_value result)) +) +(instantiate sload32 + ((args (named Type) (named MemFlags) (named Value) (named Offset32)) (ret (bv 64))) +) + +; Loads have a large number of expansions and instantiations. +(attr load (tag slow)) +(attr uload8 (tag slow)) +(attr uload16 (tag slow)) +(attr uload32 (tag slow)) +(attr sload8 (tag slow)) +(attr sload16 (tag slow)) +(attr sload32 (tag slow)) + +;; Store Instructions + +; Activate and set parameters of a CLIF store effect. +(macro (clif_store_activate clif_store value p offset) + (and + ; Activate the CLIF store effect + (:active clif_store) + + ; TODO(mbm): store flags + + ; Store size is the width of the stored value. + (= (:size_bits clif_store) (widthof value)) + + ; Address calculation. + (= (:addr clif_store) (effective_address! p offset)) + + ; Stored value is set to the low bits of the CLIF store value. + (= (conv_to (widthof value) (:value clif_store)) value) + ) +) + +; Store instruction specification. +(macro (store clif_store flags value p offset result) + (and + ; Activate the CLIF store effect + (clif_store_activate! clif_store value p offset) + + ; HACK: Result of the store is a 1-bit vector. + (= result #b1) + ) +) + +; Store to memory +(spec (store flags value p offset) + (modifies clif_store) + (provide (store! clif_store flags value p offset result)) +) +(instantiate store + ((args (named MemFlags) (bv 8) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlags) (bv 16) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlags) (bv 32) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlags) (bv 64) (named Value) (named Offset32)) (ret (bv 1))) +) + +; 8-bit store +(spec (istore8 flags value p offset) + (modifies clif_store) + (provide (store! clif_store flags (extract 7 0 value) p offset result)) +) +(instantiate istore8 + ((args (named MemFlags) (bv 16) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlags) (bv 32) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlags) (bv 64) (named Value) (named Offset32)) (ret (bv 1))) +) + +; 16-bit store +(spec (istore16 flags value p offset) + (modifies clif_store) + (provide (store! clif_store flags (extract 15 0 value) p offset result)) +) +(instantiate istore16 + ((args (named MemFlags) (bv 32) (named Value) (named Offset32)) (ret (bv 1))) + ((args (named MemFlags) (bv 64) (named Value) (named Offset32)) (ret (bv 1))) +) + +; 32-bit store +(spec (istore32 flags value p offset) + (modifies clif_store) + (provide (store! clif_store flags (extract 31 0 value) p offset result)) +) +(instantiate istore32 + ((args (named MemFlags) (bv 64) (named Value) (named Offset32)) (ret (bv 1))) +) + +; Stores have a large number of expansions and instantiations. +(attr store (tag slow)) +(attr istore8 (tag slow)) +(attr istore16 (tag slow)) +(attr istore32 (tag slow)) + +;; Floating Point Instructions + +; NaN Propagation: see WebAssembly Specification 2.0, section 4.3.3 + +; Evaluates the positive WebAssembly canonical NaN of the given width. +(macro (nan_canon w) + (conv_to w + (switch w + (32 #x000000007fc00000) + (64 #x7ff8000000000000) + ) + ) +) + +; NaN propagation with zero inputs. +; +; The CLIF semantics (inherited from WebAssembly) only requires a NaN payload +; with the top bit set. Our specification is a refinement, selecting the +; positive canonical NaN. +(macro (nans0 w) (nan_canon! w)) + +; NaN propagation with one input. +; +; The CLIF semantics (inherited from WebAssembly) requires that a canonical NaN +; input is preserved, while any other NaN is mapped to any arithmetic NaN (which +; has the top fraction bit set). Our chosen refinement is to return the NaN +; input with the top fraction bit or-ed in: this both preserves the canonical +; NaN and turns any other NaN into an arithmetic NaN. +(macro (nans1 x) (if (fp.isNaN x) (bvor x (fp_topfrac_bit_set! (widthof x))) (nans0! (widthof x)))) + +; NaN propagation with two inputs. +; +; The CLIF semantics (inherited from WebAssembly) requires that if both inputs +; are canonical then the output must be. Otherwise the output must be an +; arithmetic NaN. Our chosen refinement is to apply single-input NaN propagation +; to the first input if it's a NaN, otherwise to the second input if it's a NaN, +; and fallback to returning the canonical NaN. +(macro (nans2 x y) (if (fp.isNaN x) (nans1! x) (if (fp.isNaN y) (nans1! y) (nans0! (widthof x))))) + +; NaN negation. +; +; The CLIF semantics (inherited from WebAssembly) requires that negating a NaN +; flips the sign bit (rather than returning a nondeterministic NaN). +(macro (nan_neg x) + (conv_to (widthof x) + (bvxor x (fp_sign_bit_set! (widthof x))))) + +; f32const: single-precision floating-point constant. +(spec (f32const ty x) (provide (= result x))) +(instantiate f32const ((args (named Type) (bv 32)) (ret (bv 32)))) + +; f64const: double-precision floating-point constant. +(spec (f64const ty x) (provide (= result x))) +(instantiate f64const ((args (named Type) (bv 64)) (ret (bv 64)))) + +; fcmp: floating-point compare. +(spec (fcmp ty c x y) + (provide + ;; Restrict to operations used from Wasm for now + (or (= c (FloatCC.Equal)) + (= c (FloatCC.NotEqual)) + (= c (FloatCC.LessThan)) + (= c (FloatCC.GreaterThan)) + (= c (FloatCC.LessThanOrEqual)) + (= c (FloatCC.GreaterThanOrEqual))) + (= result + (if + (match c + ((Equal) (fp.eq x y)) + ((NotEqual) (fp.ne x y)) + ((LessThan) (fp.lt x y)) + ((GreaterThan) (fp.gt x y)) + ((LessThanOrEqual) (fp.le x y)) + ((GreaterThanOrEqual) (fp.ge x y)) + ) + #x01 + #x00 + ) + ) + ) +) +(instantiate fcmp + ((args (named Type) (named FloatCC) (bv 32) (bv 32)) (ret (bv 8))) + ((args (named Type) (named FloatCC) (bv 64) (bv 64)) (ret (bv 8))) +) + +; fadd: floating-point addition. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fadd ty x y) + (provide + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if both z1 and z2 are infinities of opposite signs, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_opposite_sign! x y)) + (nans0! (widthof x)) + ; Else if both z1 and z2 are infinities of equal sign, then return that infinity. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_equal_sign! x y)) + x + ; Else if either z1 or z2 is an infinity, then return that infinity. + (if (fp.isInfinite x) + x + (if (fp.isInfinite y) + y + ; Else if both z1 and z2 are zeroes of opposite sign, then return positive zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + (fp.+zero (widthof x)) + ; Else if both z1 and z2 are zeroes of equal sign, then return that zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_equal_sign! x y)) + x + ; Else if either z1 or z2 is a zero, then return the other operand. + (if (fp.isZero x) + y + (if (fp.isZero y) + x + ; Else if both z1 and z2 are values with the same magnitude but opposite signs, then return positive zero. + (if (and (= (fp_magnitude! x) (fp_magnitude! y)) (fp_opposite_sign! x y)) + (fp.+zero (widthof x)) + ; Else return the result of adding z1 and z2, rounded to the nearest representable value. + (fp.add x y) + )))))))))) + ) + ) +) +(instantiate fadd + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) +) + +; fsub: floating-point subtraction. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fsub ty x y) + (provide + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if both z1 and z2 are infinities of equal sign, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_equal_sign! x y)) + (nans0! (widthof x)) + ; Else if both z1 and z2 are infinities of opposite signs, then return z1. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_opposite_sign! x y)) + x + ; Else if z1 is an infinity, then return that infinity. + (if (fp.isInfinite x) + x + ; Else if z2 is an infinity, then return that infinity negated. + (if (fp.isInfinite y) + (fp.neg y) + ; Else if both z1 and z2 are zeroes of equal sign, then return positive zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else if both z1 and z2 are zeroes of opposite sign, then return z1. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + x + ; Else if z2 is a zero, then return z1. + (if (fp.isZero y) + x + ; Else if z1 is a zero, then return z2 negated. + (if (fp.isZero x) + (fp.neg y) + ; Else if both z1 and z2 are the same value, then return positive zero. + (if (and (= (fp_magnitude! x) (fp_magnitude! y)) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else return the result of subtracting z2 from z1, rounded to the nearest representable value. + (fp.sub x y) + )))))))))) + ) + ) +) +(instantiate fsub + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) +) + +; fmul: floating-point multiplication. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fmul ty x y) + (provide + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if one of z1 and z2 is a zero and the other an infinity, then return an element of nans{}. + (if (and (fp.isZero x) (fp.isInfinite y)) + (nans0! (widthof x)) + (if (and (fp.isInfinite x) (fp.isZero y)) + (nans0! (widthof x)) + ; Else if both z1 and z2 are infinities of equal sign, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + ; Else if both z1 and z2 are infinities of opposite signs, then return z1. + (if (and (fp.isInfinite x) (fp.isInfinite y) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + ; Else if either z1 or z2 is an infinity and the other a value with equal sign, then return positive infinity. + (if (and (fp.isInfinite x) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + (if (and (fp.isInfinite y) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + ; Else if either z1 or z2 is an infinity and the other a value with opposite sign, then return negative infinity. + (if (and (fp.isInfinite x) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + (if (and (fp.isInfinite y) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + ; Else if both z1 and z2 are zeroes of equal sign, then return positive zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else if both z1 and z2 are zeroes of opposite sign, then return negative zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + (fp.-zero (widthof x)) + ; Else return the result of multiplying z1 and z2, rounded to the nearest representable value. + (fp.mul x y) + ))))))))))) + ) + ) +) +(instantiate fmul + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) +) + +; fdiv: floating-point division. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fdiv ty x y) + (provide + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if both z1 and z2 are infinities, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isInfinite y)) + (nans0! (widthof x)) + ; Else if both z1 and z2 are zeroes, then return an element of nans{z1,z2}. + (if (and (fp.isZero x) (fp.isZero y)) + (nans2! x y) + ; Else if z1 is an infinity and z2 a value with equal sign, then return positive infinity. + (if (and (fp.isInfinite x) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + ; Else if z1 is an infinity and z2 a value with opposite sign, then return negative infinity. + (if (and (fp.isInfinite x) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + ; Else if z2 is an infinity and z1 a value with equal sign, then return positive zero. + (if (and (fp.isInfinite y) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else if z2 is an infinity and z1 a value with opposite sign, then return negative zero. + (if (and (fp.isInfinite y) (fp_opposite_sign! x y)) + (fp.-zero (widthof x)) + ; Else if z1 is a zero and z2 a value with equal sign, then return positive zero. + (if (and (fp.isZero x) (fp_equal_sign! x y)) + (fp.+zero (widthof x)) + ; Else if z1 is a zero and z2 a value with opposite sign, then return negative zero. + (if (and (fp.isZero x) (fp_opposite_sign! x y)) + (fp.-zero (widthof x)) + ; Else if z2 is a zero and z1 a value with equal sign, then return positive infinity. + (if (and (fp.isZero y) (fp_equal_sign! x y)) + (fp.+oo (widthof x)) + ; Else if z2 is a zero and z1 a value with opposite sign, then return negative infinity. + (if (and (fp.isZero y) (fp_opposite_sign! x y)) + (fp.-oo (widthof x)) + ; Else return the result of dividing z1 by z2, rounded to the nearest representable value. + (fp.div x y) + ))))))))))) + ) + ) +) +(instantiate fdiv + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) +) + + +; fmin: floating-point minimum. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fmin ty x y) + (provide + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if either z1 or z2 is a negative infinity, then return negative infinity. + (if (or (and (fp.isInfinite x) (fp.isNegative x)) (and (fp.isInfinite y) (fp.isNegative y))) + (fp.-oo (widthof x)) + ; Else if either z1 or z2 is a positive infinity, then return the other value. + (if (and (fp.isInfinite x) (fp.isPositive x)) + y + (if (and (fp.isInfinite y) (fp.isPositive y)) + x + ; Else if both z1 and z2 are zeroes of opposite signs, then return negative zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + (fp.-zero (widthof x)) + ; Else return the smaller value of z1 and z2. + (fp.min x y) + ))))) + ) + ) +) +(instantiate fmin + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) +) + +; fmax: floating-point minimum. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fmax ty x y) + (provide + (= result + ; If either z1 or z2 is a NaN, then return an element of nans{z1,z2}. + (if (or (fp.isNaN x) (fp.isNaN y)) + (nans2! x y) + ; Else if either z1 or z2 is a positive infinity, then return positive infinity. + (if (or (and (fp.isInfinite x) (fp.isPositive x)) (and (fp.isInfinite y) (fp.isPositive y))) + (fp.+oo (widthof x)) + ; Else if either z1 or z2 is a negative infinity, then return the other value. + (if (and (fp.isInfinite x) (fp.isNegative x)) + y + (if (and (fp.isInfinite y) (fp.isNegative y)) + x + ; Else if both z1 and z2 are zeroes of opposite signs, then return positive zero. + (if (and (fp.isZero x) (fp.isZero y) (fp_opposite_sign! x y)) + (fp.+zero (widthof x)) + ; Else return the smaller value of z1 and z2. + (fp.max x y) + ))))) + ) + ) +) +(instantiate fmax + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) +) + + +; fabs: floating-point absolute value. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fabs ty x) + (provide + (= result + ; If z is a NaN, then return z with positive sign. + (if (fp.isNaN x) + (bvand x (bvnot (fp_sign_bit_set! (widthof x)))) + ; Else if z is an infinity, then return positive infinity. + (if (fp.isInfinite x) + (fp.+oo (widthof x)) + ; Else if z is a zero, then return positive zero. + (if (fp.isZero x) + (fp.+zero (widthof x)) + ; Else if z is a positive value, then return z. + (if (fp.isPositive x) + x + ; Else return z negated. + (fp.neg x) + )))) + ) + ) +) +(instantiate fabs + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64))) +) + + +; fneg: floating-point negation. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fneg ty x) + (provide + (= result + ; If z is a NaN, then return z with negated sign. + (if (fp.isNaN x) + (nan_neg! x) + ; Else if z is an infinity, then return that infinity negated. + ; Else if z is a zero, then return that zero negated. + ; Else return z negated. + (fp.neg x)) ; Remaining cases of the spec handled by SMT fp.neg + ) + ) +) +(instantiate fneg + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64))) +) + + +; sqrt: floating-point square root. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (sqrt ty x) + (provide + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is negative infinity, then return an element of nans{}. + (if (and (fp.isInfinite x) (fp.isNegative x)) + (nans0! (widthof x)) + ; Else if z is positive infinity, then return positive infinity. + (if (and (fp.isInfinite x) (fp.isPositive x)) + (fp.+oo (widthof x)) + ; Else if z is a zero, then return that zero. + (if (fp.isZero x) + x + ; Else if z has a negative sign, then return an element of nans{}. + (if (fp.isNegative x) + (nans0! (widthof x)) + ; Else return the square root of z. + (fp.sqrt x) + ))))) + ) + ) +) +(instantiate sqrt + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64))) +) + +; ceil: floating-point ceiling. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (ceil ty x) + (provide + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is infinity, then return z. + (if (fp.isInfinite x) + x + ; Else if z is zero, then return z. + (if (fp.isZero x) + x + ; Else if z is smaller than 0 but greater than −1, then return negative zero. + (if (and (fp.lt x (fp.-zero (widthof x))) (fp.gt x (fp_minus_one! (widthof x)))) + (fp.-zero (widthof x)) + ; Else return the smallest integral value that is not smaller than z. + (fp.ceil x) + )))) + ) + ) +) +(instantiate ceil + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64))) +) + +; floor: floating-point floor. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (floor ty x) + (provide + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is infinity, then return z. + (if (fp.isInfinite x) + x + ; Else if z is zero, then return z. + (if (fp.isZero x) + x + ; Else if z is greater than 0 but smaller than 1, then return positive zero. + (if (and (fp.gt x (fp.-zero (widthof x))) (fp.lt x (fp_one! (widthof x)))) + (fp.+zero (widthof x)) + ; Else return the smallest integral value that is not smaller than z. + (fp.floor x) + )))) + ) + ) +) +(instantiate floor + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64))) +) + +; trunc: floating-point truncate. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (trunc ty x) + (provide + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is infinity, then return z. + (if (fp.isInfinite x) + x + ; Else if z is a zero, then return z. + (if (fp.isZero x) + x + ; Else if z is greater than 0 but smaller than 1, then return positive zero. + (if (and (fp.gt x (fp.+zero (widthof x))) (fp.lt x (fp_one! (widthof x)))) + (fp.+zero (widthof x)) + ; Else if z is smaller than 0 but greater than −1, then return negative zero. + (if (and (fp.lt x (fp.-zero (widthof x))) (fp.gt x (fp_minus_one! (widthof x)))) + (fp.-zero (widthof x)) + ; Else return the integral value with the same sign as z and the largest magnitude that is not larger than the magnitude of z. + (fp.trunc x) + ))))) + ) + ) +) +(instantiate trunc + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64))) +) +; nearest: floating-point nearest. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (nearest ty x) + (provide + (= result + ; If z is a NaN, then return an element of nans{z}. + (if (fp.isNaN x) + (nans1! x) + ; Else if z is infinity, then return z. + (if (fp.isInfinite x) + x + ; Else if z is zero, then return z. + (if (fp.isZero x) + x + ; Else if z is greater than 0 but smaller than or equal to 0.5, then return positive zero. + (if (and (fp.gt x (fp.+zero (widthof x))) (fp.le x (fp_half! (widthof x)))) + (fp.+zero (widthof x)) + ; Else if z is smaller than 0 but greater than or equal to −0.5, then return negative zero. + (if (and (fp.lt x (fp.-zero (widthof x))) (fp.ge x (fp_minus_half! (widthof x)))) + (fp.-zero (widthof x)) + ; Else return the integral value that is nearest to z; if two values are equally near, return the even one. + (fp.nearest x) + ))))) + ) + ) +) +(instantiate nearest + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64))) +) + +; fcopysign: floating-point copysign. +; +; Specification derived from WebAssembly Specification prose (Floating Point Numerics, section 4.3.3). +(spec (fcopysign ty x y) + (provide + (= result + ; If z1 and z2 have the same sign, then return z1. + (if (fp_equal_sign_inc_nan! x y) + x + ; Else return z1 with negated sign. + (if (fp.isNaN x) + (nan_neg! x) + (fp.neg x) + )) + ) + ) +) +(instantiate fcopysign + ((args (named Type) (bv 32) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 64) (bv 64)) (ret (bv 64))) +) + +(spec (bitcast ty flags x) + (provide (= result x)) +) +; I32ReinterpretF32 +; I64ReinterpretF64 +; F32ReinterpretI32 +; F64ReinterpretI64 +(instantiate bitcast + ((args (named Type) (named MemFlags) (bv 32)) (ret (bv 32))) + ((args (named Type) (named MemFlags) (bv 64)) (ret (bv 64))) +) + +(form fcvt + ((args (named Type) (bv 32)) (ret (bv 32))) + ((args (named Type) (bv 32)) (ret (bv 64))) + ((args (named Type) (bv 64)) (ret (bv 32))) + ((args (named Type) (bv 64)) (ret (bv 64)))) + +(spec (fcvt_from_uint ty x) + (provide + (let ((N (widthof result))) + (= result (to_fp_unsigned N (conv_to N (zero_ext 64 x)))))) +) +(instantiate fcvt_from_uint fcvt) + +(spec (fcvt_from_sint ty x) + (provide + (let ((N (widthof result))) + (= result (to_fp N (conv_to N (sign_ext 64 x)))))) +) +(instantiate fcvt_from_sint fcvt) + +;; Can trap if invalid conversion +;; Derived from Wasm reference interpreter +;; https://github.com/WebAssembly/spec/blob/5d12bd74c49932deb7ab4bae3d29bf106f19d10b/interpreter/exec/i32_convert.ml#L5 +(macro (neg_min_int_times_two_as_fp w) + (switch w + (32 #x000000004f800000) + (64 #x43f0000000000000) + ) +) +(spec (fcvt_to_uint ty x) + (modifies clif_trap) + (provide + (let ( + (s (widthof x)) + (d (widthof result)) + ) + (and + (=> (not clif_trap) (= result (fp.to_ubv d (to_fp_from_fp d x)))) + + ;; Trap if input is NaN or does not fit in the integer type + ;; if xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0 then + (= clif_trap (or + (fp.isNaN x) + (fp.ge x (to_fp_from_fp s (conv_to d (neg_min_int_times_two_as_fp! d)))) + (fp.le x (to_fp_from_fp s (conv_to d (fp_minus_one! d))))))))) +) +(instantiate fcvt_to_uint fcvt) + +(macro (min_int_as_fp w n) + (switch n + (32 (fp_i32_min! w)) + (64 (fp_i64_min! w)) + ) +) +(macro (neg_min_int_as_fp w n) + (switch n + (32 (fp_minus_i32_min! w)) + (64 (fp_minus_i64_min! w)) + ) +) +(spec (fcvt_to_sint ty x) + (modifies clif_trap) + (provide + (let ( + (s (widthof x)) + (d (widthof result)) + ) + (and + (=> (not clif_trap) (= result (fp.to_sbv d (to_fp_from_fp d x)))) + + ;; Trap if input is NaN or does not fit in the integer type + ;; + ;; Note f64 to i32 case takes a different form according to the reference interpreter. + ;; + ;; Reference interpreter i32_convert `trunc_f32_s`: + ;; if xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) then + ;; Reference interpreter i32_convert `trunc_f64_s`: + ;; if xf >= -.Int32.(to_float min_int) || xf <= Int32.(to_float min_int) -. 1.0 then + ;; Reference interpreter i64_convert `trunc_f32_s`: + ;; if xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) then + ;; Reference interpreter i64_convert `trunc_f64_s`: + ;; if xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) then + (= clif_trap (or + (fp.isNaN x) + (fp.ge x (neg_min_int_as_fp! s d)) + (if (and (= s 64) (= d 32)) + (fp.le x (fp.sub (min_int_as_fp! s d) (fp_one! s))) + (fp.lt x (min_int_as_fp! s d)) + )))))) +) +(instantiate fcvt_to_sint fcvt) + + +; Specification derived from WebAssembly Specification prose (Conversions, section 4.3.4). +(spec (fdemote ty z) + (match + ;; Demote only can return bv-32 as written + (= (widthof result) 32)) + (provide + (= result + ; If z is a canonical NaN, then return an element of nans{} (i.e., a canonical NaN of size N). + ; Else if z is a NaN, then return an element of nans{±nan(1)} (i.e., any NaN of size N). + (if (fp.isNaN z) + ;; Note: derived from Wasm reference interpreter: + ;; https://github.com/WebAssembly/spec/blob/268a03da8576cc491d708777e69676724938aec9/interpreter/exec/f32_convert.ml#L4 + (bvor #x7fc00000 + (extract 31 0 + (bvor + (bvshl_int! (bvlshr_int! z 63) 31) + (bvlshr_int! (bvshl_int! z 12) 41) + ) + ) + ) + ; Else if z is an infinity, then return that infinity. + (if (fp.isInfinite z) + (if (fp.isNegative z) (fp.-oo 32) (fp.+oo 32)) + ; Else if z is a zero, then return that zero. + (if (fp.isZero z) + (if (fp.isNegative z) (fp.-zero 32) (fp.+zero 32)) + ; Else,return float(z) + (to_fp_from_fp 32 z) + ))) + ) + ) +) +(instantiate fdemote + ((args (named Type) (bv 64)) (ret (bv 32))) +) + +; Specification derived from WebAssembly Specification prose (Conversions, section 4.3.4). +(spec (fpromote ty z) + (match + ;; Promote only can return bv-32 as written + (= (widthof result) 64)) + (provide + (= result + ; If z is a canonical NaN, then return an element of nans{} (i.e., a canonical NaN of size N). + ; Else if z is a NaN, then return an element of nans{±nan(1)} (i.e., any arithmetic NaN of size N). + (if (fp.isNaN z) + ;; Note: derived from Wasm reference interpreter: + ;; https://github.com/WebAssembly/spec/blob/5d12bd74c49932deb7ab4bae3d29bf106f19d10b/interpreter/exec/f64_convert.ml#L4 + (bvor #x7ff8000000000000 + (bvor + (bvshl_int! (bvlshr_int! (zero_ext 64 z) 31) 63) + (bvlshr_int! (bvshl_int! (zero_ext 64 z) 41) 12) + ) + ) + ; Else, return z. + (to_fp_from_fp 64 z) + ) + ) + ) +) +(instantiate fpromote + ((args (named Type) (bv 32)) (ret (bv 64))) +) + +;;;; CLIF Instruction Tags ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(attr select_spectre_guard (tag spectre)) + +; Use Z3 solver for the following instructions. +(attr fadd (tag solver_z3)) +(attr fmul (tag solver_z3)) +(attr fdiv (tag solver_z3)) +(attr sqrt (tag solver_z3)) + +(attr cls (tag solver_z3)) +(attr clz (tag solver_z3)) +(attr ctz (tag solver_z3)) +(attr popcnt (tag solver_z3)) diff --git a/cranelift/codegen/src/inst_tags.isle b/cranelift/codegen/src/inst_tags.isle new file mode 100644 index 000000000000..ef5e9da6d310 --- /dev/null +++ b/cranelift/codegen/src/inst_tags.isle @@ -0,0 +1,314 @@ +;; GENERATED BY `isletags`. DO NOT EDIT!!! + +(attr select + (tag wasm_category_stack) + (tag wasm_proposal_mvp) +) + +(attr icmp + (tag wasm_category_comparison) + (tag wasm_category_loads) + (tag wasm_category_stores) + (tag wasm_proposal_mvp) +) + +(attr iconst + (tag wasm_category_const) + (tag wasm_category_loads) + (tag wasm_category_stores) + (tag wasm_proposal_mvp) +) + +(attr isub + (tag wasm_category_binary) + (tag wasm_category_loads) + (tag wasm_category_stores) + (tag wasm_proposal_mvp) +) + +(attr uadd_overflow_trap + (tag wasm_category_loads) + (tag wasm_category_stores) + (tag wasm_proposal_mvp) +) + +(attr uload8 + (tag wasm_category_loads) + (tag wasm_proposal_mvp) +) + +(attr uload16 + (tag wasm_category_loads) + (tag wasm_proposal_mvp) +) + +(attr sload8 + (tag wasm_category_loads) + (tag wasm_proposal_mvp) +) + +(attr sload16 + (tag wasm_category_loads) + (tag wasm_proposal_mvp) +) + +(attr sload32 + (tag wasm_category_loads) + (tag wasm_proposal_mvp) +) + +(attr uload32 + (tag wasm_category_loads) + (tag wasm_proposal_mvp) +) + +(attr load + (tag wasm_category_loads) + (tag wasm_proposal_mvp) +) + +(attr store + (tag wasm_category_stores) + (tag wasm_proposal_mvp) +) + +(attr istore8 + (tag wasm_category_stores) + (tag wasm_proposal_mvp) +) + +(attr istore16 + (tag wasm_category_stores) + (tag wasm_proposal_mvp) +) + +(attr istore32 + (tag wasm_category_stores) + (tag wasm_proposal_mvp) +) + +(attr f32const + (tag wasm_category_const) + (tag wasm_proposal_mvp) +) + +(attr f64const + (tag wasm_category_const) + (tag wasm_proposal_mvp) +) + +(attr clz + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr ctz + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr popcnt + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr sextend + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr uextend + (tag wasm_category_comparison) + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr ireduce + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr sqrt + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr ceil + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr floor + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr trunc + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr nearest + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr fabs + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr fneg + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr fcvt_from_uint + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr fcvt_from_sint + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr fpromote + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr fdemote + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr fcvt_to_sint + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr fcvt_to_uint + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr bitcast + (tag wasm_category_unary) + (tag wasm_proposal_mvp) +) + +(attr iadd + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr band + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr bor + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr bxor + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr ishl + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr sshr + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr ushr + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr rotl + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr rotr + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr fadd + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr fsub + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr imul + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr fmul + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr fdiv + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr sdiv + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr udiv + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr srem + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr urem + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr fmin + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr fmax + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr fcopysign + (tag wasm_category_binary) + (tag wasm_proposal_mvp) +) + +(attr icmp_imm + (tag wasm_category_comparison) + (tag wasm_proposal_mvp) +) + +(attr fcmp + (tag wasm_category_comparison) + (tag wasm_proposal_mvp) +) diff --git a/cranelift/codegen/src/ir/constant.rs b/cranelift/codegen/src/ir/constant.rs index 637784a27a88..0db31628cca2 100644 --- a/cranelift/codegen/src/ir/constant.rs +++ b/cranelift/codegen/src/ir/constant.rs @@ -8,12 +8,11 @@ //! - ensuring alignment of constants within the pool, //! - bucketing constants by size. -use crate::ir::immediates::{IntoBytes, V128Imm}; +use crate::ir::immediates::{Ieee128, IntoBytes, V128Imm}; use crate::ir::Constant; use alloc::collections::BTreeMap; use alloc::vec::Vec; use core::fmt; -use core::iter::FromIterator; use core::slice::Iter; use core::str::{from_utf8, FromStr}; use cranelift_entity::EntityRef; @@ -55,6 +54,22 @@ impl From for ConstantData { } } +impl From for ConstantData { + fn from(v: Ieee128) -> Self { + Self(v.into_bytes()) + } +} + +impl TryFrom<&ConstantData> for Ieee128 { + type Error = <[u8; 16] as TryFrom<&'static [u8]>>::Error; + + fn try_from(value: &ConstantData) -> Result { + Ok(Ieee128::with_bits(u128::from_le_bytes( + value.as_slice().try_into()?, + ))) + } +} + impl ConstantData { /// Return the number of bytes in the constant. pub fn len(&self) -> usize { @@ -92,10 +107,7 @@ impl ConstantData { /// in the high-order byte slots. pub fn expand_to(mut self, expected_size: usize) -> Self { if self.len() > expected_size { - panic!( - "The constant data is already expanded beyond {} bytes", - expected_size - ) + panic!("The constant data is already expanded beyond {expected_size} bytes") } self.0.resize(expected_size, 0); self @@ -116,7 +128,7 @@ impl fmt::Display for ConstantData { if !self.is_empty() { write!(f, "0x")?; for b in self.0.iter().rev() { - write!(f, "{:02x}", b)?; + write!(f, "{b:02x}")?; } } Ok(()) @@ -166,8 +178,8 @@ impl FromStr for ConstantData { } } -/// Maintains the mapping between a constant handle (i.e. [`Constant`](crate::ir::Constant)) and -/// its constant data (i.e. [`ConstantData`](crate::ir::ConstantData)). +/// Maintains the mapping between a constant handle (i.e. [`Constant`]) and +/// its constant data (i.e. [`ConstantData`]). #[derive(Clone, PartialEq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct ConstantPool { @@ -399,8 +411,7 @@ mod tests { let parsed = from.parse::(); assert!( parsed.is_err(), - "Expected a parse error but parsing succeeded: {}", - from + "Expected a parse error but parsing succeeded: {from}" ); assert_eq!(parsed.err().unwrap(), error_msg); } @@ -460,4 +471,15 @@ mod tests { [0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ); } + + #[test] + fn constant_ieee128() { + let value = Ieee128::with_bits(0x000102030405060708090a0b0c0d0e0f); + let constant = ConstantData::from(value); + assert_eq!( + constant.as_slice(), + &[0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0] + ); + assert_eq!(Ieee128::try_from(&constant).unwrap().bits(), value.bits()); + } } diff --git a/cranelift/codegen/src/ir/dfg.rs b/cranelift/codegen/src/ir/dfg.rs index 76b1b08cbeea..c26c3d591e21 100644 --- a/cranelift/codegen/src/ir/dfg.rs +++ b/cranelift/codegen/src/ir/dfg.rs @@ -5,6 +5,8 @@ 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::{ types, Block, BlockCall, ConstantData, ConstantPool, DynamicType, ExtFuncData, FuncRef, Immediate, Inst, JumpTables, RelSourceLoc, SigRef, Signature, Type, Value, @@ -104,6 +106,16 @@ pub struct DataFlowGraph { /// primary `insts` map. results: SecondaryMap, + /// User-defined stack maps. + /// + /// Not to be confused with the stack maps that `regalloc2` produces. These + /// are defined by the user in `cranelift-frontend`. These will eventually + /// replace the stack maps support in `regalloc2`, but in the name of + /// incrementalism and avoiding gigantic PRs that completely overhaul + /// Cranelift and Wasmtime at the same time, we are allowing them to live in + /// parallel for the time being. + user_stack_maps: alloc::collections::BTreeMap, + /// basic blocks in the function and their parameters. /// /// This map is not in program order. That is handled by `Layout`, and so is the sequence of @@ -125,13 +137,13 @@ 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, - /// The pre-legalization signature for each entry in `signatures`, if any. - pub old_signatures: SecondaryMap>, - /// External function references. These are functions that can be called directly. pub ext_funcs: PrimaryMap, @@ -154,12 +166,13 @@ impl DataFlowGraph { Self { insts: Insts(PrimaryMap::new()), results: SecondaryMap::new(), + user_stack_maps: alloc::collections::BTreeMap::new(), blocks: Blocks(PrimaryMap::new()), dynamic_types: DynamicTypes::new(), value_lists: ValueListPool::new(), values: PrimaryMap::new(), + facts: SecondaryMap::new(), signatures: PrimaryMap::new(), - old_signatures: SecondaryMap::new(), ext_funcs: PrimaryMap::new(), values_labels: None, constants: ConstantPool::new(), @@ -172,17 +185,18 @@ impl DataFlowGraph { pub fn clear(&mut self) { self.insts.0.clear(); self.results.clear(); + self.user_stack_maps.clear(); self.blocks.0.clear(); self.dynamic_types.clear(); self.value_lists.clear(); self.values.clear(); self.signatures.clear(); - self.old_signatures.clear(); self.ext_funcs.clear(); self.values_labels = None; 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 @@ -271,7 +285,7 @@ fn resolve_aliases(values: &PrimaryMap, value: Value) -> if let Some(v) = maybe_resolve_aliases(values, value) { v } else { - panic!("Value alias loop detected for {}", value); + panic!("Value alias loop detected for {value}"); } } @@ -327,6 +341,13 @@ impl DataFlowGraph { self.values.is_valid(v) } + /// Check whether a value is valid and not an alias. + pub fn value_is_real(&self, value: Value) -> bool { + // Deleted or unused values are also stored as aliases so this excludes + // those as well. + self.value_is_valid(value) && !matches!(self.values[value].into(), ValueData::Alias { .. }) + } + /// Get the type of a value. pub fn value_type(&self, v: Value) -> Type { self.values[v].ty() @@ -372,12 +393,109 @@ impl DataFlowGraph { resolve_aliases(&self.values, value) } - /// Resolve all aliases among inst's arguments. - /// - /// For each argument of inst which is defined by an alias, replace the - /// alias with the aliased value. - pub fn resolve_aliases_in_arguments(&mut self, inst: Inst) { - self.map_inst_values(inst, |dfg, arg| resolve_aliases(&dfg.values, arg)); + /// Replace all uses of value aliases with their resolved values, and delete + /// the aliases. + pub fn resolve_all_aliases(&mut self) { + let invalid_value = ValueDataPacked::from(ValueData::Alias { + ty: types::INVALID, + original: Value::reserved_value(), + }); + + // Rewrite each chain of aliases. Update every alias along the chain + // into an alias directly to the final value. Due to updating every + // alias that it looks at, this loop runs in time linear in the number + // of values. + for mut src in self.values.keys() { + let value_data = self.values[src]; + if value_data == invalid_value { + continue; + } + if let ValueData::Alias { mut original, .. } = value_data.into() { + // We don't use the type after this, we just need some place to + // store the resolved aliases temporarily. + let resolved = ValueDataPacked::from(ValueData::Alias { + ty: types::INVALID, + original: resolve_aliases(&self.values, original), + }); + // Walk the chain again, splatting the new alias everywhere. + // resolve_aliases panics if there's an alias cycle, so we don't + // need to guard against cycles here. + loop { + self.values[src] = resolved; + src = original; + if let ValueData::Alias { original: next, .. } = self.values[src].into() { + original = next; + } else { + break; + } + } + } + } + + // Now aliases don't point to other aliases, so we can replace any use + // of an alias with the final value in constant time. + + // Rewrite InstructionData in `self.insts`. + for inst in self.insts.0.values_mut() { + inst.map_values(&mut self.value_lists, &mut self.jump_tables, |arg| { + if let ValueData::Alias { original, .. } = self.values[arg].into() { + original + } else { + arg + } + }); + } + + // - `results` and block-params in `blocks` are not aliases, by + // definition. + // - `dynamic_types` has no values. + // - `value_lists` can only be accessed via references from elsewhere. + // - `values` only has value references in aliases (which we've + // 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 { + // Debug info is best-effort. If any is attached to value aliases, + // just discard it. + values_labels.retain(|&k, _| !matches!(self.values[k].into(), ValueData::Alias { .. })); + + // If debug-info says a value should have the same labels as another + // value, then make sure that target is not a value alias. + for value_label in values_labels.values_mut() { + if let ValueLabelAssignments::Alias { value, .. } = value_label { + if let ValueData::Alias { original, .. } = self.values[*value].into() { + *value = original; + } + } + } + } + + // - `constants` and `immediates` have no values. + // - `jump_tables` is updated together with instruction-data above. + + // Delete all aliases now that there are no uses left. + for value in self.values.values_mut() { + if let ValueData::Alias { .. } = ValueData::from(*value) { + *value = invalid_value; + } + } } /// Turn a value into an alias of another. @@ -393,8 +511,7 @@ impl DataFlowGraph { let original = self.resolve_aliases(src); debug_assert_ne!( dest, original, - "Aliasing {} to {} would create a loop", - dest, src + "Aliasing {dest} to {src} would create a loop" ); let ty = self.value_type(original); debug_assert_eq!( @@ -420,33 +537,29 @@ impl DataFlowGraph { /// After calling this instruction, `dest_inst` will have had its results /// cleared, so it likely needs to be removed from the graph. /// - pub fn replace_with_aliases(&mut self, dest_inst: Inst, src_inst: Inst) { + pub fn replace_with_aliases(&mut self, dest_inst: Inst, original_inst: Inst) { debug_assert_ne!( - dest_inst, src_inst, - "Replacing {} with itself would create a loop", - dest_inst + dest_inst, original_inst, + "Replacing {dest_inst} with itself would create a loop" ); + + let dest_results = self.results[dest_inst].as_slice(&self.value_lists); + let original_results = self.results[original_inst].as_slice(&self.value_lists); + debug_assert_eq!( - self.results[dest_inst].len(&self.value_lists), - self.results[src_inst].len(&self.value_lists), - "Replacing {} with {} would produce a different number of results.", - dest_inst, - src_inst + dest_results.len(), + original_results.len(), + "Replacing {dest_inst} with {original_inst} would produce a different number of results." ); - for (&dest, &src) in self.results[dest_inst] - .as_slice(&self.value_lists) - .iter() - .zip(self.results[src_inst].as_slice(&self.value_lists)) - { - let original = src; + for (&dest, &original) in dest_results.iter().zip(original_results) { let ty = self.value_type(original); debug_assert_eq!( self.value_type(dest), ty, "Aliasing {} to {} would change its type {} to {}", dest, - src, + original, self.value_type(dest), ty ); @@ -457,6 +570,22 @@ impl DataFlowGraph { self.clear_results(dest_inst); } + + /// Get the stack map entries associated with the given instruction. + pub fn user_stack_map_entries(&self, inst: Inst) -> Option<&[UserStackMapEntry]> { + self.user_stack_maps.get(&inst).map(|es| &**es) + } + + /// Append a new stack map entry for the given call instruction. + /// + /// # Panics + /// + /// Panics if the given instruction is not a (non-tail) call instruction. + pub fn append_user_stack_map_entry(&mut self, inst: Inst, entry: UserStackMapEntry) { + let opcode = self.insts[inst].opcode(); + assert!(opcode.is_safepoint()); + self.user_stack_maps.entry(inst).or_default().push(entry); + } } /// Where did a value come from? @@ -545,10 +674,15 @@ struct ValueDataPacked(u64); /// (and is implied by `mask`), by translating 2^32-1 (0xffffffff) /// into 2^n-1 and panic'ing on 2^n..2^32-1. fn encode_narrow_field(x: u32, bits: u8) -> u32 { + let max = (1 << bits) - 1; if x == 0xffff_ffff { - (1 << bits) - 1 + max } else { - debug_assert!(x < (1 << bits)); + debug_assert!( + x < max, + "{x} does not fit into {bits} bits (must be less than {max} to \ + allow for a 0xffffffff sentinel)" + ); x } } @@ -624,7 +758,7 @@ impl From for ValueDataPacked { Self::make(Self::TAG_ALIAS, ty, 0, original.as_bits()) } ValueData::Union { ty, x, y } => { - Self::make(Self::TAG_ALIAS, ty, x.as_bits(), y.as_bits()) + Self::make(Self::TAG_UNION, ty, x.as_bits(), y.as_bits()) } } } @@ -721,24 +855,11 @@ impl DataFlowGraph { } /// Map a function over the values of the instruction. - pub fn map_inst_values(&mut self, inst: Inst, mut body: F) + pub fn map_inst_values(&mut self, inst: Inst, body: F) where - F: FnMut(&mut DataFlowGraph, Value) -> Value, + F: FnMut(Value) -> Value, { - for i in 0..self.inst_args(inst).len() { - let arg = self.inst_args(inst)[i]; - self.inst_args_mut(inst)[i] = body(self, arg); - } - - for block_ix in 0..self.insts[inst].branch_destination(&self.jump_tables).len() { - // We aren't changing the size of the args list, so we won't need to write the branch - // back to the instruction. - let mut block = self.insts[inst].branch_destination(&self.jump_tables)[block_ix]; - for i in 0..block.args_slice(&self.value_lists).len() { - let arg = block.args_slice(&self.value_lists)[i]; - block.args_slice_mut(&mut self.value_lists)[i] = body(self, arg); - } - } + self.insts[inst].map_values(&mut self.value_lists, &mut self.jump_tables, body); } /// Overwrite the instruction's value references with values from the iterator. @@ -748,16 +869,9 @@ impl DataFlowGraph { where I: Iterator, { - for arg in self.inst_args_mut(inst) { - *arg = values.next().unwrap(); - } - - for block_ix in 0..self.insts[inst].branch_destination(&self.jump_tables).len() { - let mut block = self.insts[inst].branch_destination(&self.jump_tables)[block_ix]; - for arg in block.args_slice_mut(&mut self.value_lists) { - *arg = values.next().unwrap(); - } - } + self.insts[inst].map_values(&mut self.value_lists, &mut self.jump_tables, |_| { + values.next().unwrap() + }); } /// Get all value arguments on `inst` as a slice. @@ -836,22 +950,27 @@ impl DataFlowGraph { where I: Iterator>, { - self.results[inst].clear(&mut self.value_lists); + self.clear_results(inst); let mut reuse = reuse.fuse(); let result_tys: SmallVec<[_; 16]> = self.inst_result_types(inst, ctrl_typevar).collect(); - let num_results = result_tys.len(); - for ty in result_tys { - if let Some(Some(v)) = reuse.next() { - debug_assert_eq!(self.value_type(v), ty, "Reused {} is wrong type", ty); - self.attach_result(inst, v); + for (expected, &ty) in result_tys.iter().enumerate() { + let num = u16::try_from(expected).expect("Result value index should fit in u16"); + let value_data = ValueData::Inst { ty, num, inst }; + let v = if let Some(Some(v)) = reuse.next() { + debug_assert_eq!(self.value_type(v), ty, "Reused {ty} is wrong type"); + debug_assert!(!self.value_is_attached(v)); + self.values[v] = value_data.into(); + v } else { - self.append_result(inst, ty); - } + self.make_value(value_data) + }; + let actual = self.results[inst].push(v, &mut self.value_lists); + debug_assert_eq!(expected, actual); } - num_results + result_tys.len() } /// Create a `ReplaceBuilder` that will replace `inst` with a new instruction in place. @@ -859,14 +978,6 @@ impl DataFlowGraph { ReplaceBuilder::new(self, inst) } - /// Detach the list of result values from `inst` and return it. - /// - /// This leaves `inst` without any result values. New result values can be created by calling - /// `make_inst_results` or by using a `replace(inst)` builder. - pub fn detach_results(&mut self, inst: Inst) -> ValueList { - self.results[inst].take() - } - /// Clear the list of result values from `inst`. /// /// This leaves `inst` without any result values. New result values can be created by calling @@ -875,25 +986,6 @@ impl DataFlowGraph { self.results[inst].clear(&mut self.value_lists) } - /// Attach an existing value to the result value list for `inst`. - /// - /// The `res` value is appended to the end of the result list. - /// - /// This is a very low-level operation. Usually, instruction results with the correct types are - /// created automatically. The `res` value must not be attached to anything else. - pub fn attach_result(&mut self, inst: Inst, res: Value) { - debug_assert!(!self.value_is_attached(res)); - let num = self.results[inst].push(res, &mut self.value_lists); - debug_assert!(num <= u16::MAX as usize, "Too many result values"); - let ty = self.value_type(res); - self.values[res] = ValueData::Inst { - ty, - num: num as u16, - inst, - } - .into(); - } - /// Replace an instruction result with a new value of type `new_type`. /// /// The `old_value` must be an attached instruction result. @@ -904,7 +996,7 @@ impl DataFlowGraph { pub fn replace_result(&mut self, old_value: Value, new_type: Type) -> Value { let (num, inst) = match ValueData::from(self.values[old_value]) { ValueData::Inst { num, inst, .. } => (num, inst), - _ => panic!("{} is not an instruction result value", old_value), + _ => panic!("{old_value} is not an instruction result value"), }; let new_value = self.make_value(ValueData::Inst { ty: new_type, @@ -928,23 +1020,11 @@ impl DataFlowGraph { new_value } - /// Append a new instruction result value to `inst`. - pub fn append_result(&mut self, inst: Inst, ty: Type) -> Value { - let res = self.values.next_key(); - let num = self.results[inst].push(res, &mut self.value_lists); - debug_assert!(num <= u16::MAX as usize, "Too many result values"); - self.make_value(ValueData::Inst { - ty, - inst, - num: num as u16, - }) - } - /// Clone an instruction, attaching new result `Value`s and /// returning them. pub fn clone_inst(&mut self, inst: Inst) -> Inst { // First, add a clone of the InstructionData. - let inst_data = self.insts[inst].clone(); + let inst_data = self.insts[inst]; // If the `inst_data` has a reference to a ValueList, clone it // as well, because we can't share these (otherwise mutating // one would affect the other). @@ -953,7 +1033,13 @@ impl DataFlowGraph { // Get the controlling type variable. let ctrl_typevar = self.ctrl_typevar(inst); // Create new result values. - self.make_inst_results(new_inst, ctrl_typevar); + 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(); + } new_inst } @@ -1165,7 +1251,7 @@ impl DataFlowGraph { if let ValueData::Param { num, block, .. } = ValueData::from(self.values[val]) { (block, num) } else { - panic!("{} must be a block parameter", val); + panic!("{val} must be a block parameter"); }; self.blocks[block] .params @@ -1184,7 +1270,7 @@ impl DataFlowGraph { *old_num = num; self.values[last_arg_val] = last_arg_data.into(); } else { - panic!("{} should be a Block parameter", last_arg_val); + panic!("{last_arg_val} should be a Block parameter"); } } num as usize @@ -1197,7 +1283,7 @@ impl DataFlowGraph { if let ValueData::Param { num, block, .. } = ValueData::from(self.values[val]) { (block, num) } else { - panic!("{} must be a block parameter", val); + panic!("{val} must be a block parameter"); }; self.blocks[block] .params @@ -1257,7 +1343,7 @@ impl DataFlowGraph { if let ValueData::Param { num, block, .. } = ValueData::from(self.values[old_value]) { (block, num) } else { - panic!("{} must be a block parameter", old_value); + panic!("{old_value} must be a block parameter"); }; let new_arg = self.make_value(ValueData::Param { ty: new_type, @@ -1279,6 +1365,38 @@ impl DataFlowGraph { pub fn detach_block_params(&mut self, block: Block) -> ValueList { self.blocks[block].params.take() } + + /// 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. @@ -1315,9 +1433,9 @@ impl<'a> fmt::Display for DisplayInst<'a> { let inst = self.1; if let Some((first, rest)) = dfg.inst_results(inst).split_first() { - write!(f, "{}", first)?; + write!(f, "{first}")?; for v in rest { - write!(f, ", {}", v)?; + write!(f, ", {v}")?; } write!(f, " = ")?; } @@ -1375,7 +1493,7 @@ impl DataFlowGraph { for ty in result_tys { if ty.is_dynamic_vector() { self.check_dynamic_type(ty) - .unwrap_or_else(|| panic!("Use of undeclared dynamic type: {}", ty)); + .unwrap_or_else(|| panic!("Use of undeclared dynamic type: {ty}")); } if let Some(v) = reuse_iter.next() { self.set_value_type_for_parser(v, ty); @@ -1478,8 +1596,7 @@ impl DataFlowGraph { mod tests { use super::*; use crate::cursor::{Cursor, FuncCursor}; - use crate::ir::types; - use crate::ir::{Function, InstructionData, Opcode, TrapCode}; + use crate::ir::{Function, Opcode, TrapCode}; use alloc::string::ToString; #[test] @@ -1651,8 +1768,7 @@ mod tests { }; // Remove `c` from the result list. - pos.func.dfg.clear_results(iadd); - pos.func.dfg.attach_result(iadd, s); + pos.func.stencil.dfg.results[iadd].remove(1, &mut pos.func.stencil.dfg.value_lists); // Replace `uadd_overflow` with a normal `iadd` and an `icmp`. pos.func.dfg.replace(iadd).iadd(v1, arg0); diff --git a/cranelift/codegen/src/ir/dynamic_type.rs b/cranelift/codegen/src/ir/dynamic_type.rs index ef802f90e9ac..d98ced809ef6 100644 --- a/cranelift/codegen/src/ir/dynamic_type.rs +++ b/cranelift/codegen/src/ir/dynamic_type.rs @@ -4,7 +4,6 @@ use crate::ir::entities::DynamicType; use crate::ir::types::*; use crate::ir::GlobalValue; use crate::ir::PrimaryMap; -use crate::ir::Type; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; diff --git a/cranelift/codegen/src/ir/entities.rs b/cranelift/codegen/src/ir/entities.rs index f33e198c0936..e4ed6d55a030 100644 --- a/cranelift/codegen/src/ir/entities.rs +++ b/cranelift/codegen/src/ir/entities.rs @@ -56,8 +56,10 @@ impl Block { /// [`InstBuilder`](super::InstBuilder) instructions: /// /// - [`iconst`](super::InstBuilder::iconst) for integer constants +/// - [`f16const`](super::InstBuilder::f16const) for 16-bit float constants /// - [`f32const`](super::InstBuilder::f32const) for 32-bit float constants /// - [`f64const`](super::InstBuilder::f64const) for 64-bit float constants +/// - [`f128const`](super::InstBuilder::f128const) for 128-bit float constants /// - [`vconst`](super::InstBuilder::vconst) for vector constants /// - [`null`](super::InstBuilder::null) for null reference constants /// @@ -174,18 +176,18 @@ impl DynamicType { /// An opaque reference to a global value. /// -/// A `GlobalValue` is a [`Value`](Value) that will be live across the entire +/// A `GlobalValue` is a [`Value`] that will be live across the entire /// function lifetime. It can be preloaded from other global values. /// /// You can create a `GlobalValue` in the following ways: /// /// - When compiling to WASM, you can use it to load values from a -/// [`VmContext`](super::GlobalValueData::VMContext) using -/// [`FuncEnvironment::make_global`](https://docs.rs/cranelift-wasm/*/cranelift_wasm/trait.FuncEnvironment.html#tymethod.make_global). +/// [`VmContext`](super::GlobalValueData::VMContext) using +/// [`FuncEnvironment::make_global`](https://docs.rs/cranelift-wasm/*/cranelift_wasm/trait.FuncEnvironment.html#tymethod.make_global). /// - When compiling to native code, you can use it for objects in static memory with -/// [`Module::declare_data_in_func`](https://docs.rs/cranelift-module/*/cranelift_module/trait.Module.html#method.declare_data_in_func). +/// [`Module::declare_data_in_func`](https://docs.rs/cranelift-module/*/cranelift_module/trait.Module.html#method.declare_data_in_func). /// - For any compilation target, it can be registered with -/// [`FunctionBuilder::create_global_value`](https://docs.rs/cranelift-frontend/*/cranelift_frontend/struct.FunctionBuilder.html#method.create_global_value). +/// [`FunctionBuilder::create_global_value`](https://docs.rs/cranelift-frontend/*/cranelift_frontend/struct.FunctionBuilder.html#method.create_global_value). /// /// `GlobalValue`s can be retrieved with /// [`InstBuilder:global_value`](super::InstBuilder::global_value). @@ -209,6 +211,29 @@ 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 @@ -300,13 +325,13 @@ impl JumpTable { /// `FuncRef`s can be created with /// /// - [`FunctionBuilder::import_function`](https://docs.rs/cranelift-frontend/*/cranelift_frontend/struct.FunctionBuilder.html#method.import_function) -/// for external functions +/// for external functions /// - [`Module::declare_func_in_func`](https://docs.rs/cranelift-module/*/cranelift_module/trait.Module.html#method.declare_func_in_func) -/// for functions declared elsewhere in the same native -/// [`Module`](https://docs.rs/cranelift-module/*/cranelift_module/trait.Module.html) +/// for functions declared elsewhere in the same native +/// [`Module`](https://docs.rs/cranelift-module/*/cranelift_module/trait.Module.html) /// - [`FuncEnvironment::make_direct_func`](https://docs.rs/cranelift-wasm/*/cranelift_wasm/trait.FuncEnvironment.html#tymethod.make_direct_func) -/// for functions declared in the same WebAssembly -/// [`FuncEnvironment`](https://docs.rs/cranelift-wasm/*/cranelift_wasm/trait.FuncEnvironment.html#tymethod.make_direct_func) +/// for functions declared in the same WebAssembly +/// [`FuncEnvironment`](https://docs.rs/cranelift-wasm/*/cranelift_wasm/trait.FuncEnvironment.html#tymethod.make_direct_func) /// /// While the order is stable, it is arbitrary. #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -365,33 +390,6 @@ impl SigRef { } } -/// An opaque reference to a [WebAssembly -/// table](https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format#WebAssembly_tables). -/// -/// `Table`s are used to store a list of function references. -/// They can be created with [`FuncEnvironment::make_table`](https://docs.rs/cranelift-wasm/*/cranelift_wasm/trait.FuncEnvironment.html#tymethod.make_table). -/// They can be used with -/// [`FuncEnvironment::translate_call_indirect`](https://docs.rs/cranelift-wasm/*/cranelift_wasm/trait.FuncEnvironment.html#tymethod.translate_call_indirect). -/// -/// While the order is stable, it is arbitrary. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub struct Table(u32); -entity_impl!(Table, "table"); - -impl Table { - /// Create a new table 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 any of the entities defined in this module that can appear in CLIF IR. #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -412,6 +410,8 @@ pub enum AnyEntity { DynamicType(DynamicType), /// A Global value. GlobalValue(GlobalValue), + /// A memory type. + MemoryType(MemoryType), /// A jump table. JumpTable(JumpTable), /// A constant. @@ -420,8 +420,6 @@ pub enum AnyEntity { FuncRef(FuncRef), /// A function call signature. SigRef(SigRef), - /// A table. - Table(Table), /// A function's stack limit StackLimit, } @@ -437,11 +435,11 @@ 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), Self::SigRef(r) => r.fmt(f), - Self::Table(r) => r.fmt(f), Self::StackLimit => write!(f, "stack_limit"), } } @@ -495,6 +493,12 @@ 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) @@ -519,17 +523,10 @@ impl From for AnyEntity { } } -impl From for AnyEntity { - fn from(r: Table) -> Self { - Self::Table(r) - } -} - #[cfg(test)] mod tests { use super::*; use alloc::string::ToString; - use core::u32; #[test] fn value_with_number() { diff --git a/cranelift/codegen/src/ir/extfunc.rs b/cranelift/codegen/src/ir/extfunc.rs index cafdb11dc995..319ec4038243 100644 --- a/cranelift/codegen/src/ir/extfunc.rs +++ b/cranelift/codegen/src/ir/extfunc.rs @@ -108,9 +108,9 @@ fn write_list(f: &mut fmt::Formatter, args: &[AbiParam]) -> fmt::Result { match args.split_first() { None => {} Some((first, rest)) => { - write!(f, "{}", first)?; + write!(f, "{first}")?; for arg in rest { - write!(f, ", {}", arg)?; + write!(f, ", {arg}")?; } } } @@ -233,7 +233,13 @@ pub enum ArgumentPurpose { Normal, /// A C struct passed as argument. - StructArgument(u32), + /// + /// Note that this should only be used when interacting with code following + /// a C ABI which is expecting a struct passed *by value*. + StructArgument( + /// The size, in bytes, of the struct. + u32, + ), /// Struct return pointer. /// @@ -250,22 +256,15 @@ pub enum ArgumentPurpose { /// This is a pointer to a context struct containing details about the current sandbox. It is /// used as a base pointer for `vmctx` global values. VMContext, - - /// A stack limit pointer. - /// - /// This is a pointer to a stack limit. It is used to check the current stack pointer - /// against. Can only appear once in a signature. - StackLimit, } impl fmt::Display for ArgumentPurpose { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match self { Self::Normal => "normal", - Self::StructArgument(size) => return write!(f, "sarg({})", size), + Self::StructArgument(size) => return write!(f, "sarg({size})"), Self::StructReturn => "sret", Self::VMContext => "vmctx", - Self::StackLimit => "stack_limit", }) } } @@ -277,7 +276,6 @@ impl FromStr for ArgumentPurpose { "normal" => Ok(Self::Normal), "sret" => Ok(Self::StructReturn), "vmctx" => Ok(Self::VMContext), - "stack_limit" => Ok(Self::StackLimit), _ if s.starts_with("sarg(") => { if !s.ends_with(")") { return Err(()); @@ -374,7 +372,6 @@ mod tests { (ArgumentPurpose::Normal, "normal"), (ArgumentPurpose::StructReturn, "sret"), (ArgumentPurpose::VMContext, "vmctx"), - (ArgumentPurpose::StackLimit, "stack_limit"), (ArgumentPurpose::StructArgument(42), "sarg(42)"), ]; for &(e, n) in &all_purpose { diff --git a/cranelift/codegen/src/ir/extname.rs b/cranelift/codegen/src/ir/extname.rs index b06eb843f536..8484376b5a87 100644 --- a/cranelift/codegen/src/ir/extname.rs +++ b/cranelift/codegen/src/ir/extname.rs @@ -105,7 +105,7 @@ impl fmt::Display for TestcaseName { impl fmt::Debug for TestcaseName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self) + write!(f, "{self}") } } @@ -204,8 +204,8 @@ impl<'a> fmt::Display for DisplayableExternalName<'a> { } } ExternalName::TestCase(testcase) => testcase.fmt(f), - ExternalName::LibCall(lc) => write!(f, "%{}", lc), - ExternalName::KnownSymbol(ks) => write!(f, "%{}", ks), + ExternalName::LibCall(lc) => write!(f, "%{lc}"), + ExternalName::KnownSymbol(ks) => write!(f, "%{ks}"), } } } diff --git a/cranelift/codegen/src/ir/function.rs b/cranelift/codegen/src/ir/function.rs index f5b85c50af80..39dfbbf6435d 100644 --- a/cranelift/codegen/src/ir/function.rs +++ b/cranelift/codegen/src/ir/function.rs @@ -5,10 +5,10 @@ use crate::entity::{PrimaryMap, SecondaryMap}; use crate::ir::{ - self, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData, DynamicStackSlots, - DynamicType, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Inst, JumpTable, - JumpTableData, Layout, Opcode, SigRef, Signature, SourceLocs, StackSlot, StackSlotData, - StackSlots, Table, TableData, Type, + self, pcc::Fact, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData, + DynamicStackSlots, DynamicType, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Inst, + JumpTable, JumpTableData, Layout, MemoryType, MemoryTypeData, Opcode, SigRef, Signature, + SourceLocs, StackSlot, StackSlotData, StackSlots, Type, }; use crate::isa::CallConv; use crate::write::write_function; @@ -172,8 +172,11 @@ pub struct FunctionStencil { /// Global values referenced. pub global_values: PrimaryMap, - /// Tables referenced. - pub tables: 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, @@ -201,7 +204,8 @@ impl FunctionStencil { self.sized_stack_slots.clear(); self.dynamic_stack_slots.clear(); self.global_values.clear(); - self.tables.clear(); + self.global_value_facts.clear(); + self.memory_types.clear(); self.dfg.clear(); self.layout.clear(); self.srclocs.clear(); @@ -235,6 +239,11 @@ 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 @@ -251,15 +260,10 @@ impl FunctionStencil { self.dfg .dynamic_types .get(ty) - .unwrap_or_else(|| panic!("Undeclared dynamic vector type: {}", ty)) + .unwrap_or_else(|| panic!("Undeclared dynamic vector type: {ty}")) .concrete() } - /// Declares a table accessible to the function. - pub fn create_table(&mut self, data: TableData) -> Table { - self.tables.push(data) - } - /// Find a presumed unique special-purpose function parameter value. /// /// Returns the value of the last `purpose` parameter, or `None` if no such parameter exists. @@ -309,12 +313,32 @@ impl FunctionStencil { Ok(()) } + /// Returns an iterator over the blocks succeeding the given block. + pub fn block_successors(&self, block: Block) -> impl DoubleEndedIterator + '_ { + self.layout.last_inst(block).into_iter().flat_map(|inst| { + self.dfg.insts[inst] + .branch_destination(&self.dfg.jump_tables) + .iter() + .map(|block| block.block(&self.dfg.value_lists)) + }) + } + /// Returns true if the function is function that doesn't call any other functions. This is not /// to be confused with a "leaf function" in Windows terminology. pub fn is_leaf(&self) -> bool { // Conservative result: if there's at least one function signature referenced in this // function, assume it is not a leaf. - self.dfg.signatures.is_empty() + let has_signatures = !self.dfg.signatures.is_empty(); + + // Under some TLS models, retrieving the address of a TLS variable requires calling a + // function. Conservatively assume that any function that references a tls global value + // is not a leaf. + let has_tls = self.global_values.values().any(|gv| match gv { + GlobalValueData::Symbol { tls, .. } => *tls, + _ => false, + }); + + !has_signatures && !has_tls } /// Replace the `dst` instruction's data with the `src` instruction's data @@ -398,7 +422,8 @@ impl Function { sized_stack_slots: StackSlots::new(), dynamic_stack_slots: DynamicStackSlots::new(), global_values: PrimaryMap::new(), - tables: 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/globalvalue.rs b/cranelift/codegen/src/ir/globalvalue.rs index 530875641e43..89120c8b8364 100644 --- a/cranelift/codegen/src/ir/globalvalue.rs +++ b/cranelift/codegen/src/ir/globalvalue.rs @@ -1,7 +1,7 @@ //! Global values. use crate::ir::immediates::{Imm64, Offset32}; -use crate::ir::{ExternalName, GlobalValue, Type}; +use crate::ir::{ExternalName, GlobalValue, MemFlags, Type}; use crate::isa::TargetIsa; use core::fmt; @@ -9,7 +9,7 @@ use core::fmt; use serde_derive::{Deserialize, Serialize}; /// Information about a global value declaration. -#[derive(Clone, PartialEq, Hash)] +#[derive(Debug, Clone, PartialEq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub enum GlobalValueData { /// Value is the address of the VM context struct. @@ -31,9 +31,8 @@ pub enum GlobalValueData { /// Type of the loaded value. global_type: Type, - /// Specifies whether the memory that this refers to is readonly, allowing for the - /// elimination of redundant loads. - readonly: bool, + /// Specifies the memory flags to be used by the load. Guaranteed to be notrap and aligned. + flags: MemFlags, }, /// Value is an offset from another global value. @@ -111,20 +110,13 @@ impl fmt::Display for GlobalValueData { base, offset, global_type, - readonly, - } => write!( - f, - "load.{} notrap aligned {}{}{}", - global_type, - if readonly { "readonly " } else { "" }, - base, - offset - ), + flags, + } => write!(f, "load.{global_type}{flags} {base}{offset}"), Self::IAddImm { global_type, base, offset, - } => write!(f, "iadd_imm.{} {}, {}", global_type, base, offset), + } => write!(f, "iadd_imm.{global_type} {base}, {offset}"), Self::Symbol { ref name, offset, @@ -143,12 +135,12 @@ impl fmt::Display for GlobalValueData { write!(f, "+")?; } if offset_val != 0 { - write!(f, "{}", offset)?; + write!(f, "{offset}")?; } Ok(()) } Self::DynScaleTargetConst { vector_type } => { - write!(f, "dyn_scale_target_const.{}", vector_type) + write!(f, "dyn_scale_target_const.{vector_type}") } } } diff --git a/cranelift/codegen/src/ir/immediates.rs b/cranelift/codegen/src/ir/immediates.rs index 9900cb8064e7..7ec7ab8efad1 100644 --- a/cranelift/codegen/src/ir/immediates.rs +++ b/cranelift/codegen/src/ir/immediates.rs @@ -6,7 +6,6 @@ use alloc::vec::Vec; use core::cmp::Ordering; -use core::convert::TryFrom; use core::fmt::{self, Display, Formatter}; use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Sub}; use core::str::FromStr; @@ -75,19 +74,38 @@ impl Imm64 { self.0 } + /// Mask this immediate to the given power-of-two bit width. + #[must_use] + pub(crate) fn mask_to_width(&self, bit_width: u32) -> Self { + debug_assert!(bit_width.is_power_of_two()); + + if bit_width >= 64 { + return *self; + } + + let bit_width = i64::from(bit_width); + let mask = (1 << bit_width) - 1; + let masked = self.0 & mask; + Imm64(masked) + } + /// Sign extend this immediate as if it were a signed integer of the given /// power-of-two width. - pub fn sign_extend_from_width(&mut self, bit_width: u32) { - debug_assert!(bit_width.is_power_of_two()); + #[must_use] + pub(crate) fn sign_extend_from_width(&self, bit_width: u32) -> Self { + debug_assert!( + bit_width.is_power_of_two(), + "{bit_width} is not a power of two" + ); if bit_width >= 64 { - return; + return *self; } let bit_width = i64::from(bit_width); let delta = 64 - bit_width; let sign_extended = (self.0 << delta) >> delta; - *self = Imm64(sign_extended); + Imm64(sign_extended) } } @@ -112,9 +130,9 @@ impl From for Imm64 { impl Display for Imm64 { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let x = self.0; - if -10_000 < x && x < 10_000 { - // Use decimal for small numbers. - write!(f, "{}", x) + if x < 10_000 { + // Use decimal for small and negative numbers. + write!(f, "{x}") } else { write_hex(x as u64, f) } @@ -205,7 +223,7 @@ impl Display for Uimm64 { let x = self.0; if x < 10_000 { // Use decimal for small numbers. - write!(f, "{}", x) + write!(f, "{x}") } else { write_hex(x, f) } @@ -219,9 +237,9 @@ fn parse_u64(s: &str) -> Result { if s.starts_with("-0x") { return Err("Invalid character in hexadecimal number"); - } else if s.starts_with("0x") { + } else if let Some(num) = s.strip_prefix("0x") { // Hexadecimal. - for ch in s[2..].chars() { + for ch in num.chars() { match ch.to_digit(16) { Some(digit) => { digits += 1; @@ -242,7 +260,7 @@ fn parse_u64(s: &str) -> Result { } else { // Decimal number, possibly negative. for ch in s.chars() { - match ch.to_digit(16) { + match ch.to_digit(10) { Some(digit) => { digits += 1; match value.checked_mul(10) { @@ -444,7 +462,7 @@ impl Display for Offset32 { let val = i64::from(self.0).abs(); if val < 10_000 { - write!(f, "{}", val) + write!(f, "{val}") } else { write_hex(val as u64, f) } @@ -469,35 +487,381 @@ impl FromStr for Offset32 { } } -/// An IEEE binary32 immediate floating point value, represented as a u32 -/// containing the bit pattern. -/// -/// We specifically avoid using a f32 here since some architectures may silently alter floats. -/// See: -/// -/// The [PartialEq] and [Hash] implementations are over the underlying bit pattern, but -/// [PartialOrd] respects IEEE754 semantics. -/// -/// All bit patterns are allowed. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[repr(C)] -pub struct Ieee32(u32); +// FIXME(rust-lang/rust#83527): Replace with `${ignore()}` once it is stabilised. +macro_rules! ignore { + ($($t:tt)*) => {}; +} + +macro_rules! ieee_float { + ( + name = $name:ident, + bits = $bits:literal, + significand_bits = $significand_bits:literal, + bits_ty = $bits_ty:ident, + float_ty = $float_ty:ident, + $(as_float = $as_float:ident,)? + $(rust_type_not_stable = $rust_type_not_stable:ident,)? + ) => { + /// An IEEE + #[doc = concat!("binary", stringify!($bits))] + /// immediate floating point value, represented as a + #[doc = stringify!($bits_ty)] + /// containing the bit pattern. + /// + /// We specifically avoid using a + #[doc = stringify!($float_ty)] + /// here since some architectures may silently alter floats. + /// See: + /// + /// The [PartialEq] and [Hash] implementations are over the underlying bit pattern, but + /// [PartialOrd] respects IEEE754 semantics. + /// + /// All bit patterns are allowed. + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] + #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] + #[repr(C)] + pub struct $name { + bits: $bits_ty + } -/// An IEEE binary64 immediate floating point value, represented as a u64 -/// containing the bit pattern. -/// -/// We specifically avoid using a f64 here since some architectures may silently alter floats. -/// See: -/// -/// The [PartialEq] and [Hash] implementations are over the underlying bit pattern, but -/// [PartialOrd] respects IEEE754 semantics. -/// -/// All bit patterns are allowed. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[repr(C)] -pub struct Ieee64(u64); + impl $name { + const BITS: u8 = $bits; + const SIGNIFICAND_BITS: u8 = $significand_bits; + const EXPONENT_BITS: u8 = Self::BITS - Self::SIGNIFICAND_BITS - 1; + const SIGN_MASK: $bits_ty = 1 << (Self::EXPONENT_BITS + Self::SIGNIFICAND_BITS); + const SIGNIFICAND_MASK: $bits_ty = $bits_ty::MAX >> (Self::EXPONENT_BITS + 1); + const EXPONENT_MASK: $bits_ty = !Self::SIGN_MASK & !Self::SIGNIFICAND_MASK; + /// The positive WebAssembly canonical NaN. + pub const NAN: Self = Self::with_bits(Self::EXPONENT_MASK | (1 << (Self::SIGNIFICAND_BITS - 1))); + + /// Create a new + #[doc = concat!("`", stringify!($name), "`")] + /// containing the bits of `bits`. + pub const fn with_bits(bits: $bits_ty) -> Self { + Self { bits } + } + + /// Get the bitwise representation. + pub fn bits(self) -> $bits_ty { + self.bits + } + + $( + /// Create a new + #[doc = concat!("`", stringify!($name), "`")] + /// representing the number `x`. + pub fn with_float(x: $float_ty) -> Self { + Self::with_bits(x.to_bits()) + } + + /// Converts `self` to a Rust + #[doc = concat!("`", stringify!($float_ty), "`.")] + pub fn $as_float(self) -> $float_ty { + $float_ty::from_bits(self.bits()) + } + )? + + /// Computes the absolute value of `self`. + pub fn abs(self) -> Self { + Self::with_bits(self.bits() & !Self::SIGN_MASK) + } + + /// Returns a number composed of the magnitude of `self` and the sign of `sign`. + pub fn copysign(self, sign: Self) -> Self { + Self::with_bits((self.bits() & !Self::SIGN_MASK) | (sign.bits() & Self::SIGN_MASK)) + } + + /// Returns the minimum of `self` and `other`, following the WebAssembly/IEEE 754-2019 definition. + pub fn minimum(self, other: Self) -> Self { + // FIXME: Replace with Rust float method once it is stabilised. + if self.is_nan() || other.is_nan() { + Self::NAN + } else if self.is_zero() && other.is_zero() { + if self.is_negative() { + self + } else { + other + } + } else if self <= other { + self + } else { + other + } + } + + /// Returns the maximum of `self` and `other`, following the WebAssembly/IEEE 754-2019 definition. + pub fn maximum(self, other: Self) -> Self { + // FIXME: Replace with Rust float method once it is stabilised. + if self.is_nan() || other.is_nan() { + Self::NAN + } else if self.is_zero() && other.is_zero() { + if self.is_positive() { + self + } else { + other + } + } else if self >= other { + self + } else { + other + } + } + + /// Create an + #[doc = concat!("`", stringify!($name), "`")] + /// number representing `2.0^n`. + pub fn pow2>(n: I) -> Self { + let n = n.into(); + let w = Self::EXPONENT_BITS; + let t = Self::SIGNIFICAND_BITS; + let bias = (1 << (w - 1)) - 1; + let exponent = n + bias; + assert!(exponent > 0, "Underflow n={}", n); + assert!(exponent < (1 << w) + 1, "Overflow n={}", n); + Self::with_bits((exponent as $bits_ty) << t) + } + + /// Create an + #[doc = concat!("`", stringify!($name), "`")] + /// number representing the greatest negative value not convertible from + #[doc = concat!("`", stringify!($float_ty), "`")] + /// to a signed integer with width n. + pub fn fcvt_to_sint_negative_overflow>(n: I) -> Self { + let n = n.into(); + debug_assert!(n < i32::from(Self::BITS)); + debug_assert!(i32::from(Self::SIGNIFICAND_BITS) + 1 - n < i32::from(Self::BITS)); + Self::with_bits((1 << (Self::BITS - 1)) | Self::pow2(n - 1).bits() | (1 << (i32::from(Self::SIGNIFICAND_BITS) + 1 - n))) + } + + /// Check if the value is a NaN. For + #[doc = concat!("`", stringify!($name), "`,")] + /// this means checking that all the exponent bits are set and the significand is non-zero. + pub fn is_nan(self) -> bool { + self.abs().bits() > Self::EXPONENT_MASK + } + + /// Returns true if `self` has a negative sign, including 0.0, NaNs with positive sign bit and positive infinity. + pub fn is_positive(self) -> bool { + !self.is_negative() + } + + /// Returns true if `self` has a negative sign, including -0.0, NaNs with negative sign bit and negative infinity. + pub fn is_negative(self) -> bool { + self.bits() & Self::SIGN_MASK == Self::SIGN_MASK + } + + /// Returns `true` if `self` is positive or negative zero. + pub fn is_zero(self) -> bool { + self.abs().bits() == 0 + } + + /// Returns `None` if `self` is a NaN and `Some(self)` otherwise. + pub fn non_nan(self) -> Option { + Some(self).filter(|f| !f.is_nan()) + } + + $( + /// Returns the square root of `self`. + pub fn sqrt(self) -> Self { + Self::with_float(self.$as_float().sqrt()) + } + + /// Returns the smallest integer greater than or equal to `self`. + pub fn ceil(self) -> Self { + Self::with_float(self.$as_float().ceil()) + } + + /// Returns the largest integer less than or equal to `self`. + pub fn floor(self) -> Self { + Self::with_float(self.$as_float().floor()) + } + + /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero. + pub fn trunc(self) -> Self { + Self::with_float(self.$as_float().trunc()) + } + + /// Returns the nearest integer to `self`. Rounds half-way cases to the number + /// with an even least significant digit. + pub fn round_ties_even(self) -> Self { + Self::with_float(self.$as_float().round_ties_even()) + } + )? + } + + impl PartialOrd for $name { + fn partial_cmp(&self, rhs: &Self) -> Option { + $(self.$as_float().partial_cmp(&rhs.$as_float()))? + $( + ignore!($rust_type_not_stable); + // FIXME(#8312): Use builtin Rust comparisons once `f16` and `f128` support is stabalised. + if self.is_nan() || rhs.is_nan() { + // One of the floats is a NaN. + return None; + } + if self.is_zero() || rhs.is_zero() { + // Zeros are always equal regardless of sign. + return Some(Ordering::Equal); + } + let lhs_positive = self.is_positive(); + let rhs_positive = rhs.is_positive(); + if lhs_positive != rhs_positive { + // Different signs: negative < positive + return lhs_positive.partial_cmp(&rhs_positive); + } + // Finite or infinity will order correctly with an integer comparison of the bits. + if lhs_positive { + self.bits().partial_cmp(&rhs.bits()) + } else { + // Reverse the comparison when both floats are negative. + rhs.bits().partial_cmp(&self.bits()) + } + )? + } + } + + impl Display for $name { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + format_float(u128::from(self.bits()), Self::EXPONENT_BITS, Self::SIGNIFICAND_BITS, f) + } + } + + impl FromStr for $name { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match parse_float(s, Self::EXPONENT_BITS, Self::SIGNIFICAND_BITS) { + Ok(b) => Ok(Self::with_bits(b.try_into().unwrap())), + Err(s) => Err(s), + } + } + } + + impl IntoBytes for $name { + fn into_bytes(self) -> Vec { + self.bits().to_le_bytes().to_vec() + } + } + + impl Neg for $name { + type Output = Self; + + fn neg(self) -> Self { + Self::with_bits(self.bits() ^ Self::SIGN_MASK) + } + } + + + + $( + impl From<$float_ty> for $name { + fn from(x: $float_ty) -> Self { + Self::with_float(x) + } + } + + impl Add for $name { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self::with_float(self.$as_float() + rhs.$as_float()) + } + } + + impl Sub for $name { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Self::with_float(self.$as_float() - rhs.$as_float()) + } + } + + impl Mul for $name { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + Self::with_float(self.$as_float() * rhs.$as_float()) + } + } + + impl Div for $name { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self::with_float(self.$as_float() / rhs.$as_float()) + } + } + )? + + impl BitAnd for $name { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self { + Self::with_bits(self.bits() & rhs.bits()) + } + } + + impl BitOr for $name { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self { + Self::with_bits(self.bits() | rhs.bits()) + } + } + + impl BitXor for $name { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self { + Self::with_bits(self.bits() ^ rhs.bits()) + } + } + + impl Not for $name { + type Output = Self; + + fn not(self) -> Self { + Self::with_bits(!self.bits()) + } + } + }; +} + +ieee_float! { + name = Ieee16, + bits = 16, + significand_bits = 10, + bits_ty = u16, + float_ty = f16, + rust_type_not_stable = rust_type_not_stable, +} + +ieee_float! { + name = Ieee32, + bits = 32, + significand_bits = 23, + bits_ty = u32, + float_ty = f32, + as_float = as_f32, +} + +ieee_float! { + name = Ieee64, + bits = 64, + significand_bits = 52, + bits_ty = u64, + float_ty = f64, + as_float = as_f64, +} + +ieee_float! { + name = Ieee128, + bits = 128, + significand_bits = 112, + bits_ty = u128, + float_ty = f128, + rust_type_not_stable = rust_type_not_stable, +} /// Format a floating point number in a way that is reasonably human-readable, and that can be /// converted back to binary without any rounding issues. The hexadecimal formatting of normal and @@ -509,13 +873,13 @@ pub struct Ieee64(u64); /// w - exponent field width in bits /// t - trailing significand field width in bits /// -fn format_float(bits: u64, w: u8, t: u8, f: &mut Formatter) -> fmt::Result { +fn format_float(bits: u128, w: u8, t: u8, f: &mut Formatter) -> fmt::Result { debug_assert!(w > 0 && w <= 16, "Invalid exponent range"); - debug_assert!(1 + w + t <= 64, "Too large IEEE format for u64"); + debug_assert!(1 + w + t <= 128, "Too large IEEE format for u128"); debug_assert!((t + w + 1).is_power_of_two(), "Unexpected IEEE format size"); - let max_e_bits = (1u64 << w) - 1; - let t_bits = bits & ((1u64 << t) - 1); // Trailing significand. + let max_e_bits = (1u128 << w) - 1; + let t_bits = bits & ((1u128 << t) - 1); // Trailing significand. let e_bits = (bits >> t) & max_e_bits; // Biased exponent. let sign_bit = (bits >> (w + t)) & 1; @@ -562,13 +926,13 @@ fn format_float(bits: u64, w: u8, t: u8, f: &mut Formatter) -> fmt::Result { if t_bits & (1 << (t - 1)) != 0 { // Quiet NaN. if payload != 0 { - write!(f, "NaN:0x{:x}", payload) + write!(f, "NaN:0x{payload:x}") } else { write!(f, "NaN") } } else { // Signaling NaN. - write!(f, "sNaN:0x{:x}", payload) + write!(f, "sNaN:0x{payload:x}") } } } else { @@ -584,22 +948,22 @@ fn format_float(bits: u64, w: u8, t: u8, f: &mut Formatter) -> fmt::Result { /// w - exponent field width in bits /// t - trailing significand field width in bits /// -fn parse_float(s: &str, w: u8, t: u8) -> Result { +fn parse_float(s: &str, w: u8, t: u8) -> Result { debug_assert!(w > 0 && w <= 16, "Invalid exponent range"); - debug_assert!(1 + w + t <= 64, "Too large IEEE format for u64"); + debug_assert!(1 + w + t <= 128, "Too large IEEE format for u128"); debug_assert!((t + w + 1).is_power_of_two(), "Unexpected IEEE format size"); - let (sign_bit, s2) = if s.starts_with('-') { - (1u64 << (t + w), &s[1..]) - } else if s.starts_with('+') { - (0, &s[1..]) + let (sign_bit, s2) = if let Some(num) = s.strip_prefix('-') { + (1u128 << (t + w), num) + } else if let Some(num) = s.strip_prefix('+') { + (0, num) } else { (0, s) }; if !s2.starts_with("0x") { - let max_e_bits = ((1u64 << w) - 1) << t; - let quiet_bit = 1u64 << (t - 1); + let max_e_bits = ((1u128 << w) - 1) << t; + let quiet_bit = 1u128 << (t - 1); // The only decimal encoding allowed is 0. if s2 == "0.0" { @@ -614,18 +978,18 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result { // Canonical quiet NaN: e = max, t = quiet. return Ok(sign_bit | max_e_bits | quiet_bit); } - if s2.starts_with("NaN:0x") { + if let Some(nan) = s2.strip_prefix("NaN:0x") { // Quiet NaN with payload. - return match u64::from_str_radix(&s2[6..], 16) { + return match u128::from_str_radix(nan, 16) { Ok(payload) if payload < quiet_bit => { Ok(sign_bit | max_e_bits | quiet_bit | payload) } _ => Err("Invalid NaN payload"), }; } - if s2.starts_with("sNaN:0x") { + if let Some(nan) = s2.strip_prefix("sNaN:0x") { // Signaling NaN with payload. - return match u64::from_str_radix(&s2[7..], 16) { + return match u128::from_str_radix(nan, 16) { Ok(payload) if 0 < payload && payload < quiet_bit => { Ok(sign_bit | max_e_bits | payload) } @@ -639,7 +1003,7 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result { let mut digits = 0u8; let mut digits_before_period: Option = None; - let mut significand = 0u64; + let mut significand = 0u128; let mut exponent = 0i32; for (idx, ch) in s3.char_indices() { @@ -666,10 +1030,10 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result { _ => match ch.to_digit(16) { Some(digit) => { digits += 1; - if digits > 16 { + if digits > 32 { return Err("Too many digits"); } - significand = (significand << 4) | u64::from(digit); + significand = (significand << 4) | u128::from(digit); } None => return Err("Invalid character"), }, @@ -692,10 +1056,10 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result { }; // Normalize the significand and exponent. - let significant_bits = (64 - significand.leading_zeros()) as u8; + let significant_bits = (128 - significand.leading_zeros()) as u8; if significant_bits > t + 1 { let adjust = significant_bits - (t + 1); - if significand & ((1u64 << adjust) - 1) != 0 { + if significand & ((1u128 << adjust) - 1) != 0 { return Err("Too many significant bits"); } // Adjust significand down. @@ -719,13 +1083,13 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result { Err("Magnitude too large") } else if exponent > 0 { // This is a normal number. - let e_bits = (exponent as u64) << t; + let e_bits = (exponent as u128) << t; Ok(sign_bit | e_bits | t_bits) } else if 1 - exponent <= i32::from(t) { // This is a subnormal number: e = 0, t = significand bits. // Renormalize significand for exponent = 1. let adjust = 1 - exponent; - if significand & ((1u64 << adjust) - 1) != 0 { + if significand & ((1u128 << adjust) - 1) != 0 { Err("Subnormal underflow") } else { significand >>= adjust; @@ -736,454 +1100,10 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result { } } -impl Ieee32 { - /// Create a new `Ieee32` containing the bits of `x`. - pub fn with_bits(x: u32) -> Self { - Self(x) - } - - /// Create an `Ieee32` number representing `2.0^n`. - pub fn pow2>(n: I) -> Self { - let n = n.into(); - let w = 8; - let t = 23; - let bias = (1 << (w - 1)) - 1; - let exponent = (n + bias) as u32; - assert!(exponent > 0, "Underflow n={}", n); - assert!(exponent < (1 << w) + 1, "Overflow n={}", n); - Self(exponent << t) - } - - /// Create an `Ieee32` number representing the greatest negative value - /// not convertable from f32 to a signed integer with width n. - pub fn fcvt_to_sint_negative_overflow>(n: I) -> Self { - let n = n.into(); - debug_assert!(n < 32); - debug_assert!(23 + 1 - n < 32); - Self::with_bits((1u32 << (32 - 1)) | Self::pow2(n - 1).0 | (1u32 << (23 + 1 - n))) - } - - /// Return self negated. - pub fn neg(self) -> Self { - Self(self.0 ^ (1 << 31)) - } - - /// Create a new `Ieee32` representing the number `x`. - pub fn with_float(x: f32) -> Self { - Self(x.to_bits()) - } - - /// Get the bitwise representation. - pub fn bits(self) -> u32 { - self.0 - } - - /// Check if the value is a NaN. - pub fn is_nan(&self) -> bool { - self.as_f32().is_nan() - } - - /// Converts Self to a rust f32 - pub fn as_f32(self) -> f32 { - f32::from_bits(self.0) - } - - /// Returns the square root of self. - pub fn sqrt(self) -> Self { - Self::with_float(self.as_f32().sqrt()) - } - - /// Computes the absolute value of self. - pub fn abs(self) -> Self { - Self::with_float(self.as_f32().abs()) - } - - /// Returns a number composed of the magnitude of self and the sign of sign. - pub fn copysign(self, sign: Self) -> Self { - Self::with_float(self.as_f32().copysign(sign.as_f32())) - } - - /// Returns true if self has a negative sign, including -0.0, NaNs with negative sign bit and negative infinity. - pub fn is_negative(&self) -> bool { - self.as_f32().is_sign_negative() - } - - /// Returns true if self is positive or negative zero - pub fn is_zero(&self) -> bool { - self.as_f32() == 0.0 - } - - /// Returns the smallest integer greater than or equal to `self`. - pub fn ceil(self) -> Self { - Self::with_float(self.as_f32().ceil()) - } - - /// Returns the largest integer less than or equal to `self`. - pub fn floor(self) -> Self { - Self::with_float(self.as_f32().floor()) - } - - /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero. - pub fn trunc(self) -> Self { - Self::with_float(self.as_f32().trunc()) - } - - /// Returns the nearest integer to `self`. Rounds half-way cases to the number - /// with an even least significant digit. - pub fn round_ties_even(self) -> Self { - // TODO: Replace with the native implementation once - // https://github.com/rust-lang/rust/issues/96710 is stabilized - let toint_32: f32 = 1.0 / f32::EPSILON; - - let f = self.as_f32(); - let e = self.0 >> 23 & 0xff; - if e >= 0x7f_u32 + 23 { - self - } else { - Self::with_float((f.abs() + toint_32 - toint_32).copysign(f)) - } - } -} - -impl PartialOrd for Ieee32 { - fn partial_cmp(&self, other: &Self) -> Option { - self.as_f32().partial_cmp(&other.as_f32()) - } -} - -impl Display for Ieee32 { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let bits: u32 = self.0; - format_float(u64::from(bits), 8, 23, f) - } -} - -impl FromStr for Ieee32 { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match parse_float(s, 8, 23) { - Ok(b) => Ok(Self(b as u32)), - Err(s) => Err(s), - } - } -} - -impl From for Ieee32 { - fn from(x: f32) -> Self { - Self::with_float(x) - } -} - -impl IntoBytes for Ieee32 { - fn into_bytes(self) -> Vec { - self.0.to_le_bytes().to_vec() - } -} - -impl Neg for Ieee32 { - type Output = Ieee32; - - fn neg(self) -> Self::Output { - Self::with_float(self.as_f32().neg()) - } -} - -impl Add for Ieee32 { - type Output = Ieee32; - - fn add(self, rhs: Self) -> Self::Output { - Self::with_float(self.as_f32() + rhs.as_f32()) - } -} - -impl Sub for Ieee32 { - type Output = Ieee32; - - fn sub(self, rhs: Self) -> Self::Output { - Self::with_float(self.as_f32() - rhs.as_f32()) - } -} - -impl Mul for Ieee32 { - type Output = Ieee32; - - fn mul(self, rhs: Self) -> Self::Output { - Self::with_float(self.as_f32() * rhs.as_f32()) - } -} - -impl Div for Ieee32 { - type Output = Ieee32; - - fn div(self, rhs: Self) -> Self::Output { - Self::with_float(self.as_f32() / rhs.as_f32()) - } -} - -impl BitAnd for Ieee32 { - type Output = Ieee32; - - fn bitand(self, rhs: Self) -> Self::Output { - Self::with_bits(self.bits() & rhs.bits()) - } -} - -impl BitOr for Ieee32 { - type Output = Ieee32; - - fn bitor(self, rhs: Self) -> Self::Output { - Self::with_bits(self.bits() | rhs.bits()) - } -} - -impl BitXor for Ieee32 { - type Output = Ieee32; - - fn bitxor(self, rhs: Self) -> Self::Output { - Self::with_bits(self.bits() ^ rhs.bits()) - } -} - -impl Not for Ieee32 { - type Output = Ieee32; - - fn not(self) -> Self::Output { - Self::with_bits(!self.bits()) - } -} - -impl Ieee64 { - /// Create a new `Ieee64` containing the bits of `x`. - pub fn with_bits(x: u64) -> Self { - Self(x) - } - - /// Create an `Ieee64` number representing `2.0^n`. - pub fn pow2>(n: I) -> Self { - let n = n.into(); - let w = 11; - let t = 52; - let bias = (1 << (w - 1)) - 1; - let exponent = (n + bias) as u64; - assert!(exponent > 0, "Underflow n={}", n); - assert!(exponent < (1 << w) + 1, "Overflow n={}", n); - Self(exponent << t) - } - - /// Create an `Ieee64` number representing the greatest negative value - /// not convertable from f64 to a signed integer with width n. - pub fn fcvt_to_sint_negative_overflow>(n: I) -> Self { - let n = n.into(); - debug_assert!(n < 64); - debug_assert!(52 + 1 - n < 64); - Self::with_bits((1u64 << (64 - 1)) | Self::pow2(n - 1).0 | (1u64 << (52 + 1 - n))) - } - - /// Return self negated. - pub fn neg(self) -> Self { - Self(self.0 ^ (1 << 63)) - } - - /// Create a new `Ieee64` representing the number `x`. - pub fn with_float(x: f64) -> Self { - Self(x.to_bits()) - } - - /// Get the bitwise representation. - pub fn bits(self) -> u64 { - self.0 - } - - /// Check if the value is a NaN. For [Ieee64], this means checking that the 11 exponent bits are - /// all set. - pub fn is_nan(&self) -> bool { - self.as_f64().is_nan() - } - - /// Converts Self to a rust f64 - pub fn as_f64(self) -> f64 { - f64::from_bits(self.0) - } - - /// Returns the square root of self. - pub fn sqrt(self) -> Self { - Self::with_float(self.as_f64().sqrt()) - } - - /// Computes the absolute value of self. - pub fn abs(self) -> Self { - Self::with_float(self.as_f64().abs()) - } - - /// Returns a number composed of the magnitude of self and the sign of sign. - pub fn copysign(self, sign: Self) -> Self { - Self::with_float(self.as_f64().copysign(sign.as_f64())) - } - - /// Returns true if self has a negative sign, including -0.0, NaNs with negative sign bit and negative infinity. - pub fn is_negative(&self) -> bool { - self.as_f64().is_sign_negative() - } - - /// Returns true if self is positive or negative zero - pub fn is_zero(&self) -> bool { - self.as_f64() == 0.0 - } - - /// Returns the smallest integer greater than or equal to `self`. - pub fn ceil(self) -> Self { - Self::with_float(self.as_f64().ceil()) - } - - /// Returns the largest integer less than or equal to `self`. - pub fn floor(self) -> Self { - Self::with_float(self.as_f64().floor()) - } - - /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero. - pub fn trunc(self) -> Self { - Self::with_float(self.as_f64().trunc()) - } - - /// Returns the nearest integer to `self`. Rounds half-way cases to the number - /// with an even least significant digit. - pub fn round_ties_even(self) -> Self { - // TODO: Replace with the native implementation once - // https://github.com/rust-lang/rust/issues/96710 is stabilized - let toint_64: f64 = 1.0 / f64::EPSILON; - - let f = self.as_f64(); - let e = self.0 >> 52 & 0x7ff_u64; - if e >= 0x3ff_u64 + 52 { - self - } else { - Self::with_float((f.abs() + toint_64 - toint_64).copysign(f)) - } - } -} - -impl PartialOrd for Ieee64 { - fn partial_cmp(&self, other: &Self) -> Option { - self.as_f64().partial_cmp(&other.as_f64()) - } -} - -impl Display for Ieee64 { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let bits: u64 = self.0; - format_float(bits, 11, 52, f) - } -} - -impl FromStr for Ieee64 { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match parse_float(s, 11, 52) { - Ok(b) => Ok(Self(b)), - Err(s) => Err(s), - } - } -} - -impl From for Ieee64 { - fn from(x: f64) -> Self { - Self::with_float(x) - } -} - -impl From for Ieee64 { - fn from(x: u64) -> Self { - Self::with_float(f64::from_bits(x)) - } -} - -impl IntoBytes for Ieee64 { - fn into_bytes(self) -> Vec { - self.0.to_le_bytes().to_vec() - } -} - -impl Neg for Ieee64 { - type Output = Ieee64; - - fn neg(self) -> Self::Output { - Self::with_float(self.as_f64().neg()) - } -} - -impl Add for Ieee64 { - type Output = Ieee64; - - fn add(self, rhs: Self) -> Self::Output { - Self::with_float(self.as_f64() + rhs.as_f64()) - } -} - -impl Sub for Ieee64 { - type Output = Ieee64; - - fn sub(self, rhs: Self) -> Self::Output { - Self::with_float(self.as_f64() - rhs.as_f64()) - } -} - -impl Mul for Ieee64 { - type Output = Ieee64; - - fn mul(self, rhs: Self) -> Self::Output { - Self::with_float(self.as_f64() * rhs.as_f64()) - } -} - -impl Div for Ieee64 { - type Output = Ieee64; - - fn div(self, rhs: Self) -> Self::Output { - Self::with_float(self.as_f64() / rhs.as_f64()) - } -} - -impl BitAnd for Ieee64 { - type Output = Ieee64; - - fn bitand(self, rhs: Self) -> Self::Output { - Self::with_bits(self.bits() & rhs.bits()) - } -} - -impl BitOr for Ieee64 { - type Output = Ieee64; - - fn bitor(self, rhs: Self) -> Self::Output { - Self::with_bits(self.bits() | rhs.bits()) - } -} - -impl BitXor for Ieee64 { - type Output = Ieee64; - - fn bitxor(self, rhs: Self) -> Self::Output { - Self::with_bits(self.bits() ^ rhs.bits()) - } -} - -impl Not for Ieee64 { - type Output = Ieee64; - - fn not(self) -> Self::Output { - Self::with_bits(!self.bits()) - } -} - #[cfg(test)] mod tests { use super::*; use alloc::string::ToString; - use core::fmt::Display; - use core::mem; - use core::str::FromStr; use core::{f32, f64}; #[test] @@ -1192,7 +1112,7 @@ mod tests { assert_eq!(Imm64(9999).to_string(), "9999"); assert_eq!(Imm64(10000).to_string(), "0x2710"); assert_eq!(Imm64(-9999).to_string(), "-9999"); - assert_eq!(Imm64(-10000).to_string(), "0xffff_ffff_ffff_d8f0"); + assert_eq!(Imm64(-10000).to_string(), "-10000"); assert_eq!(Imm64(0xffff).to_string(), "0xffff"); assert_eq!(Imm64(0x10000).to_string(), "0x0001_0000"); } @@ -1212,12 +1132,13 @@ mod tests { } // Verify that `text` can be parsed as a `T` into a value that displays as `want`. + #[track_caller] fn parse_ok(text: &str, want: &str) where ::Err: Display, { match text.parse::() { - Err(s) => panic!("\"{}\".parse() error: {}", text, s), + Err(s) => panic!("\"{text}\".parse() error: {s}"), Ok(x) => assert_eq!(x.to_string(), want), } } @@ -1229,7 +1150,7 @@ mod tests { { match text.parse::() { Err(s) => assert_eq!(s.to_string(), msg), - Ok(x) => panic!("Wanted Err({}), but got {}", msg, x), + Ok(x) => panic!("Wanted Err({msg}), but got {x}"), } } @@ -1245,11 +1166,11 @@ mod tests { // Probe limits. parse_ok::("0xffffffff_ffffffff", "-1"); - parse_ok::("0x80000000_00000000", "0x8000_0000_0000_0000"); - parse_ok::("-0x80000000_00000000", "0x8000_0000_0000_0000"); + parse_ok::("0x80000000_00000000", "-9223372036854775808"); + parse_ok::("-0x80000000_00000000", "-9223372036854775808"); parse_err::("-0x80000000_00000001", "Negative number too small"); parse_ok::("18446744073709551615", "-1"); - parse_ok::("-9223372036854775808", "0x8000_0000_0000_0000"); + parse_ok::("-9223372036854775808", "-9223372036854775808"); // Overflow both the `checked_add` and `checked_mul`. parse_err::("18446744073709551616", "Too large decimal number"); parse_err::("184467440737095516100", "Too large decimal number"); @@ -1273,6 +1194,8 @@ mod tests { parse_err::(" 0", "Invalid character in decimal number"); parse_err::("--", "Invalid character in decimal number"); parse_err::("-0x-", "Invalid character in hexadecimal number"); + parse_err::("abc", "Invalid character in decimal number"); + parse_err::("-abc", "Invalid character in decimal number"); // Hex count overflow. parse_err::("0x0_0000_0000_0000_0000", "Too many hexadecimal digits"); @@ -1313,6 +1236,8 @@ mod tests { parse_err::("-0x-", "Invalid character in hexadecimal number"); parse_err::("-0", "Invalid character in decimal number"); parse_err::("-1", "Invalid character in decimal number"); + parse_err::("abc", "Invalid character in decimal number"); + parse_err::("-abc", "Invalid character in decimal number"); // Hex count overflow. parse_err::("0x0_0000_0000_0000_0000", "Too many hexadecimal digits"); @@ -1345,6 +1270,160 @@ mod tests { parse_err::("+0x8000_0000", "Offset out of range"); } + #[test] + fn format_ieee16() { + assert_eq!(Ieee16::with_bits(0).to_string(), "0.0"); // 0.0 + assert_eq!(Ieee16::with_bits(0x8000).to_string(), "-0.0"); // -0.0 + assert_eq!(Ieee16::with_bits(0x3c00).to_string(), "0x1.000p0"); // 1.0 + assert_eq!(Ieee16::with_bits(0x3e00).to_string(), "0x1.800p0"); // 1.5 + assert_eq!(Ieee16::with_bits(0x3800).to_string(), "0x1.000p-1"); // 0.5 + assert_eq!( + Ieee16::with_bits(0x1400).to_string(), // `f16::EPSILON` + "0x1.000p-10" + ); + assert_eq!( + Ieee16::with_bits(0xfbff).to_string(), // `f16::MIN` + "-0x1.ffcp15" + ); + assert_eq!( + Ieee16::with_bits(0x7bff).to_string(), // `f16::MAX` + "0x1.ffcp15" + ); + // Smallest positive normal number. + assert_eq!( + Ieee16::with_bits(0x0400).to_string(), // `f16::MIN_POSITIVE` + "0x1.000p-14" + ); + // Subnormals. + assert_eq!( + Ieee16::with_bits(0x0200).to_string(), // `f16::MIN_POSITIVE / 2.0` + "0x0.800p-14" + ); + assert_eq!( + Ieee16::with_bits(0x0001).to_string(), // `f16::MIN_POSITIVE * f16::EPSILON` + "0x0.004p-14" + ); + assert_eq!( + Ieee16::with_bits(0x7c00).to_string(), // `f16::INFINITY` + "+Inf" + ); + assert_eq!( + Ieee16::with_bits(0xfc00).to_string(), // `f16::NEG_INFINITY` + "-Inf" + ); + assert_eq!( + Ieee16::with_bits(0x7e00).to_string(), // `f16::NAN` + "+NaN" + ); + assert_eq!( + Ieee16::with_bits(0xfe00).to_string(), // `-f16::NAN` + "-NaN" + ); + // Construct some qNaNs with payloads. + assert_eq!(Ieee16::with_bits(0x7e01).to_string(), "+NaN:0x1"); + assert_eq!(Ieee16::with_bits(0x7f01).to_string(), "+NaN:0x101"); + // Signaling NaNs. + assert_eq!(Ieee16::with_bits(0x7c01).to_string(), "+sNaN:0x1"); + assert_eq!(Ieee16::with_bits(0x7d01).to_string(), "+sNaN:0x101"); + } + + #[test] + fn parse_ieee16() { + parse_ok::("0.0", "0.0"); + parse_ok::("+0.0", "0.0"); + parse_ok::("-0.0", "-0.0"); + parse_ok::("0x0", "0.0"); + parse_ok::("0x0.0", "0.0"); + parse_ok::("0x.0", "0.0"); + parse_ok::("0x0.", "0.0"); + parse_ok::("0x1", "0x1.000p0"); + parse_ok::("+0x1", "0x1.000p0"); + parse_ok::("-0x1", "-0x1.000p0"); + parse_ok::("0x10", "0x1.000p4"); + parse_ok::("0x10.0", "0x1.000p4"); + parse_err::("0.", "Float must be hexadecimal"); + parse_err::(".0", "Float must be hexadecimal"); + parse_err::("0", "Float must be hexadecimal"); + parse_err::("-0", "Float must be hexadecimal"); + parse_err::(".", "Float must be hexadecimal"); + parse_err::("", "Float must be hexadecimal"); + parse_err::("-", "Float must be hexadecimal"); + parse_err::("0x", "No digits"); + parse_err::("0x..", "Multiple radix points"); + + // Check significant bits. + parse_ok::("0x0.ffe", "0x1.ffcp-1"); + parse_ok::("0x1.ffc", "0x1.ffcp0"); + parse_ok::("0x3.ff8", "0x1.ffcp1"); + parse_ok::("0x7.ff", "0x1.ffcp2"); + parse_ok::("0xf.fe", "0x1.ffcp3"); + parse_err::("0x1.ffe", "Too many significant bits"); + parse_err::("0x1.ffc00000000000000000000000000000", "Too many digits"); + + // Exponents. + parse_ok::("0x1p3", "0x1.000p3"); + parse_ok::("0x1p-3", "0x1.000p-3"); + parse_ok::("0x1.0p3", "0x1.000p3"); + parse_ok::("0x2.0p3", "0x1.000p4"); + parse_ok::("0x1.0p15", "0x1.000p15"); + parse_ok::("0x1.0p-14", "0x1.000p-14"); + parse_ok::("0x0.1p-10", "0x1.000p-14"); + parse_err::("0x2.0p15", "Magnitude too large"); + + // Subnormals. + parse_ok::("0x1.0p-15", "0x0.800p-14"); + parse_ok::("0x1.0p-24", "0x0.004p-14"); + parse_ok::("0x0.004p-14", "0x0.004p-14"); + parse_err::("0x0.102p-14", "Subnormal underflow"); + parse_err::("0x1.8p-24", "Subnormal underflow"); + parse_err::("0x1.0p-25", "Magnitude too small"); + + // NaNs and Infs. + parse_ok::("Inf", "+Inf"); + parse_ok::("+Inf", "+Inf"); + parse_ok::("-Inf", "-Inf"); + parse_ok::("NaN", "+NaN"); + parse_ok::("+NaN", "+NaN"); + parse_ok::("-NaN", "-NaN"); + parse_ok::("NaN:0x0", "+NaN"); + parse_err::("NaN:", "Float must be hexadecimal"); + parse_err::("NaN:0", "Float must be hexadecimal"); + parse_err::("NaN:0x", "Invalid NaN payload"); + parse_ok::("NaN:0x001", "+NaN:0x1"); + parse_ok::("NaN:0x101", "+NaN:0x101"); + parse_err::("NaN:0x301", "Invalid NaN payload"); + parse_ok::("sNaN:0x1", "+sNaN:0x1"); + parse_err::("sNaN:0x0", "Invalid sNaN payload"); + parse_ok::("sNaN:0x101", "+sNaN:0x101"); + parse_err::("sNaN:0x301", "Invalid sNaN payload"); + } + + #[test] + fn pow2_ieee16() { + assert_eq!(Ieee16::pow2(0).to_string(), "0x1.000p0"); + assert_eq!(Ieee16::pow2(1).to_string(), "0x1.000p1"); + assert_eq!(Ieee16::pow2(-1).to_string(), "0x1.000p-1"); + assert_eq!(Ieee16::pow2(15).to_string(), "0x1.000p15"); + assert_eq!(Ieee16::pow2(-14).to_string(), "0x1.000p-14"); + + assert_eq!((-Ieee16::pow2(1)).to_string(), "-0x1.000p1"); + } + + #[test] + fn fcvt_to_sint_negative_overflow_ieee16() { + // FIXME(#8312): Replace with commented out version once Rust f16 support is stabilised. + // let n = 8; + // assert_eq!( + // -((1u16 << (n - 1)) as f16) - 1.0, + // Ieee16::fcvt_to_sint_negative_overflow(n).as_f16() + // ); + let n = 8; + assert_eq!( + "-0x1.020p7", + Ieee16::fcvt_to_sint_negative_overflow(n).to_string() + ); + } + #[test] fn format_ieee32() { assert_eq!(Ieee32::with_float(0.0).to_string(), "0.0"); @@ -1377,11 +1456,11 @@ mod tests { assert_eq!(Ieee32::with_float(f32::NAN).to_string(), "+NaN"); assert_eq!(Ieee32::with_float(-f32::NAN).to_string(), "-NaN"); // Construct some qNaNs with payloads. - assert_eq!(Ieee32(0x7fc00001).to_string(), "+NaN:0x1"); - assert_eq!(Ieee32(0x7ff00001).to_string(), "+NaN:0x300001"); + assert_eq!(Ieee32::with_bits(0x7fc00001).to_string(), "+NaN:0x1"); + assert_eq!(Ieee32::with_bits(0x7ff00001).to_string(), "+NaN:0x300001"); // Signaling NaNs. - assert_eq!(Ieee32(0x7f800001).to_string(), "+sNaN:0x1"); - assert_eq!(Ieee32(0x7fa00001).to_string(), "+sNaN:0x200001"); + assert_eq!(Ieee32::with_bits(0x7f800001).to_string(), "+sNaN:0x1"); + assert_eq!(Ieee32::with_bits(0x7fa00001).to_string(), "+sNaN:0x200001"); } #[test] @@ -1415,7 +1494,7 @@ mod tests { parse_ok::("0x7.fffff8", "0x1.fffffep2"); parse_ok::("0xf.fffff0", "0x1.fffffep3"); parse_err::("0x1.ffffff", "Too many significant bits"); - parse_err::("0x1.fffffe0000000000", "Too many digits"); + parse_err::("0x1.fffffe00000000000000000000000000", "Too many digits"); // Exponents. parse_ok::("0x1p3", "0x1.000000p3"); @@ -1463,15 +1542,17 @@ mod tests { assert_eq!(Ieee32::pow2(127).to_string(), "0x1.000000p127"); assert_eq!(Ieee32::pow2(-126).to_string(), "0x1.000000p-126"); - assert_eq!(Ieee32::pow2(1).neg().to_string(), "-0x1.000000p1"); + assert_eq!((-Ieee32::pow2(1)).to_string(), "-0x1.000000p1"); } #[test] fn fcvt_to_sint_negative_overflow_ieee32() { - for n in &[8, 16] { - assert_eq!(-((1u32 << (n - 1)) as f32) - 1.0, unsafe { - mem::transmute(Ieee32::fcvt_to_sint_negative_overflow(*n)) - }); + for n in [8, 16] { + assert_eq!( + -((1u32 << (n - 1)) as f32) - 1.0, + Ieee32::fcvt_to_sint_negative_overflow(n).as_f32(), + "n = {n}" + ); } } @@ -1513,15 +1594,21 @@ mod tests { assert_eq!(Ieee64::with_float(f64::NAN).to_string(), "+NaN"); assert_eq!(Ieee64::with_float(-f64::NAN).to_string(), "-NaN"); // Construct some qNaNs with payloads. - assert_eq!(Ieee64(0x7ff8000000000001).to_string(), "+NaN:0x1"); assert_eq!( - Ieee64(0x7ffc000000000001).to_string(), + Ieee64::with_bits(0x7ff8000000000001).to_string(), + "+NaN:0x1" + ); + assert_eq!( + Ieee64::with_bits(0x7ffc000000000001).to_string(), "+NaN:0x4000000000001" ); // Signaling NaNs. - assert_eq!(Ieee64(0x7ff0000000000001).to_string(), "+sNaN:0x1"); assert_eq!( - Ieee64(0x7ff4000000000001).to_string(), + Ieee64::with_bits(0x7ff0000000000001).to_string(), + "+sNaN:0x1" + ); + assert_eq!( + Ieee64::with_bits(0x7ff4000000000001).to_string(), "+sNaN:0x4000000000001" ); } @@ -1555,7 +1642,7 @@ mod tests { parse_ok::("0x7.ffffffffffffc", "0x1.fffffffffffffp2"); parse_ok::("0xf.ffffffffffff8", "0x1.fffffffffffffp3"); parse_err::("0x3.fffffffffffff", "Too many significant bits"); - parse_err::("0x001.fffffe00000000", "Too many digits"); + parse_err::("0x001.fffffe000000000000000000000000", "Too many digits"); // Exponents. parse_ok::("0x1p3", "0x1.0000000000000p3"); @@ -1601,15 +1688,254 @@ mod tests { assert_eq!(Ieee64::pow2(1023).to_string(), "0x1.0000000000000p1023"); assert_eq!(Ieee64::pow2(-1022).to_string(), "0x1.0000000000000p-1022"); - assert_eq!(Ieee64::pow2(1).neg().to_string(), "-0x1.0000000000000p1"); + assert_eq!((-Ieee64::pow2(1)).to_string(), "-0x1.0000000000000p1"); } #[test] fn fcvt_to_sint_negative_overflow_ieee64() { - for n in &[8, 16, 32] { - assert_eq!(-((1u64 << (n - 1)) as f64) - 1.0, unsafe { - mem::transmute(Ieee64::fcvt_to_sint_negative_overflow(*n)) - }); + for n in [8, 16, 32] { + assert_eq!( + -((1u64 << (n - 1)) as f64) - 1.0, + Ieee64::fcvt_to_sint_negative_overflow(n).as_f64(), + "n = {n}" + ); + } + } + + #[test] + fn format_ieee128() { + assert_eq!( + Ieee128::with_bits(0x00000000000000000000000000000000).to_string(), // 0.0 + "0.0" + ); + assert_eq!( + Ieee128::with_bits(0x80000000000000000000000000000000).to_string(), // -0.0 + "-0.0" + ); + assert_eq!( + Ieee128::with_bits(0x3fff0000000000000000000000000000).to_string(), // 1.0 + "0x1.0000000000000000000000000000p0" + ); + assert_eq!( + Ieee128::with_bits(0x3fff8000000000000000000000000000).to_string(), // 1.5 + "0x1.8000000000000000000000000000p0" + ); + assert_eq!( + Ieee128::with_bits(0x3ffe0000000000000000000000000000).to_string(), // 0.5 + "0x1.0000000000000000000000000000p-1" + ); + assert_eq!( + Ieee128::with_bits(0x3f8f0000000000000000000000000000).to_string(), // `f128::EPSILON` + "0x1.0000000000000000000000000000p-112" + ); + assert_eq!( + Ieee128::with_bits(0xfffeffffffffffffffffffffffffffff).to_string(), // `f128::MIN` + "-0x1.ffffffffffffffffffffffffffffp16383" + ); + assert_eq!( + Ieee128::with_bits(0x7ffeffffffffffffffffffffffffffff).to_string(), // `f128::MAX` + "0x1.ffffffffffffffffffffffffffffp16383" + ); + // Smallest positive normal number. + assert_eq!( + Ieee128::with_bits(0x00010000000000000000000000000000).to_string(), // `f128::MIN_POSITIVE` + "0x1.0000000000000000000000000000p-16382" + ); + // Subnormals. + assert_eq!( + Ieee128::with_bits(0x00008000000000000000000000000000).to_string(), // `f128::MIN_POSITIVE / 2.0` + "0x0.8000000000000000000000000000p-16382" + ); + assert_eq!( + Ieee128::with_bits(0x00000000000000000000000000000001).to_string(), // `f128::MIN_POSITIVE * f128::EPSILON` + "0x0.0000000000000000000000000001p-16382" + ); + assert_eq!( + Ieee128::with_bits(0x7fff0000000000000000000000000000).to_string(), // `f128::INFINITY` + "+Inf" + ); + assert_eq!( + Ieee128::with_bits(0xffff0000000000000000000000000000).to_string(), // `f128::NEG_INFINITY` + "-Inf" + ); + assert_eq!( + Ieee128::with_bits(0x7fff8000000000000000000000000000).to_string(), // `f128::NAN` + "+NaN" + ); + assert_eq!( + Ieee128::with_bits(0xffff8000000000000000000000000000).to_string(), // `-f128::NAN` + "-NaN" + ); + // Construct some qNaNs with payloads. + assert_eq!( + Ieee128::with_bits(0x7fff8000000000000000000000000001).to_string(), + "+NaN:0x1" + ); + assert_eq!( + Ieee128::with_bits(0x7fffc000000000000000000000000001).to_string(), + "+NaN:0x4000000000000000000000000001" + ); + // Signaling NaNs. + assert_eq!( + Ieee128::with_bits(0x7fff0000000000000000000000000001).to_string(), + "+sNaN:0x1" + ); + assert_eq!( + Ieee128::with_bits(0x7fff4000000000000000000000000001).to_string(), + "+sNaN:0x4000000000000000000000000001" + ); + } + + #[test] + fn parse_ieee128() { + parse_ok::("0.0", "0.0"); + parse_ok::("-0.0", "-0.0"); + parse_ok::("0x0", "0.0"); + parse_ok::("0x0.0", "0.0"); + parse_ok::("0x.0", "0.0"); + parse_ok::("0x0.", "0.0"); + parse_ok::("0x1", "0x1.0000000000000000000000000000p0"); + parse_ok::("-0x1", "-0x1.0000000000000000000000000000p0"); + parse_ok::("0x10", "0x1.0000000000000000000000000000p4"); + parse_ok::("0x10.0", "0x1.0000000000000000000000000000p4"); + parse_err::("0.", "Float must be hexadecimal"); + parse_err::(".0", "Float must be hexadecimal"); + parse_err::("0", "Float must be hexadecimal"); + parse_err::("-0", "Float must be hexadecimal"); + parse_err::(".", "Float must be hexadecimal"); + parse_err::("", "Float must be hexadecimal"); + parse_err::("-", "Float must be hexadecimal"); + parse_err::("0x", "No digits"); + parse_err::("0x..", "Multiple radix points"); + + // Check significant bits. + parse_ok::( + "0x0.ffffffffffffffffffffffffffff8", + "0x1.ffffffffffffffffffffffffffffp-1", + ); + parse_ok::( + "0x1.ffffffffffffffffffffffffffff", + "0x1.ffffffffffffffffffffffffffffp0", + ); + parse_ok::( + "0x3.fffffffffffffffffffffffffffe", + "0x1.ffffffffffffffffffffffffffffp1", + ); + parse_ok::( + "0x7.fffffffffffffffffffffffffffc", + "0x1.ffffffffffffffffffffffffffffp2", + ); + parse_ok::( + "0xf.fffffffffffffffffffffffffff8", + "0x1.ffffffffffffffffffffffffffffp3", + ); + parse_err::( + "0x3.ffffffffffffffffffffffffffff", + "Too many significant bits", + ); + parse_err::("0x001.fffffe000000000000000000000000", "Too many digits"); + + // Exponents. + parse_ok::("0x1p3", "0x1.0000000000000000000000000000p3"); + parse_ok::("0x1p-3", "0x1.0000000000000000000000000000p-3"); + parse_ok::("0x1.0p3", "0x1.0000000000000000000000000000p3"); + parse_ok::("0x2.0p3", "0x1.0000000000000000000000000000p4"); + parse_ok::("0x1.0p16383", "0x1.0000000000000000000000000000p16383"); + parse_ok::("0x1.0p-16382", "0x1.0000000000000000000000000000p-16382"); + parse_ok::("0x0.1p-16378", "0x1.0000000000000000000000000000p-16382"); + parse_err::("0x2.0p16383", "Magnitude too large"); + + // Subnormals. + parse_ok::("0x1.0p-16383", "0x0.8000000000000000000000000000p-16382"); + parse_ok::("0x1.0p-16494", "0x0.0000000000000000000000000001p-16382"); + parse_ok::( + "0x0.0000000000000000000000000001p-16382", + "0x0.0000000000000000000000000001p-16382", + ); + parse_err::( + "0x0.10000000000000000000000000008p-16382", + "Subnormal underflow", + ); + parse_err::("0x1.8p-16494", "Subnormal underflow"); + parse_err::("0x1.0p-16495", "Magnitude too small"); + + // NaNs and Infs. + parse_ok::("Inf", "+Inf"); + parse_ok::("-Inf", "-Inf"); + parse_ok::("NaN", "+NaN"); + parse_ok::("-NaN", "-NaN"); + parse_ok::("NaN:0x0", "+NaN"); + parse_err::("NaN:", "Float must be hexadecimal"); + parse_err::("NaN:0", "Float must be hexadecimal"); + parse_err::("NaN:0x", "Invalid NaN payload"); + parse_ok::("NaN:0x000001", "+NaN:0x1"); + parse_ok::( + "NaN:0x4000000000000000000000000001", + "+NaN:0x4000000000000000000000000001", + ); + parse_err::("NaN:0x8000000000000000000000000001", "Invalid NaN payload"); + parse_ok::("sNaN:0x1", "+sNaN:0x1"); + parse_err::("sNaN:0x0", "Invalid sNaN payload"); + parse_ok::( + "sNaN:0x4000000000000000000000000001", + "+sNaN:0x4000000000000000000000000001", + ); + parse_err::( + "sNaN:0x8000000000000000000000000001", + "Invalid sNaN payload", + ); + } + + #[test] + fn pow2_ieee128() { + assert_eq!( + Ieee128::pow2(0).to_string(), + "0x1.0000000000000000000000000000p0" + ); + assert_eq!( + Ieee128::pow2(1).to_string(), + "0x1.0000000000000000000000000000p1" + ); + assert_eq!( + Ieee128::pow2(-1).to_string(), + "0x1.0000000000000000000000000000p-1" + ); + assert_eq!( + Ieee128::pow2(16383).to_string(), + "0x1.0000000000000000000000000000p16383" + ); + assert_eq!( + Ieee128::pow2(-16382).to_string(), + "0x1.0000000000000000000000000000p-16382" + ); + + assert_eq!( + (-Ieee128::pow2(1)).to_string(), + "-0x1.0000000000000000000000000000p1" + ); + } + + #[test] + fn fcvt_to_sint_negative_overflow_ieee128() { + // FIXME(#8312): Replace with commented out version once Rust f128 support is stabilised. + // for n in [8, 16, 32, 64] { + // assert_eq!( + // -((1u128 << (n - 1)) as f128) - 1.0, + // Ieee128::fcvt_to_sint_negative_overflow(n).as_f128(), + // "n = {n}" + // ); + // } + for (n, expected) in [ + (8, "-0x1.0200000000000000000000000000p7"), + (16, "-0x1.0002000000000000000000000000p15"), + (32, "-0x1.0000000200000000000000000000p31"), + (64, "-0x1.0000000000000002000000000000p63"), + ] { + assert_eq!( + expected, + Ieee128::fcvt_to_sint_negative_overflow(n).to_string(), + "n = {n}" + ); } } } diff --git a/cranelift/codegen/src/ir/instructions.rs b/cranelift/codegen/src/ir/instructions.rs index 35607e73ea1c..ab97327e4c69 100644 --- a/cranelift/codegen/src/ir/instructions.rs +++ b/cranelift/codegen/src/ir/instructions.rs @@ -6,6 +6,7 @@ //! A large part of this module is auto-generated from the instruction descriptions in the meta //! directory. +use crate::constant_hash::Table; use alloc::vec::Vec; use core::fmt::{self, Display, Formatter}; use core::ops::{Deref, DerefMut}; @@ -14,7 +15,7 @@ use core::str::FromStr; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; -use crate::bitset::BitSet; +use crate::bitset::ScalarBitSet; use crate::entity; use crate::ir::{ self, @@ -150,7 +151,7 @@ impl<'a> Display for DisplayBlockCall<'a> { if ix > 0 { write!(f, ", ")?; } - write!(f, "{}", arg)?; + write!(f, "{arg}")?; } write!(f, ")")?; } @@ -193,12 +194,12 @@ impl Opcode { OPCODE_CONSTRAINTS[self as usize - 1] } - /// Returns true if the instruction is a resumable trap. - pub fn is_resumable_trap(&self) -> bool { - match self { - Opcode::ResumableTrap | Opcode::ResumableTrapnz => true, - _ => false, - } + /// Is this instruction a GC safepoint? + /// + /// Safepoints are all kinds of calls, except for tail calls. + #[inline] + pub fn is_safepoint(self) -> bool { + self.is_call() && !self.is_return() } } @@ -211,17 +212,7 @@ impl FromStr for Opcode { /// Parse an Opcode name from a string. fn from_str(s: &str) -> Result { - use crate::constant_hash::{probe, simple_hash, Table}; - - impl<'a> Table<&'a str> for [Option] { - fn len(&self) -> usize { - self.len() - } - - fn key(&self, idx: usize) -> Option<&'a str> { - self[idx].map(opcode_name) - } - } + use crate::constant_hash::{probe, simple_hash}; match probe::<&str, [Option]>(&OPCODE_HASH_TABLE, s, simple_hash(s)) { Err(_) => Err("Unknown opcode"), @@ -232,6 +223,16 @@ impl FromStr for Opcode { } } +impl<'a> Table<&'a str> for [Option] { + fn len(&self) -> usize { + self.len() + } + + fn key(&self, idx: usize) -> Option<&'a str> { + self[idx].map(opcode_name) + } +} + /// A variable list of `Value` operands used for function call arguments and passing arguments to /// basic blocks. #[derive(Clone, Debug)] @@ -281,9 +282,9 @@ impl Display for VariableArgs { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { for (i, val) in self.0.iter().enumerate() { if i == 0 { - write!(fmt, "{}", val)?; + write!(fmt, "{val}")?; } else { - write!(fmt, ", {}", val)?; + write!(fmt, ", {val}")?; } } Ok(()) @@ -341,6 +342,25 @@ impl InstructionData { } } + /// Replace the values used in this instruction according to the given + /// function. + pub fn map_values( + &mut self, + pool: &mut ValueListPool, + jump_tables: &mut ir::JumpTables, + mut f: impl FnMut(Value) -> Value, + ) { + for arg in self.arguments_mut(pool) { + *arg = f(*arg); + } + + for block in self.branch_destination_mut(jump_tables) { + for arg in block.args_slice_mut(pool) { + *arg = f(*arg); + } + } + } + /// If this is a trapping instruction, get its trap code. Otherwise, return /// `None`. pub fn trap_code(&self) -> Option { @@ -430,6 +450,14 @@ impl InstructionData { Self::CallIndirect { sig_ref, ref args, .. } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]), + Self::Ternary { + opcode: Opcode::StackSwitch, + .. + } => { + // `StackSwitch` is not actually a call, but has the .call() side + // effect as it continues execution elsewhere. + CallInfo::NotACall + } _ => { debug_assert!(!self.opcode().is_call()); CallInfo::NotACall @@ -438,7 +466,7 @@ impl InstructionData { } #[inline] - pub(crate) fn sign_extend_immediates(&mut self, ctrl_typevar: Type) { + pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) { if ctrl_typevar.is_invalid() { return; } @@ -446,13 +474,16 @@ impl InstructionData { let bit_width = ctrl_typevar.bits(); match self { + Self::UnaryImm { opcode: _, imm } => { + *imm = imm.mask_to_width(bit_width); + } Self::BinaryImm64 { opcode, arg: _, imm, } => { if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm { - imm.sign_extend_from_width(bit_width); + *imm = imm.mask_to_width(bit_width); } } Self::IntCompareImm { @@ -463,7 +494,7 @@ impl InstructionData { } => { debug_assert_eq!(*opcode, Opcode::IcmpImm); if cond.unsigned() != *cond { - imm.sign_extend_from_width(bit_width); + *imm = imm.mask_to_width(bit_width); } } _ => {} @@ -577,7 +608,7 @@ impl OpcodeConstraints { debug_assert!(n < self.num_fixed_results(), "Invalid result index"); match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) { ResolvedConstraint::Bound(t) => t, - ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {:?}", ts), + ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"), } } @@ -607,8 +638,8 @@ impl OpcodeConstraints { } } -type BitSet8 = BitSet; -type BitSet16 = BitSet; +type BitSet8 = ScalarBitSet; +type BitSet16 = ScalarBitSet; /// A value type set describes the permitted set of types for a type variable. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -619,8 +650,6 @@ pub struct ValueTypeSet { pub ints: BitSet8, /// Allowed float widths pub floats: BitSet8, - /// Allowed ref widths - pub refs: BitSet8, /// Allowed dynamic vectors minimum lane sizes pub dynamic_lanes: BitSet16, } @@ -630,13 +659,11 @@ impl ValueTypeSet { /// /// Note that the base type set does not have to be included in the type set proper. fn is_base_type(self, scalar: Type) -> bool { - let l2b = scalar.log2_lane_bits(); + let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap(); if scalar.is_int() { self.ints.contains(l2b) } else if scalar.is_float() { self.floats.contains(l2b) - } else if scalar.is_ref() { - self.refs.contains(l2b) } else { false } @@ -645,10 +672,10 @@ impl ValueTypeSet { /// Does `typ` belong to this set? pub fn contains(self, typ: Type) -> bool { if typ.is_dynamic_vector() { - let l2l = typ.log2_min_lane_count(); + let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap(); self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type()) } else { - let l2l = typ.log2_lane_count(); + let l2l = u8::try_from(typ.log2_lane_count()).unwrap(); self.lanes.contains(l2l) && self.is_base_type(typ.lane_type()) } } @@ -774,7 +801,7 @@ impl OperandConstraint { let mut tys = ValueTypeSet::default(); // We're testing scalar values, only. - tys.lanes = BitSet::from_range(0, 1); + tys.lanes = ScalarBitSet::from_range(0, 1); if ctrl_type.is_int() { // The upper bound in from_range is exclusive, and we want to exclude the @@ -782,8 +809,8 @@ impl OperandConstraint { tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8); } else if ctrl_type.is_float() { // The upper bound in from_range is exclusive, and we want to exclude the - // control type to construct the interval of [F32, ctrl_type). - tys.floats = BitSet8::from_range(5, ctrl_type_bits as u8); + // control type to construct the interval of [F16, ctrl_type). + tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8); } else { panic!("The Narrower constraint only operates on floats or ints"); } @@ -794,7 +821,7 @@ impl OperandConstraint { let mut tys = ValueTypeSet::default(); // We're testing scalar values, only. - tys.lanes = BitSet::from_range(0, 1); + tys.lanes = ScalarBitSet::from_range(0, 1); if ctrl_type.is_int() { let lower_bound = ctrl_type_bits as u8 + 1; @@ -803,17 +830,18 @@ impl OperandConstraint { // lower bound would overflow as 2^8 doesn't fit in a u8, but this would // already describe the empty set so instead we leave `ints` in its default // empty state. - if lower_bound < BitSet8::bits() as u8 { + if lower_bound < BitSet8::capacity() { // The interval should include all types wider than `ctrl_type`, so we use // `2^8` as the upper bound, and add one to the bits of `ctrl_type` to define // the interval `(ctrl_type, I128]`. tys.ints = BitSet8::from_range(lower_bound, 8); } } else if ctrl_type.is_float() { - // The interval should include all float types wider than `ctrl_type`, so we - // use `2^7` as the upper bound, and add one to the bits of `ctrl_type` to - // define the interval `(ctrl_type, F64]`. - tys.floats = BitSet8::from_range(ctrl_type_bits as u8 + 1, 7); + // Same as above but for `tys.floats`, as the largest float type is F128. + let lower_bound = ctrl_type_bits as u8 + 1; + if lower_bound < BitSet8::capacity() { + tys.floats = BitSet8::from_range(lower_bound, 8); + } } else { panic!("The Wider constraint only operates on floats or ints"); } @@ -944,7 +972,6 @@ mod tests { lanes: BitSet16::from_range(0, 8), ints: BitSet8::from_range(4, 7), floats: BitSet8::from_range(0, 0), - refs: BitSet8::from_range(5, 7), dynamic_lanes: BitSet16::from_range(0, 4), }; assert!(!vts.contains(I8)); @@ -952,16 +979,15 @@ mod tests { assert!(vts.contains(I64)); assert!(vts.contains(I32X4)); assert!(vts.contains(I32X4XN)); + assert!(!vts.contains(F16)); assert!(!vts.contains(F32)); - assert!(vts.contains(R32)); - assert!(vts.contains(R64)); + assert!(!vts.contains(F128)); assert_eq!(vts.example().to_string(), "i32"); let vts = ValueTypeSet { lanes: BitSet16::from_range(0, 8), ints: BitSet8::from_range(0, 0), floats: BitSet8::from_range(5, 7), - refs: BitSet8::from_range(0, 0), dynamic_lanes: BitSet16::from_range(0, 8), }; assert_eq!(vts.example().to_string(), "f32"); @@ -970,7 +996,6 @@ mod tests { lanes: BitSet16::from_range(1, 8), ints: BitSet8::from_range(0, 0), floats: BitSet8::from_range(5, 7), - refs: BitSet8::from_range(0, 0), dynamic_lanes: BitSet16::from_range(0, 8), }; assert_eq!(vts.example().to_string(), "f32x2"); @@ -979,7 +1004,6 @@ mod tests { lanes: BitSet16::from_range(2, 8), ints: BitSet8::from_range(3, 7), floats: BitSet8::from_range(0, 0), - refs: BitSet8::from_range(0, 0), dynamic_lanes: BitSet16::from_range(0, 8), }; assert_eq!(vts.example().to_string(), "i32x4"); @@ -989,12 +1013,9 @@ mod tests { lanes: BitSet16::from_range(0, 9), ints: BitSet8::from_range(3, 7), floats: BitSet8::from_range(0, 0), - refs: BitSet8::from_range(0, 0), dynamic_lanes: BitSet16::from_range(0, 8), }; assert!(vts.contains(I32)); assert!(vts.contains(I32X4)); - assert!(!vts.contains(R32)); - assert!(!vts.contains(R64)); } } diff --git a/cranelift/codegen/src/ir/jumptable.rs b/cranelift/codegen/src/ir/jumptable.rs index dce3470483b3..8e1e15c7d621 100644 --- a/cranelift/codegen/src/ir/jumptable.rs +++ b/cranelift/codegen/src/ir/jumptable.rs @@ -20,7 +20,7 @@ use serde_derive::{Deserialize, Serialize}; /// It can be accessed through the `default_block` and `default_block_mut` functions. All blocks /// may be iterated using the `all_branches` and `all_branches_mut` functions, which will both /// iterate over the default block first. -#[derive(Clone, PartialEq, Hash)] +#[derive(Debug, Clone, PartialEq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct JumpTableData { // Table entries. diff --git a/cranelift/codegen/src/ir/layout.rs b/cranelift/codegen/src/ir/layout.rs index 2f6788fbe763..8d8b577e2874 100644 --- a/cranelift/codegen/src/ir/layout.rs +++ b/cranelift/codegen/src/ir/layout.rs @@ -9,7 +9,6 @@ use crate::ir::{Block, Inst}; use crate::packed_option::PackedOption; use crate::{timing, trace}; use core::cmp; -use core::iter::{IntoIterator, Iterator}; /// The `Layout` struct determines the layout of blocks and instructions in a function. It does not /// contain definitions of instructions or blocks, but depends on `Inst` and `Block` entity references @@ -681,7 +680,6 @@ mod serde { use ::serde::de::{Deserializer, Error, SeqAccess, Visitor}; use ::serde::ser::{SerializeSeq, Serializer}; use ::serde::{Deserialize, Serialize}; - use core::convert::TryFrom; use core::fmt; use core::marker::PhantomData; diff --git a/cranelift/codegen/src/ir/memflags.rs b/cranelift/codegen/src/ir/memflags.rs index 87165ba69117..9b706bd56d2e 100644 --- a/cranelift/codegen/src/ir/memflags.rs +++ b/cranelift/codegen/src/ir/memflags.rs @@ -1,31 +1,12 @@ //! Memory operation flags. +use super::TrapCode; use core::fmt; +use core::str::FromStr; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; -enum FlagBit { - Notrap, - Aligned, - Readonly, - LittleEndian, - BigEndian, - /// Accesses only the "heap" part of abstract state. Used for - /// alias analysis. Mutually exclusive with "table" and "vmctx". - Heap, - /// Accesses only the "table" part of abstract state. Used for - /// alias analysis. Mutually exclusive with "heap" and "vmctx". - Table, - /// Accesses only the "vmctx" part of abstract state. Used for - /// alias analysis. Mutually exclusive with "heap" and "table". - Vmctx, -} - -const NAMES: [&str; 8] = [ - "notrap", "aligned", "readonly", "little", "big", "heap", "table", "vmctx", -]; - /// Endianness of a memory access. #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum Endianness { @@ -35,6 +16,38 @@ pub enum Endianness { Big, } +/// Which disjoint region of aliasing memory is accessed in this memory +/// operation. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +#[repr(u8)] +#[allow(missing_docs)] +#[rustfmt::skip] +pub enum AliasRegion { + // None = 0b00; + Heap = 0b01, + Table = 0b10, + Vmctx = 0b11, +} + +impl AliasRegion { + const fn from_bits(bits: u8) -> Option { + match bits { + 0b00 => None, + 0b01 => Some(Self::Heap), + 0b10 => Some(Self::Table), + 0b11 => Some(Self::Vmctx), + _ => panic!("invalid alias region bits"), + } + } + + const fn to_bits(region: Option) -> u8 { + match region { + None => 0b00, + Some(r) => r as u8, + } + } +} + /// Flags for memory operations like load/store. /// /// Each of these flags introduce a limited form of undefined behavior. The flags each enable @@ -48,53 +61,150 @@ pub enum Endianness { #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct MemFlags { - bits: u8, + // Initialized to all zeros to have all flags have their default value. + // This is interpreted through various methods below. Currently the bits of + // this are defined as: + // + // * 0 - aligned flag + // * 1 - readonly flag + // * 2 - little endian flag + // * 3 - big endian flag + // * 4 - checked flag + // * 5/6 - alias region + // * 7/8/9/10 - trap code + // * 11/12/13/14/15 - unallocated + // + // Current properties upheld are: + // + // * only one of little/big endian is set + // * only one alias region can be set - once set it cannot be changed + bits: u16, } +/// Guaranteed to use "natural alignment" for the given type. This +/// may enable better instruction selection. +const BIT_ALIGNED: u16 = 1 << 0; + +/// A load that reads data in memory that does not change for the +/// duration of the function's execution. This may enable +/// additional optimizations to be performed. +const BIT_READONLY: u16 = 1 << 1; + +/// Load multi-byte values from memory in a little-endian format. +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; +const ALIAS_REGION_OFFSET: u16 = 5; + +/// Trap code, if any, for this memory operation. +const MASK_TRAP_CODE: u16 = 0b1111 << TRAP_CODE_OFFSET; +const TRAP_CODE_OFFSET: u16 = 7; + impl MemFlags { /// Create a new empty set of flags. - pub fn new() -> Self { + pub const fn new() -> Self { Self { bits: 0 } } /// Create a set of flags representing an access from a "trusted" address, meaning it's /// known to be aligned and non-trapping. - pub fn trusted() -> Self { - let mut result = Self::new(); - result.set_notrap(); - result.set_aligned(); - result + pub const fn trusted() -> Self { + Self::new().with_notrap().with_aligned() } /// Read a flag bit. - fn read(self, bit: FlagBit) -> bool { - self.bits & (1 << bit as usize) != 0 + const fn read_bit(self, bit: u16) -> bool { + self.bits & bit != 0 } - /// Set a flag bit. - fn set(&mut self, bit: FlagBit) { - self.bits |= 1 << bit as usize + /// Return a new `MemFlags` with this flag bit set. + const fn with_bit(mut self, bit: u16) -> Self { + self.bits |= bit; + self + } + + /// Reads the alias region that this memory operation works with. + pub const fn alias_region(self) -> Option { + AliasRegion::from_bits(((self.bits & MASK_ALIAS_REGION) >> ALIAS_REGION_OFFSET) as u8) + } + + /// Sets the alias region that this works on to the specified `region`. + pub const fn with_alias_region(mut self, region: Option) -> Self { + let bits = AliasRegion::to_bits(region); + self.bits &= !MASK_ALIAS_REGION; + self.bits |= (bits as u16) << ALIAS_REGION_OFFSET; + self + } + + /// Sets the alias region that this works on to the specified `region`. + pub fn set_alias_region(&mut self, region: Option) { + *self = self.with_alias_region(region); } /// Set a flag bit by name. /// - /// Returns true if the flag was found and set, false for an unknown flag name. - /// Will also return false when trying to set inconsistent endianness flags. - pub fn set_by_name(&mut self, name: &str) -> bool { - match NAMES.iter().position(|&s| s == name) { - Some(bit) => { - let bits = self.bits | 1 << bit; - if (bits & (1 << FlagBit::LittleEndian as usize)) != 0 - && (bits & (1 << FlagBit::BigEndian as usize)) != 0 - { - false - } else { - self.bits = bits; - true + /// Returns true if the flag was found and set, false for an unknown flag + /// name. + /// + /// # Errors + /// + /// Returns an error message if the `name` is known but couldn't be applied + /// due to it being a semantic error. + pub fn set_by_name(&mut self, name: &str) -> Result { + *self = match name { + "notrap" => self.with_trap_code(None), + "aligned" => self.with_aligned(), + "readonly" => self.with_readonly(), + "little" => { + if self.read_bit(BIT_BIG_ENDIAN) { + return Err("cannot set both big and little endian bits"); } + self.with_endianness(Endianness::Little) } - None => false, - } + "big" => { + if self.read_bit(BIT_LITTLE_ENDIAN) { + return Err("cannot set both big and little endian bits"); + } + self.with_endianness(Endianness::Big) + } + "heap" => { + if self.alias_region().is_some() { + return Err("cannot set more than one alias region"); + } + self.with_alias_region(Some(AliasRegion::Heap)) + } + "table" => { + if self.alias_region().is_some() { + return Err("cannot set more than one alias region"); + } + self.with_alias_region(Some(AliasRegion::Table)) + } + "vmctx" => { + if self.alias_region().is_some() { + return Err("cannot set more than one alias region"); + } + self.with_alias_region(Some(AliasRegion::Vmctx)) + } + "checked" => self.with_checked(), + + other => match TrapCode::from_str(other) { + Ok(TrapCode::User(_)) => return Err("cannot set user trap code on mem flags"), + Ok(code) => self.with_trap_code(Some(code)), + Err(()) => return Ok(false), + }, + }; + Ok(true) } /// Return endianness of the memory access. This will return the endianness @@ -102,10 +212,10 @@ impl MemFlags { /// endianness otherwise. The native endianness has to be provided by the /// caller since it is not explicitly encoded in CLIF IR -- this allows a /// front end to create IR without having to know the target endianness. - pub fn endianness(self, native_endianness: Endianness) -> Endianness { - if self.read(FlagBit::LittleEndian) { + pub const fn endianness(self, native_endianness: Endianness) -> Endianness { + if self.read_bit(BIT_LITTLE_ENDIAN) { Endianness::Little - } else if self.read(FlagBit::BigEndian) { + } else if self.read_bit(BIT_BIG_ENDIAN) { Endianness::Big } else { native_endianness @@ -114,41 +224,42 @@ impl MemFlags { /// Set endianness of the memory access. pub fn set_endianness(&mut self, endianness: Endianness) { - match endianness { - Endianness::Little => self.set(FlagBit::LittleEndian), - Endianness::Big => self.set(FlagBit::BigEndian), - }; - assert!(!(self.read(FlagBit::LittleEndian) && self.read(FlagBit::BigEndian))); + *self = self.with_endianness(endianness); } /// Set endianness of the memory access, returning new flags. - pub fn with_endianness(mut self, endianness: Endianness) -> Self { - self.set_endianness(endianness); - self + pub const fn with_endianness(self, endianness: Endianness) -> Self { + let res = match endianness { + Endianness::Little => self.with_bit(BIT_LITTLE_ENDIAN), + Endianness::Big => self.with_bit(BIT_BIG_ENDIAN), + }; + assert!(!(res.read_bit(BIT_LITTLE_ENDIAN) && res.read_bit(BIT_BIG_ENDIAN))); + res } - /// Test if the `notrap` flag is set. + /// Test if this memory operation cannot trap. /// - /// Normally, trapping is part of the semantics of a load/store operation. If the platform - /// would cause a trap when accessing the effective address, the Cranelift memory operation is - /// also required to trap. + /// By default `MemFlags` will assume that any load/store can trap and is + /// associated with a `TrapCode::HeapOutOfBounds` code. If the trap code is + /// configured to `None` though then this method will return `true` and + /// indicates that the memory operation will not trap. /// - /// The `notrap` flag tells Cranelift that the memory is *accessible*, which means that - /// accesses will not trap. This makes it possible to delete an unused load or a dead store - /// instruction. - pub fn notrap(self) -> bool { - self.read(FlagBit::Notrap) + /// If this returns `true` then the memory is *accessible*, which means + /// that accesses will not trap. This makes it possible to delete an unused + /// load or a dead store instruction. + pub const fn notrap(self) -> bool { + self.trap_code().is_none() } - /// Set the `notrap` flag. + /// Sets the trap code for this `MemFlags` to `None`. pub fn set_notrap(&mut self) { - self.set(FlagBit::Notrap) + *self = self.with_notrap(); } - /// Set the `notrap` flag, returning new flags. - pub fn with_notrap(mut self) -> Self { - self.set_notrap(); - self + /// Sets the trap code for this `MemFlags` to `None`, returning the new + /// flags. + pub const fn with_notrap(self) -> Self { + self.with_trap_code(None) } /// Test if the `aligned` flag is set. @@ -156,19 +267,18 @@ impl MemFlags { /// By default, Cranelift memory instructions work with any unaligned effective address. If the /// `aligned` flag is set, the instruction is permitted to trap or return a wrong result if the /// effective address is misaligned. - pub fn aligned(self) -> bool { - self.read(FlagBit::Aligned) + pub const fn aligned(self) -> bool { + self.read_bit(BIT_ALIGNED) } /// Set the `aligned` flag. pub fn set_aligned(&mut self) { - self.set(FlagBit::Aligned) + *self = self.with_aligned(); } /// Set the `aligned` flag, returning new flags. - pub fn with_aligned(mut self) -> Self { - self.set_aligned(); - self + pub const fn with_aligned(self) -> Self { + self.with_bit(BIT_ALIGNED) } /// Test if the `readonly` flag is set. @@ -176,104 +286,176 @@ impl MemFlags { /// Loads with this flag have no memory dependencies. /// This results in undefined behavior if the dereferenced memory is mutated at any time /// between when the function is called and when it is exited. - pub fn readonly(self) -> bool { - self.read(FlagBit::Readonly) + pub const fn readonly(self) -> bool { + self.read_bit(BIT_READONLY) } /// Set the `readonly` flag. pub fn set_readonly(&mut self) { - self.set(FlagBit::Readonly) + *self = self.with_readonly(); } /// Set the `readonly` flag, returning new flags. - pub fn with_readonly(mut self) -> Self { - self.set_readonly(); - self + pub const fn with_readonly(self) -> Self { + self.with_bit(BIT_READONLY) } - /// Test if the `heap` bit is set. + /// Test if the `checked` bit is set. /// - /// Loads and stores with this flag accesses the "heap" part of - /// abstract state. This is disjoint from the "table", "vmctx", - /// and "other" parts of abstract state. In concrete terms, this - /// means that behavior is undefined if the same memory is also - /// accessed by another load/store with one of the other - /// alias-analysis bits (`table`, `vmctx`) set, or `heap` not set. - pub fn heap(self) -> bool { - self.read(FlagBit::Heap) + /// 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 `heap` bit. See the notes about mutual exclusion with - /// other bits in `heap()`. - pub fn set_heap(&mut self) { - assert!(!self.table() && !self.vmctx()); - self.set(FlagBit::Heap); + /// Set the `checked` bit. + pub fn set_checked(&mut self) { + *self = self.with_checked(); } - /// Set the `heap` bit, returning new flags. - pub fn with_heap(mut self) -> Self { - self.set_heap(); - self + /// Set the `checked` bit, returning new flags. + pub const fn with_checked(self) -> Self { + self.with_bit(BIT_CHECKED) } - /// Test if the `table` bit is set. + /// Get the trap code to report if this memory access traps. /// - /// Loads and stores with this flag accesses the "table" part of - /// abstract state. This is disjoint from the "heap", "vmctx", - /// and "other" parts of abstract state. In concrete terms, this - /// means that behavior is undefined if the same memory is also - /// accessed by another load/store with one of the other - /// alias-analysis bits (`heap`, `vmctx`) set, or `table` not set. - pub fn table(self) -> bool { - self.read(FlagBit::Table) - } - - /// Set the `table` bit. See the notes about mutual exclusion with - /// other bits in `table()`. - pub fn set_table(&mut self) { - assert!(!self.heap() && !self.vmctx()); - self.set(FlagBit::Table); - } - - /// Set the `table` bit, returning new flags. - pub fn with_table(mut self) -> Self { - self.set_table(); - self + /// A `None` trap code indicates that this memory access does not trap. + pub const fn trap_code(self) -> Option { + // NB: keep this encoding in sync with `with_trap_code` below. + // + // Also note that the default, all zeros, is `HeapOutOfBounds`. It is + // intentionally not `None` so memory operations are all considered + // effect-ful by default. + match (self.bits & MASK_TRAP_CODE) >> TRAP_CODE_OFFSET { + 0b0000 => Some(TrapCode::HeapOutOfBounds), + 0b0001 => Some(TrapCode::StackOverflow), + 0b0010 => Some(TrapCode::HeapMisaligned), + 0b0011 => Some(TrapCode::TableOutOfBounds), + 0b0100 => Some(TrapCode::IndirectCallToNull), + 0b0101 => Some(TrapCode::BadSignature), + 0b0110 => Some(TrapCode::IntegerOverflow), + 0b0111 => Some(TrapCode::IntegerDivisionByZero), + 0b1000 => Some(TrapCode::BadConversionToInteger), + 0b1001 => Some(TrapCode::UnreachableCodeReached), + 0b1010 => Some(TrapCode::Interrupt), + 0b1011 => Some(TrapCode::NullReference), + 0b1100 => Some(TrapCode::NullI31Ref), + // 0b1101 => {} not allocated + // 0b1110 => {} not allocated + 0b1111 => None, + _ => unreachable!(), + } } - /// Test if the `vmctx` bit is set. + /// Configures these flags with the specified trap code `code`. /// - /// Loads and stores with this flag accesses the "vmctx" part of - /// abstract state. This is disjoint from the "heap", "table", - /// and "other" parts of abstract state. In concrete terms, this - /// means that behavior is undefined if the same memory is also - /// accessed by another load/store with one of the other - /// alias-analysis bits (`heap`, `table`) set, or `vmctx` not set. - pub fn vmctx(self) -> bool { - self.read(FlagBit::Vmctx) - } - - /// Set the `vmctx` bit. See the notes about mutual exclusion with - /// other bits in `vmctx()`. - pub fn set_vmctx(&mut self) { - assert!(!self.heap() && !self.table()); - self.set(FlagBit::Vmctx); - } - - /// Set the `vmctx` bit, returning new flags. - pub fn with_vmctx(mut self) -> Self { - self.set_vmctx(); + /// Note that `TrapCode::User(_)` cannot be set in `MemFlags`. A trap code + /// indicates that this memory operation cannot be optimized away and it + /// must "stay where it is" in the programs. Traps are considered side + /// effects, for example, and have meaning through the trap code that is + /// communicated and which instruction trapped. + pub const fn with_trap_code(mut self, code: Option) -> Self { + let bits = match code { + Some(TrapCode::HeapOutOfBounds) => 0b0000, + Some(TrapCode::StackOverflow) => 0b0001, + Some(TrapCode::HeapMisaligned) => 0b0010, + Some(TrapCode::TableOutOfBounds) => 0b0011, + Some(TrapCode::IndirectCallToNull) => 0b0100, + Some(TrapCode::BadSignature) => 0b0101, + Some(TrapCode::IntegerOverflow) => 0b0110, + Some(TrapCode::IntegerDivisionByZero) => 0b0111, + Some(TrapCode::BadConversionToInteger) => 0b1000, + Some(TrapCode::UnreachableCodeReached) => 0b1001, + Some(TrapCode::Interrupt) => 0b1010, + Some(TrapCode::NullReference) => 0b1011, + Some(TrapCode::NullI31Ref) => 0b1100, + None => 0b1111, + + Some(TrapCode::User(_)) => panic!("cannot set user trap code in mem flags"), + }; + self.bits &= !MASK_TRAP_CODE; + self.bits |= bits << TRAP_CODE_OFFSET; self } } impl fmt::Display for MemFlags { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for (i, n) in NAMES.iter().enumerate() { - if self.bits & (1 << i) != 0 { - write!(f, " {}", n)?; - } + match self.trap_code() { + None => write!(f, " notrap")?, + // This is the default trap code, so don't print anything extra + // for this. + Some(TrapCode::HeapOutOfBounds) => {} + Some(t) => write!(f, " {t}")?, + } + if self.aligned() { + write!(f, " aligned")?; + } + if self.readonly() { + write!(f, " readonly")?; + } + if self.read_bit(BIT_BIG_ENDIAN) { + write!(f, " big")?; + } + 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")?, + Some(AliasRegion::Table) => write!(f, " table")?, + Some(AliasRegion::Vmctx) => write!(f, " vmctx")?, } Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn roundtrip_traps() { + for trap in TrapCode::non_user_traps().iter().copied() { + let flags = MemFlags::new().with_trap_code(Some(trap)); + assert_eq!(flags.trap_code(), Some(trap)); + } + let flags = MemFlags::new().with_trap_code(None); + assert_eq!(flags.trap_code(), None); + } + + #[test] + fn cannot_set_big_and_little() { + let mut big = MemFlags::new().with_endianness(Endianness::Big); + assert!(big.set_by_name("little").is_err()); + + let mut little = MemFlags::new().with_endianness(Endianness::Little); + assert!(little.set_by_name("big").is_err()); + } + + #[test] + fn only_one_region() { + let mut big = MemFlags::new().with_alias_region(Some(AliasRegion::Heap)); + assert!(big.set_by_name("table").is_err()); + assert!(big.set_by_name("vmctx").is_err()); + + let mut big = MemFlags::new().with_alias_region(Some(AliasRegion::Table)); + assert!(big.set_by_name("heap").is_err()); + assert!(big.set_by_name("vmctx").is_err()); + + let mut big = MemFlags::new().with_alias_region(Some(AliasRegion::Vmctx)); + assert!(big.set_by_name("heap").is_err()); + assert!(big.set_by_name("table").is_err()); + } +} diff --git a/cranelift/codegen/src/ir/memtype.rs b/cranelift/codegen/src/ir/memtype.rs new file mode 100644 index 000000000000..4df26f1647e5 --- /dev/null +++ b/cranelift/codegen/src/ir/memtype.rs @@ -0,0 +1,190 @@ +//! 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 std::default::Default for MemoryTypeData { + fn default() -> Self { + Self::Empty + } +} + +impl std::fmt::Display for MemoryTypeData { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::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 986f8003722c..e6f082d70a8d 100644 --- a/cranelift/codegen/src/ir/mod.rs +++ b/cranelift/codegen/src/ir/mod.rs @@ -18,12 +18,14 @@ 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; -mod table; mod trapcode; pub mod types; +mod user_stack_maps; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; @@ -37,7 +39,7 @@ pub use crate::ir::dfg::{BlockData, DataFlowGraph, ValueDef}; pub use crate::ir::dynamic_type::{dynamic_to_fixed, DynamicTypeData, DynamicTypes}; pub use crate::ir::entities::{ Block, Constant, DynamicStackSlot, DynamicType, FuncRef, GlobalValue, Immediate, Inst, - JumpTable, SigRef, StackSlot, Table, UserExternalNameRef, Value, + JumpTable, MemoryType, SigRef, StackSlot, UserExternalNameRef, Value, }; pub use crate::ir::extfunc::{ AbiParam, ArgumentExtension, ArgumentPurpose, ExtFuncData, Signature, @@ -52,16 +54,18 @@ pub use crate::ir::jumptable::JumpTableData; pub use crate::ir::known_symbol::KnownSymbol; pub use crate::ir::layout::Layout; pub use crate::ir::libcall::{get_probestack_funcref, LibCall}; -pub use crate::ir::memflags::{Endianness, MemFlags}; +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; pub use crate::ir::stackslot::{ DynamicStackSlotData, DynamicStackSlots, StackSlotData, StackSlotKind, StackSlots, }; -pub use crate::ir::table::TableData; pub use crate::ir::trapcode::TrapCode; pub use crate::ir::types::Type; +pub use crate::ir::user_stack_maps::{UserStackMap, UserStackMapEntry}; use crate::entity::{entity_impl, PrimaryMap, SecondaryMap}; diff --git a/cranelift/codegen/src/ir/pcc.rs b/cranelift/codegen/src/ir/pcc.rs new file mode 100644 index 000000000000..33a515cac670 --- /dev/null +++ b/cranelift/codegen/src/ir/pcc.rs @@ -0,0 +1,1682 @@ +//! 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 +//! `cranelift-wasm`) 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 regalloc2::Function as _; +use std::fmt; + +#[cfg(feature = "enable-serde")] +use serde_derive::{Deserialize, Serialize}; + +/// The result of checking proof-carrying-code facts. +pub type PccResult = std::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: std::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: std::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: std::cmp::max(*min_lhs, *min_rhs), + max: std::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: std::cmp::max(*min_offset_lhs, *min_offset_rhs), + max_offset: std::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 = std::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: ref min_dynamic, + max: ref max_dynamic, + }, + ) + | ( + Fact::DynamicRange { + bit_width: bw_dynamic, + min: ref min_dynamic, + max: ref 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: ref min_dynamic, + max: ref max_dynamic, + nullable, + }, + ) + | ( + Fact::DynamicMem { + ty, + min: ref min_dynamic, + max: ref 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/ir/progpoint.rs b/cranelift/codegen/src/ir/progpoint.rs index 780054776714..84d394276f67 100644 --- a/cranelift/codegen/src/ir/progpoint.rs +++ b/cranelift/codegen/src/ir/progpoint.rs @@ -23,7 +23,7 @@ impl ProgramPoint { pub fn unwrap_inst(self) -> Inst { match self { Self::Inst(x) => x, - Self::Block(x) => panic!("expected inst: {}", x), + Self::Block(x) => panic!("expected inst: {x}"), } } } @@ -43,15 +43,15 @@ impl From for ProgramPoint { impl fmt::Display for ProgramPoint { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Self::Inst(x) => write!(f, "{}", x), - Self::Block(x) => write!(f, "{}", x), + Self::Inst(x) => write!(f, "{x}"), + Self::Block(x) => write!(f, "{x}"), } } } impl fmt::Debug for ProgramPoint { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ProgramPoint({})", self) + write!(f, "ProgramPoint({self})") } } @@ -59,7 +59,6 @@ impl fmt::Debug for ProgramPoint { mod tests { use super::*; use crate::entity::EntityRef; - use crate::ir::{Block, Inst}; use alloc::string::ToString; #[test] diff --git a/cranelift/codegen/src/ir/stackslot.rs b/cranelift/codegen/src/ir/stackslot.rs index dfe0109e90c5..d906fb291324 100644 --- a/cranelift/codegen/src/ir/stackslot.rs +++ b/cranelift/codegen/src/ir/stackslot.rs @@ -70,32 +70,38 @@ pub struct StackSlotData { /// Size of stack slot in bytes. pub size: StackSize, + + /// Alignment of stack slot as a power-of-two exponent (log2 + /// value). The stack slot will be at least this aligned; it may + /// be aligned according to other considerations, such as minimum + /// stack slot size or machine word size, as well. + pub align_shift: u8, } impl StackSlotData { - /// Create a stack slot with the specified byte size. - pub fn new(kind: StackSlotKind, size: StackSize) -> Self { - Self { kind, size } - } - - /// Get the alignment in bytes of this stack slot given the stack pointer alignment. - pub fn alignment(&self, max_align: StackSize) -> StackSize { - debug_assert!(max_align.is_power_of_two()); - if self.kind == StackSlotKind::ExplicitDynamicSlot { - max_align - } else { - // We want to find the largest power of two that divides both `self.size` and `max_align`. - // That is the same as isolating the rightmost bit in `x`. - let x = self.size | max_align; - // C.f. Hacker's delight. - x & x.wrapping_neg() + /// Create a stack slot with the specified byte size and alignment. + pub fn new(kind: StackSlotKind, size: StackSize, align_shift: u8) -> Self { + Self { + kind, + size, + align_shift, } } } impl fmt::Display for StackSlotData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.kind, self.size) + if self.align_shift != 0 { + write!( + f, + "{} {}, align = {}", + self.kind, + self.size, + 1u32 << self.align_shift + ) + } else { + write!(f, "{} {}", self.kind, self.size) + } } } @@ -146,8 +152,10 @@ mod tests { fn stack_slot() { let mut func = Function::new(); - let ss0 = func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4)); - let ss1 = func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8)); + let ss0 = + func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0)); + let ss1 = + func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8, 0)); assert_eq!(ss0.to_string(), "ss0"); assert_eq!(ss1.to_string(), "ss1"); @@ -197,20 +205,4 @@ mod tests { "explicit_dynamic_slot dt1" ); } - - #[test] - fn alignment() { - let slot = StackSlotData::new(StackSlotKind::ExplicitSlot, 8); - - assert_eq!(slot.alignment(4), 4); - assert_eq!(slot.alignment(8), 8); - assert_eq!(slot.alignment(16), 8); - - let slot2 = StackSlotData::new(StackSlotKind::ExplicitSlot, 24); - - assert_eq!(slot2.alignment(4), 4); - assert_eq!(slot2.alignment(8), 8); - assert_eq!(slot2.alignment(16), 8); - assert_eq!(slot2.alignment(32), 8); - } } diff --git a/cranelift/codegen/src/ir/table.rs b/cranelift/codegen/src/ir/table.rs deleted file mode 100644 index 6c6b32a147b9..000000000000 --- a/cranelift/codegen/src/ir/table.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Tables. - -use crate::ir::immediates::Uimm64; -use crate::ir::{GlobalValue, Type}; -use core::fmt; - -#[cfg(feature = "enable-serde")] -use serde_derive::{Deserialize, Serialize}; - -/// Information about a table declaration. -#[derive(Clone, PartialEq, Hash)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -pub struct TableData { - /// Global value giving the address of the start of the table. - pub base_gv: GlobalValue, - - /// Guaranteed minimum table size in elements. Table accesses before `min_size` don't need - /// bounds checking. - pub min_size: Uimm64, - - /// Global value giving the current bound of the table, in elements. - pub bound_gv: GlobalValue, - - /// The size of a table element, in bytes. - pub element_size: Uimm64, - - /// The index type for the table. - pub index_type: Type, -} - -impl fmt::Display for TableData { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("dynamic")?; - write!( - f, - " {}, min {}, bound {}, element_size {}, index_type {}", - self.base_gv, self.min_size, self.bound_gv, self.element_size, self.index_type - ) - } -} diff --git a/cranelift/codegen/src/ir/trapcode.rs b/cranelift/codegen/src/ir/trapcode.rs index 339c331ea529..e33a655456a3 100644 --- a/cranelift/codegen/src/ir/trapcode.rs +++ b/cranelift/codegen/src/ir/trapcode.rs @@ -46,7 +46,6 @@ pub enum TrapCode { UnreachableCodeReached, /// Execution has potentially run too long and may be interrupted. - /// This trap is resumable. Interrupt, /// A user-defined trap code. @@ -54,6 +53,9 @@ pub enum TrapCode { /// A null reference was encountered which was required to be non-null. NullReference, + + /// A null `i31ref` was encountered which was required to be non-null. + NullI31Ref, } impl TrapCode { @@ -91,8 +93,9 @@ impl Display for TrapCode { BadConversionToInteger => "bad_toint", UnreachableCodeReached => "unreachable", Interrupt => "interrupt", - User(x) => return write!(f, "user{}", x), + User(x) => return write!(f, "user{x}"), NullReference => "null_reference", + NullI31Ref => "null_i31ref", }; f.write_str(identifier) } @@ -116,6 +119,7 @@ impl FromStr for TrapCode { "unreachable" => Ok(UnreachableCodeReached), "interrupt" => Ok(Interrupt), "null_reference" => Ok(NullReference), + "null_i31ref" => Ok(NullI31Ref), _ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()), _ => Err(()), } diff --git a/cranelift/codegen/src/ir/types.rs b/cranelift/codegen/src/ir/types.rs index b1a58c5a2091..bc7704cf0662 100644 --- a/cranelift/codegen/src/ir/types.rs +++ b/cranelift/codegen/src/ir/types.rs @@ -1,6 +1,5 @@ //! Common types for the Cranelift code generator. -use core::default::Default; use core::fmt::{self, Debug, Display, Formatter}; use cranelift_codegen_shared::constants; #[cfg(feature = "enable-serde")] @@ -15,7 +14,7 @@ use target_lexicon::{PointerWidth, Triple}; /// /// Basic integer types: `I8`, `I16`, `I32`, `I64`, and `I128`. These types are sign-agnostic. /// -/// Basic floating point types: `F32` and `F64`. IEEE single and double precision. +/// Basic floating point types: `F16`, `F32`, `F64`, and `F128`. IEEE half, single, double, and quadruple precision. /// /// SIMD vector types have power-of-two lanes, up to 256. Lanes can be any int/float type. /// @@ -57,10 +56,10 @@ impl Type { pub fn log2_lane_bits(self) -> u32 { match self.lane_type() { I8 => 3, - I16 => 4, - I32 | F32 | R32 => 5, - I64 | F64 | R64 => 6, - I128 => 7, + I16 | F16 => 4, + I32 | F32 => 5, + I64 | F64 => 6, + I128 | F128 => 7, _ => 0, } } @@ -69,10 +68,10 @@ impl Type { pub fn lane_bits(self) -> u32 { match self.lane_type() { I8 => 8, - I16 => 16, - I32 | F32 | R32 => 32, - I64 | F64 | R64 => 64, - I128 => 128, + I16 | F16 => 16, + I32 | F32 => 32, + I64 | F64 => 64, + I128 | F128 => 128, _ => 0, } } @@ -137,11 +136,10 @@ impl Type { // Replace the low 4 bits with the boolean version, preserve the high 4 bits. self.replace_lanes(match self.lane_type() { I8 => I8, - I16 => I16, + I16 | F16 => I16, I32 | F32 => I32, I64 | F64 => I64, - R32 | R64 => panic!("Reference types are not truthy"), - I128 => I128, + I128 | F128 => I128, _ => I8, }) } @@ -159,15 +157,13 @@ impl Type { /// Get a type with the same number of lanes as this type, but with the lanes replaced by /// integers of the same size. - /// - /// Scalar types follow this same rule, but `b1` is converted into `i8` pub fn as_int(self) -> Self { self.replace_lanes(match self.lane_type() { I8 => I8, - I16 => I16, + I16 | F16 => I16, I32 | F32 => I32, I64 | F64 => I64, - I128 => I128, + I128 | F128 => I128, _ => unimplemented!(), }) } @@ -180,7 +176,9 @@ impl Type { I32 => I16, I64 => I32, I128 => I64, + F32 => F16, F64 => F32, + F128 => F64, _ => return None, })) } @@ -193,7 +191,9 @@ impl Type { I16 => I32, I32 => I64, I64 => I128, + F16 => F32, F32 => F64, + F64 => F128, _ => return None, })) } @@ -238,15 +238,7 @@ impl Type { /// Is this a scalar floating point type? pub fn is_float(self) -> bool { match self { - F32 | F64 => true, - _ => false, - } - } - - /// Is this a ref type? - pub fn is_ref(self) -> bool { - match self { - R32 | R64 => true, + F16 | F32 | F64 | F128 => true, _ => false, } } @@ -436,8 +428,6 @@ impl Display for Type { write!(f, "{}x{}", self.lane_type(), self.lane_count()) } else if self.is_dynamic_vector() { write!(f, "{:?}x{}xN", self.lane_type(), self.min_lane_count()) - } else if self.is_ref() { - write!(f, "r{}", self.lane_bits()) } else { match *self { INVALID => panic!("INVALID encountered"), @@ -457,8 +447,6 @@ impl Debug for Type { write!(f, "{:?}X{}", self.lane_type(), self.lane_count()) } else if self.is_dynamic_vector() { write!(f, "{:?}X{}XN", self.lane_type(), self.min_lane_count()) - } else if self.is_ref() { - write!(f, "types::R{}", self.lane_bits()) } else { match *self { INVALID => write!(f, "types::INVALID"), @@ -489,11 +477,11 @@ mod tests { assert_eq!(I64, I64.lane_type()); assert_eq!(I128, I128.lane_type()); assert_eq!(F32, F32.lane_type()); + assert_eq!(F16, F16.lane_type()); assert_eq!(F64, F64.lane_type()); + assert_eq!(F128, F128.lane_type()); assert_eq!(I32, I32X4.lane_type()); assert_eq!(F64, F64X2.lane_type()); - assert_eq!(R32, R32.lane_type()); - assert_eq!(R64, R64.lane_type()); assert_eq!(INVALID.lane_bits(), 0); assert_eq!(I8.lane_bits(), 8); @@ -501,10 +489,10 @@ mod tests { assert_eq!(I32.lane_bits(), 32); assert_eq!(I64.lane_bits(), 64); assert_eq!(I128.lane_bits(), 128); + assert_eq!(F16.lane_bits(), 16); assert_eq!(F32.lane_bits(), 32); assert_eq!(F64.lane_bits(), 64); - assert_eq!(R32.lane_bits(), 32); - assert_eq!(R64.lane_bits(), 64); + assert_eq!(F128.lane_bits(), 128); } #[test] @@ -517,8 +505,10 @@ mod tests { assert_eq!(I32X4.half_width(), Some(I16X4)); assert_eq!(I64.half_width(), Some(I32)); assert_eq!(I128.half_width(), Some(I64)); - assert_eq!(F32.half_width(), None); + assert_eq!(F16.half_width(), None); + assert_eq!(F32.half_width(), Some(F16)); assert_eq!(F64.half_width(), Some(F32)); + assert_eq!(F128.half_width(), Some(F64)); assert_eq!(INVALID.double_width(), None); assert_eq!(I8.double_width(), Some(I16)); @@ -527,8 +517,10 @@ mod tests { assert_eq!(I32X4.double_width(), Some(I64X4)); assert_eq!(I64.double_width(), Some(I128)); assert_eq!(I128.double_width(), None); + assert_eq!(F16.double_width(), Some(F32)); assert_eq!(F32.double_width(), Some(F64)); - assert_eq!(F64.double_width(), None); + assert_eq!(F64.double_width(), Some(F128)); + assert_eq!(F128.double_width(), None); } #[test] @@ -561,14 +553,18 @@ mod tests { // Conversions to and from vectors. assert_eq!(I8.by(16).unwrap().vector_to_dynamic(), Some(I8X16XN)); assert_eq!(I16.by(8).unwrap().vector_to_dynamic(), Some(I16X8XN)); + assert_eq!(F16.by(8).unwrap().vector_to_dynamic(), Some(F16X8XN)); assert_eq!(I32.by(4).unwrap().vector_to_dynamic(), Some(I32X4XN)); assert_eq!(F32.by(4).unwrap().vector_to_dynamic(), Some(F32X4XN)); assert_eq!(F64.by(2).unwrap().vector_to_dynamic(), Some(F64X2XN)); assert_eq!(I128.by(2).unwrap().vector_to_dynamic(), Some(I128X2XN)); + assert_eq!(F128.by(2).unwrap().vector_to_dynamic(), Some(F128X2XN)); assert_eq!(I128X2XN.dynamic_to_vector(), Some(I128X2)); + assert_eq!(F16X4XN.dynamic_to_vector(), Some(F16X4)); assert_eq!(F32X4XN.dynamic_to_vector(), Some(F32X4)); assert_eq!(F64X4XN.dynamic_to_vector(), Some(F64X4)); + assert_eq!(F128X4XN.dynamic_to_vector(), Some(F128X4)); assert_eq!(I32X2XN.dynamic_to_vector(), Some(I32X2)); assert_eq!(I32X8XN.dynamic_to_vector(), Some(I32X8)); assert_eq!(I16X16XN.dynamic_to_vector(), Some(I16X16)); @@ -589,8 +585,6 @@ mod tests { assert_eq!(I128.to_string(), "i128"); assert_eq!(F32.to_string(), "f32"); assert_eq!(F64.to_string(), "f64"); - assert_eq!(R32.to_string(), "r32"); - assert_eq!(R64.to_string(), "r64"); } #[test] diff --git a/cranelift/codegen/src/ir/user_stack_maps.rs b/cranelift/codegen/src/ir/user_stack_maps.rs new file mode 100644 index 000000000000..6ad9b697cd02 --- /dev/null +++ b/cranelift/codegen/src/ir/user_stack_maps.rs @@ -0,0 +1,199 @@ +//! User-defined stack maps. +//! +//! This module provides types allowing users to define stack maps and associate +//! them with safepoints. +//! +//! A **safepoint** is a program point (i.e. CLIF instruction) where it must be +//! safe to run GC. Currently all non-tail call instructions are considered +//! safepoints. (This does *not* allow, for example, skipping safepoints for +//! calls that are statically known not to trigger collections, or to have a +//! safepoint on a volatile load to a page that gets protected when it is time +//! to GC, triggering a fault that pauses the mutator and lets the collector do +//! its work before resuming the mutator. We can lift this restriction in the +//! future, if necessary.) +//! +//! A **stack map** is a description of where to find all the GC-managed values +//! that are live at a particular safepoint. Stack maps let the collector find +//! on-stack roots. Each stack map is logically a set of offsets into the stack +//! frame and the type of value at that associated offset. However, because the +//! stack layout isn't defined until much later in the compiler's pipeline, each +//! stack map entry instead includes both an `ir::StackSlot` and an offset +//! within that slot. +//! +//! These stack maps are **user-defined** in that it is the CLIF producer's +//! responsibility to identify and spill the live GC-managed values and attach +//! the associated stack map entries to each safepoint themselves (see +//! `cranelift_frontend::Function::declare_needs_stack_map` and +//! `cranelift_codegen::ir::DataFlowGraph::append_user_stack_map_entry`). Cranelift +//! will not insert spills and record these stack map entries automatically. +//! +//! Logically, a set of stack maps for a function record a table of the form: +//! +//! ```text +//! +---------------------+-------------------------------------------+ +//! | Instruction Pointer | SP-Relative Offsets of Live GC References | +//! +---------------------+-------------------------------------------+ +//! | 0x12345678 | 2, 6, 12 | +//! | 0x1234abcd | 2, 6 | +//! | ... | ... | +//! +---------------------+-------------------------------------------+ +//! ``` +//! +//! Where "instruction pointer" is an instruction pointer within the function, +//! and "offsets of live GC references" contains the offsets (in units of words) +//! from the frame's stack pointer where live GC references are stored on the +//! stack. Instruction pointers within the function that do not have an entry in +//! this table are not GC safepoints. +//! +//! Because +//! +//! * offsets of live GC references are relative from the stack pointer, and +//! * stack frames grow down from higher addresses to lower addresses, +//! +//! to get a pointer to a live reference at offset `x` within a stack frame, you +//! add `x` to the frame's stack pointer. +//! +//! For example, to calculate the pointer to the live GC reference inside "frame +//! 1" below, you would do `frame_1_sp + x`: +//! +//! ```text +//! Stack +//! +-------------------+ +//! | Frame 0 | +//! | | +//! | | | +//! | +-------------------+ <--- Frame 0's SP +//! | | Frame 1 | +//! Grows | | +//! down | | +//! | | Live GC reference | --+-- +//! | | | | +//! | | | | +//! V | | x = offset of live GC reference +//! | | | +//! | | | +//! +-------------------+ --+-- <--- Frame 1's SP +//! | Frame 2 | +//! | ... | +//! ``` +//! +//! An individual `UserStackMap` is associated with just one instruction pointer +//! within the function, contains the size of the stack frame, and represents +//! the stack frame as a bitmap. There is one bit per word in the stack frame, +//! and if the bit is set, then the word contains a live GC reference. +//! +//! Note that a caller's outgoing argument stack slots (if any) and callee's +//! incoming argument stack slots (if any) overlap, so we must choose which +//! function's stack maps record live GC references in these slots. We record +//! the incoming arguments in the callee's stack map. This choice plays nice +//! with tail calls, where by the time we transfer control to the callee, the +//! caller no longer exists. + +use crate::ir; +use cranelift_bitset::CompoundBitSet; +use cranelift_entity::PrimaryMap; +use smallvec::SmallVec; + +pub(crate) type UserStackMapEntryVec = SmallVec<[UserStackMapEntry; 4]>; + +/// A stack map entry describes a single GC-managed value and its location on +/// the stack. +/// +/// A stack map entry is associated with a particular instruction, and that +/// instruction must be a safepoint. The GC-managed value must be stored in the +/// described location across this entry's instruction. +#[derive(Clone, Debug, PartialEq, Hash)] +#[cfg_attr( + feature = "enable-serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct UserStackMapEntry { + /// The type of the value stored in this stack map entry. + pub ty: ir::Type, + + /// The stack slot that this stack map entry is within. + pub slot: ir::StackSlot, + + /// The offset within the stack slot where this entry's value can be found. + pub offset: u32, +} + +/// A compiled stack map, describing the location of many GC-managed values. +/// +/// A stack map is associated with a particular instruction, and that +/// instruction is a safepoint. +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr( + feature = "enable-serde", + derive(serde_derive::Deserialize, serde_derive::Serialize) +)] +pub struct UserStackMap { + // Offsets into the frame's sized stack slots that are GC references, by type. + by_type: SmallVec<[(ir::Type, CompoundBitSet); 1]>, + + // The offset of the sized stack slots, from SP, for this stack map's + // associated PC. + // + // This is initially `None` upon construction during lowering, but filled in + // after regalloc during emission when we have the precise frame layout. + sp_to_sized_stack_slots: Option, +} + +impl UserStackMap { + /// Coalesce the given entries into a new `UserStackMap`. + pub(crate) fn new( + entries: &[UserStackMapEntry], + stack_slot_offsets: &PrimaryMap, + ) -> Self { + let mut by_type = SmallVec::<[(ir::Type, CompoundBitSet); 1]>::default(); + + for entry in entries { + let offset = stack_slot_offsets[entry.slot] + entry.offset; + let offset = usize::try_from(offset).unwrap(); + + // Don't bother trying to avoid an `O(n)` search here: `n` is + // basically always one in practice; even if it isn't, there aren't + // that many different CLIF types. + let index = by_type + .iter() + .position(|(ty, _)| *ty == entry.ty) + .unwrap_or_else(|| { + by_type.push((entry.ty, CompoundBitSet::with_capacity(offset + 1))); + by_type.len() - 1 + }); + + by_type[index].1.insert(offset); + } + + UserStackMap { + by_type, + sp_to_sized_stack_slots: None, + } + } + + /// Finalize this stack map by filling in the SP-to-stack-slots offset. + pub(crate) fn finalize(&mut self, sp_to_sized_stack_slots: u32) { + debug_assert!(self.sp_to_sized_stack_slots.is_none()); + self.sp_to_sized_stack_slots = Some(sp_to_sized_stack_slots); + } + + /// Iterate over the entries in this stack map. + /// + /// Yields pairs of the type of GC reference that is at the offset, and the + /// offset from SP. If a pair `(i64, 0x42)` is yielded, for example, then + /// when execution is at this stack map's associated PC, `SP + 0x42` is a + /// pointer to an `i64`, and that `i64` is a live GC reference. + pub fn entries(&self) -> impl Iterator + '_ { + let sp_to_sized_stack_slots = self.sp_to_sized_stack_slots.expect( + "`sp_to_sized_stack_slots` should have been filled in before this stack map was used", + ); + self.by_type.iter().flat_map(move |(ty, bitset)| { + bitset.iter().map(move |slot_offset| { + ( + *ty, + sp_to_sized_stack_slots + u32::try_from(slot_offset).unwrap(), + ) + }) + }) + } +} diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs index bcf865d2603f..59a493ea2b41 100644 --- a/cranelift/codegen/src/isa/aarch64/abi.rs +++ b/cranelift/codegen/src/isa/aarch64/abi.rs @@ -4,17 +4,16 @@ use crate::ir; use crate::ir::types; use crate::ir::types::*; use crate::ir::MemFlags; -use crate::ir::Opcode; use crate::ir::{dynamic_to_fixed, ExternalName, LibCall, Signature}; use crate::isa; -use crate::isa::aarch64::{inst::EmitState, inst::*, settings as aarch64_settings}; +use crate::isa::aarch64::{inst::*, settings as aarch64_settings}; use crate::isa::unwind::UnwindInst; use crate::machinst::*; use crate::settings; use crate::{CodegenError, CodegenResult}; use alloc::boxed::Box; use alloc::vec::Vec; -use regalloc2::{MachineEnv, PReg, PRegSet, VReg}; +use regalloc2::{MachineEnv, PReg, PRegSet}; use smallvec::{smallvec, SmallVec}; use std::sync::OnceLock; @@ -35,9 +34,11 @@ static STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024; impl Into for StackAMode { fn into(self) -> AMode { match self { - StackAMode::FPOffset(off, ty) => AMode::FPOffset { off, ty }, - StackAMode::NominalSPOffset(off, ty) => AMode::NominalSPOffset { off, ty }, - StackAMode::SPOffset(off, ty) => AMode::SPOffset { off, ty }, + StackAMode::IncomingArg(off, stack_args_size) => AMode::IncomingArg { + off: i64::from(stack_args_size) - off, + }, + StackAMode::Slot(off) => AMode::SlotOffset { off }, + StackAMode::OutgoingArg(off) => AMode::SPOffset { off }, } } } @@ -99,21 +100,14 @@ impl ABIMachineSpec for AArch64MachineDeps { 16 } - fn compute_arg_locs<'a, I>( + fn compute_arg_locs( call_conv: isa::CallConv, - _flags: &settings::Flags, - params: I, + flags: &settings::Flags, + params: &[ir::AbiParam], args_or_rets: ArgsOrRets, add_ret_area_ptr: bool, - mut args: ArgsAccumulator<'_>, - ) -> CodegenResult<(u32, Option)> - where - I: IntoIterator, - { - if call_conv == isa::CallConv::Tail { - return compute_arg_locs_tail(params, add_ret_area_ptr, args); - } - + mut args: ArgsAccumulator, + ) -> CodegenResult<(u32, Option)> { let is_apple_cc = call_conv.extends_apple_aarch64(); // See AArch64 ABI (https://github.com/ARM-software/abi-aa/blob/2021Q1/aapcs64/aapcs64.rst#64parameter-passing), sections 6.4. @@ -130,7 +124,20 @@ impl ABIMachineSpec for AArch64MachineDeps { // break our other invariants that the stack is always allocated in // 16-bytes chunks. - let mut next_xreg = 0; + let mut next_xreg = if call_conv == isa::CallConv::Tail { + // We reserve `x0` for the return area pointer. For simplicity, we + // reserve it even when there is no return area pointer needed. This + // also means that identity functions don't have to shuffle arguments to + // different return registers because we shifted all argument register + // numbers down by one to make space for the return area pointer. + // + // Also, we cannot use all allocatable GPRs as arguments because we need + // at least one allocatable register for holding the callee address in + // indirect calls. So skip `x1` also, reserving it for that role. + 2 + } else { + 0 + }; let mut next_vreg = 0; let mut next_stack: u32 = 0; @@ -148,14 +155,27 @@ impl ABIMachineSpec for AArch64MachineDeps { }; for param in params { - assert!( - legal_type_for_machine(param.value_type), - "Invalid type for AArch64: {:?}", - param.value_type - ); + if is_apple_cc && param.value_type == types::F128 && !flags.enable_llvm_abi_extensions() + { + panic!( + "f128 args/return values not supported for apple_aarch64 unless LLVM ABI extensions are enabled" + ); + } let (rcs, reg_types) = Inst::rc_for_type(param.value_type)?; + if matches!( + param.purpose, + ir::ArgumentPurpose::StructArgument(_) | ir::ArgumentPurpose::StructReturn + ) { + assert!( + call_conv != isa::CallConv::Tail, + "support for {:?} parameters is not implemented for the `tail` calling \ + convention yet", + param.purpose, + ); + } + if let ir::ArgumentPurpose::StructArgument(size) = param.purpose { assert_eq!(args_or_rets, ArgsOrRets::Args); let offset = next_stack as i64; @@ -193,7 +213,7 @@ impl ABIMachineSpec for AArch64MachineDeps { // // See AArch64 ABI (https://github.com/ARM-software/abi-aa/blob/2021Q1/aapcs64/aapcs64.rst#642parameter-passing-rules), (Section 6.4.2 Stage C). // - // For arguments with alignment of 16 we round up the the register number + // For arguments with alignment of 16 we round up the register number // to the next even value. So we can never allocate for example an i128 // to X1 and X2, we have to skip one register and do X2, X3 // (Stage C.8) @@ -336,21 +356,30 @@ impl ABIMachineSpec for AArch64MachineDeps { let extra_arg = if add_ret_area_ptr { debug_assert!(args_or_rets == ArgsOrRets::Args); - if next_xreg < max_per_class_reg_vals && remaining_reg_vals > 0 { + if call_conv == isa::CallConv::Tail { args.push_non_formal(ABIArg::reg( - xreg(next_xreg).to_real_reg().unwrap(), + xreg_preg(0).into(), I64, ir::ArgumentExtension::None, ir::ArgumentPurpose::Normal, )); } else { - args.push_non_formal(ABIArg::stack( - next_stack as i64, - I64, - ir::ArgumentExtension::None, - ir::ArgumentPurpose::Normal, - )); - next_stack += 8; + if next_xreg < max_per_class_reg_vals && remaining_reg_vals > 0 { + args.push_non_formal(ABIArg::reg( + xreg(next_xreg).to_real_reg().unwrap(), + I64, + ir::ArgumentExtension::None, + ir::ArgumentPurpose::Normal, + )); + } else { + args.push_non_formal(ABIArg::stack( + next_stack as i64, + I64, + ir::ArgumentExtension::None, + ir::ArgumentPurpose::Normal, + )); + next_stack += 8; + } } Some(args.args().len() - 1) } else { @@ -368,10 +397,6 @@ impl ABIMachineSpec for AArch64MachineDeps { Ok((next_stack, extra_arg)) } - fn fp_to_arg_offset(_call_conv: isa::CallConv, _flags: &settings::Flags) -> i64 { - 16 // frame pointer + return address. - } - fn gen_load_stack(mem: StackAMode, into_reg: Writable, ty: Type) -> Inst { Inst::gen_load(into_reg, mem.into(), ty, MemFlags::trusted()) } @@ -463,7 +488,7 @@ impl ABIMachineSpec for AArch64MachineDeps { insts } - fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable, _ty: Type) -> Inst { + fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable) -> Inst { // FIXME: Do something different for dynamic types? let mem = mem.into(); Inst::LoadAddr { rd: into_reg, mem } @@ -477,7 +502,6 @@ impl ABIMachineSpec for AArch64MachineDeps { let mem = AMode::RegOffset { rn: base, off: offset as i64, - ty, }; Inst::gen_load(into_reg, mem, ty, MemFlags::trusted()) } @@ -486,7 +510,6 @@ impl ABIMachineSpec for AArch64MachineDeps { let mem = AMode::RegOffset { rn: base, off: offset as i64, - ty, }; Inst::gen_store(mem, from_reg, ty, MemFlags::trusted()) } @@ -533,12 +556,6 @@ impl ABIMachineSpec for AArch64MachineDeps { ret } - fn gen_nominal_sp_adj(offset: i32) -> Inst { - Inst::VirtualSPOffsetAdj { - offset: offset as i64, - } - } - fn gen_prologue_frame_setup( call_conv: isa::CallConv, flags: &settings::Flags, @@ -616,7 +633,7 @@ impl ABIMachineSpec for AArch64MachineDeps { fn gen_epilogue_frame_restore( call_conv: isa::CallConv, _flags: &settings::Flags, - isa_flags: &aarch64_settings::Flags, + _isa_flags: &aarch64_settings::Flags, frame_layout: &FrameLayout, ) -> SmallInstVec { let setup_frame = frame_layout.setup_area_size > 0; @@ -638,24 +655,33 @@ impl ABIMachineSpec for AArch64MachineDeps { }); } - if call_conv == isa::CallConv::Tail && frame_layout.stack_args_size > 0 { + if call_conv == isa::CallConv::Tail && frame_layout.tail_args_size > 0 { insts.extend(Self::gen_sp_reg_adjust( - frame_layout.stack_args_size.try_into().unwrap(), + frame_layout.tail_args_size.try_into().unwrap(), )); } + + insts + } + + fn gen_return( + call_conv: isa::CallConv, + isa_flags: &aarch64_settings::Flags, + frame_layout: &FrameLayout, + ) -> SmallInstVec { + let setup_frame = frame_layout.setup_area_size > 0; + match select_api_key(isa_flags, call_conv, setup_frame) { Some(key) => { - insts.push(Inst::AuthenticatedRet { + smallvec![Inst::AuthenticatedRet { key, is_hint: !isa_flags.has_pauth(), - }); + }] } None => { - insts.push(Inst::Ret {}); + smallvec![Inst::Ret {}] } } - - insts } fn gen_probestack(_insts: &mut SmallInstVec, _: u32) { @@ -677,43 +703,9 @@ impl ABIMachineSpec for AArch64MachineDeps { let probe_count = align_to(frame_size, guard_size) / guard_size; if probe_count <= PROBE_MAX_UNROLL { - // When manually unrolling stick an instruction that stores 0 at a - // constant offset relative to the stack pointer. This will - // turn into something like `movn tmp, #n ; stur xzr [sp, tmp]`. - // - // Note that this may actually store beyond the stack size for the - // last item but that's ok since it's unused stack space and if - // that faults accidentally we're so close to faulting it shouldn't - // make too much difference to fault there. - insts.reserve(probe_count as usize); - for i in 0..probe_count { - let offset = (guard_size * (i + 1)) as i64; - insts.push(Self::gen_store_stack( - StackAMode::SPOffset(-offset, I8), - zero_reg(), - I32, - )); - } + Self::gen_probestack_unroll(insts, guard_size, probe_count) } else { - // The non-unrolled version uses two temporary registers. The - // `start` contains the current offset from sp and counts downwards - // during the loop by increments of `guard_size`. The `end` is - // the size of the frame and where we stop. - // - // Note that this emission is all post-regalloc so it should be ok - // to use the temporary registers here as input/output as the loop - // itself is not allowed to use the registers. - let start = writable_spilltmp_reg(); - let end = writable_tmp2_reg(); - // `gen_inline_probestack` is called after regalloc2, so it's acceptable to reuse - // `start` and `end` as temporaries in load_constant. - insts.extend(Inst::load_constant(start, 0, &mut |_| start)); - insts.extend(Inst::load_constant(end, frame_size.into(), &mut |_| end)); - insts.push(Inst::StackProbeLoop { - start, - end: end.to_reg(), - step: Imm12::maybe_from_u64(guard_size.into()).unwrap(), - }); + Self::gen_probestack_loop(insts, frame_size, guard_size) } } @@ -722,20 +714,50 @@ impl ABIMachineSpec for AArch64MachineDeps { flags: &settings::Flags, frame_layout: &FrameLayout, ) -> SmallVec<[Inst; 16]> { - let mut clobbered_int = vec![]; - let mut clobbered_vec = vec![]; - - for ® in frame_layout.clobbered_callee_saves.iter() { - match reg.to_reg().class() { - RegClass::Int => clobbered_int.push(reg), - RegClass::Float => clobbered_vec.push(reg), - RegClass::Vector => unreachable!(), - } - } + let (clobbered_int, clobbered_vec) = frame_layout.clobbered_callee_saves_by_class(); let mut insts = SmallVec::new(); + let setup_frame = frame_layout.setup_area_size > 0; + + // When a return_call within this function required more stack arguments than we have + // present, resize the incoming argument area of the frame to accommodate those arguments. + let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size; + if incoming_args_diff > 0 { + // Decrement SP to account for the additional space required by a tail call. + insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32))); + + // Move fp and lr down. + if setup_frame { + // Reload the frame pointer from the stack. + insts.push(Inst::ULoad64 { + rd: regs::writable_fp_reg(), + mem: AMode::SPOffset { + off: i64::from(incoming_args_diff), + }, + flags: MemFlags::trusted(), + }); - if flags.unwind_info() && frame_layout.setup_area_size > 0 { + // Store the frame pointer and link register again at the new SP + insts.push(Inst::StoreP64 { + rt: fp_reg(), + rt2: link_reg(), + mem: PairAMode::SignedOffset { + reg: regs::stack_reg(), + simm7: SImm7Scaled::maybe_from_i64(0, types::I64).unwrap(), + }, + flags: MemFlags::trusted(), + }); + + // Keep the frame pointer in sync + insts.push(Self::gen_move( + regs::writable_fp_reg(), + regs::stack_reg(), + types::I64, + )); + } + } + + if flags.unwind_info() && setup_frame { // The *unwind* frame (but not the actual frame) starts at the // clobbers, just below the saved FP/LR pair. insts.push(Inst::Unwind { @@ -896,10 +918,9 @@ impl ABIMachineSpec for AArch64MachineDeps { } // Allocate the fixed frame below the clobbers if necessary. - if frame_layout.fixed_frame_storage_size > 0 { - insts.extend(Self::gen_sp_reg_adjust( - -(frame_layout.fixed_frame_storage_size as i32), - )); + let stack_size = frame_layout.fixed_frame_storage_size + frame_layout.outgoing_args_size; + if stack_size > 0 { + insts.extend(Self::gen_sp_reg_adjust(-(stack_size as i32))); } insts @@ -911,22 +932,12 @@ impl ABIMachineSpec for AArch64MachineDeps { frame_layout: &FrameLayout, ) -> SmallVec<[Inst; 16]> { let mut insts = SmallVec::new(); - let mut clobbered_int = vec![]; - let mut clobbered_vec = vec![]; - - for ® in frame_layout.clobbered_callee_saves.iter() { - match reg.to_reg().class() { - RegClass::Int => clobbered_int.push(reg), - RegClass::Float => clobbered_vec.push(reg), - RegClass::Vector => unreachable!(), - } - } + let (clobbered_int, clobbered_vec) = frame_layout.clobbered_callee_saves_by_class(); // Free the fixed frame if necessary. - if frame_layout.fixed_frame_storage_size > 0 { - insts.extend(Self::gen_sp_reg_adjust( - frame_layout.fixed_frame_storage_size as i32, - )); + let stack_size = frame_layout.fixed_frame_storage_size + frame_layout.outgoing_args_size; + if stack_size > 0 { + insts.extend(Self::gen_sp_reg_adjust(stack_size as i32)); } let load_vec_reg = |rd| Inst::FpuLoad64 { @@ -1008,7 +1019,6 @@ impl ABIMachineSpec for AArch64MachineDeps { uses: CallArgList, defs: CallRetList, clobbers: PRegSet, - opcode: ir::Opcode, tmp: Writable, callee_conv: isa::CallConv, caller_conv: isa::CallConv, @@ -1022,7 +1032,6 @@ impl ABIMachineSpec for AArch64MachineDeps { uses, defs, clobbers, - opcode, caller_callconv: caller_conv, callee_callconv: callee_conv, callee_pop_size, @@ -1040,7 +1049,6 @@ impl ABIMachineSpec for AArch64MachineDeps { uses, defs, clobbers, - opcode, caller_callconv: caller_conv, callee_callconv: callee_conv, callee_pop_size, @@ -1053,7 +1061,6 @@ impl ABIMachineSpec for AArch64MachineDeps { uses, defs, clobbers, - opcode, caller_callconv: caller_conv, callee_callconv: callee_conv, callee_pop_size, @@ -1096,7 +1103,6 @@ impl ABIMachineSpec for AArch64MachineDeps { ], defs: smallvec![], clobbers: Self::get_regs_clobbered_by_call(call_conv), - opcode: Opcode::Call, caller_callconv: call_conv, callee_callconv: call_conv, callee_pop_size: 0, @@ -1119,16 +1125,6 @@ impl ABIMachineSpec for AArch64MachineDeps { } } - /// Get the current virtual-SP offset from an instruction-emission state. - fn get_virtual_sp_offset_from_state(s: &EmitState) -> i64 { - s.virtual_sp_offset - } - - /// Get the nominal-SP-to-FP offset from an instruction-emission state. - fn get_nominal_sp_to_fp(s: &EmitState) -> i64 { - s.nominal_sp_to_fp - } - fn get_machine_env(flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv { if flags.enable_pinned_reg() { static MACHINE_ENV: OnceLock = OnceLock::new(); @@ -1139,19 +1135,19 @@ impl ABIMachineSpec for AArch64MachineDeps { } } - fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> PRegSet { - if call_conv_of_callee == isa::CallConv::Tail { - TAIL_CLOBBERS - } else { - DEFAULT_AAPCS_CLOBBERS - } + fn get_regs_clobbered_by_call(_call_conv: isa::CallConv) -> PRegSet { + DEFAULT_AAPCS_CLOBBERS } fn get_ext_mode( - _call_conv: isa::CallConv, - _specified: ir::ArgumentExtension, + call_conv: isa::CallConv, + specified: ir::ArgumentExtension, ) -> ir::ArgumentExtension { - ir::ArgumentExtension::None + if call_conv == isa::CallConv::AppleAarch64 { + specified + } else { + ir::ArgumentExtension::None + } } fn compute_frame_layout( @@ -1160,7 +1156,8 @@ impl ABIMachineSpec for AArch64MachineDeps { sig: &Signature, regs: &[Writable], is_leaf: bool, - stack_args_size: u32, + incoming_args_size: u32, + tail_args_size: u32, fixed_frame_storage_size: u32, outgoing_args_size: u32, ) -> FrameLayout { @@ -1174,7 +1171,7 @@ impl ABIMachineSpec for AArch64MachineDeps { // Sort registers for deterministic code output. We can do an unstable // sort because the registers will be unique (there are no dups). - regs.sort_unstable_by_key(|r| VReg::from(r.to_reg()).vreg()); + regs.sort_unstable(); // Compute clobber size. let clobber_size = compute_clobber_size(®s); @@ -1184,7 +1181,7 @@ impl ABIMachineSpec for AArch64MachineDeps { || !is_leaf // The function arguments that are passed on the stack are addressed // relative to the Frame Pointer. - || stack_args_size > 0 + || incoming_args_size > 0 || clobber_size > 0 || fixed_frame_storage_size > 0 { @@ -1194,9 +1191,9 @@ impl ABIMachineSpec for AArch64MachineDeps { }; // Return FrameLayout structure. - debug_assert!(outgoing_args_size == 0); FrameLayout { - stack_args_size, + incoming_args_size, + tail_args_size, setup_area_size, clobber_size, fixed_frame_storage_size, @@ -1206,6 +1203,53 @@ impl ABIMachineSpec for AArch64MachineDeps { } } +impl AArch64MachineDeps { + fn gen_probestack_unroll(insts: &mut SmallInstVec, guard_size: u32, probe_count: u32) { + // When manually unrolling adjust the stack pointer and then write a zero + // to the stack at that offset. This generates something like + // `sub sp, sp, #1, lsl #12` followed by `stur wzr, [sp]`. + // + // We do this because valgrind expects us to never write beyond the stack + // pointer and associated redzone. + // See: https://github.com/bytecodealliance/wasmtime/issues/7454 + for _ in 0..probe_count { + insts.extend(Self::gen_sp_reg_adjust(-(guard_size as i32))); + + insts.push(Inst::gen_store( + AMode::SPOffset { off: 0 }, + zero_reg(), + I32, + MemFlags::trusted(), + )); + } + + // Restore the stack pointer to its original value + insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32)); + } + + fn gen_probestack_loop(insts: &mut SmallInstVec, frame_size: u32, guard_size: u32) { + // The non-unrolled version uses two temporary registers. The + // `start` contains the current offset from sp and counts downwards + // during the loop by increments of `guard_size`. The `end` is + // the size of the frame and where we stop. + // + // Note that this emission is all post-regalloc so it should be ok + // to use the temporary registers here as input/output as the loop + // itself is not allowed to use the registers. + let start = writable_spilltmp_reg(); + let end = writable_tmp2_reg(); + // `gen_inline_probestack` is called after regalloc2, so it's acceptable to reuse + // `start` and `end` as temporaries in load_constant. + insts.extend(Inst::load_constant(start, 0, &mut |_| start)); + insts.extend(Inst::load_constant(end, frame_size.into(), &mut |_| end)); + insts.push(Inst::StackProbeLoop { + start, + end: end.to_reg(), + step: Imm12::maybe_from_u64(guard_size.into()).unwrap(), + }); + } +} + fn select_api_key( isa_flags: &aarch64_settings::Flags, call_conv: isa::CallConv, @@ -1239,16 +1283,20 @@ impl AArch64CallSite { args: isle::ValueSlice, isa_flags: &aarch64_settings::Flags, ) { - let (new_stack_arg_size, old_stack_arg_size) = - self.emit_temporary_tail_call_frame(ctx, args); + let new_stack_arg_size = + u32::try_from(self.sig(ctx.sigs()).sized_stack_arg_space()).unwrap(); + + ctx.abi_mut().accumulate_tail_args_size(new_stack_arg_size); + + // Put all arguments in registers and stack slots (within that newly + // allocated stack space). + self.emit_args(ctx, args); + self.emit_stack_ret_arg_for_tail_call(ctx); let dest = self.dest().clone(); - let opcode = self.opcode(); let uses = self.take_uses(); let info = Box::new(ReturnCallInfo { uses, - opcode, - old_stack_arg_size, new_stack_arg_size, key: select_api_key(isa_flags, isa::CallConv::Tail, true), }); @@ -1275,148 +1323,14 @@ impl AArch64CallSite { } } -fn compute_arg_locs_tail<'a, I>( - params: I, - add_ret_area_ptr: bool, - mut args: ArgsAccumulator<'_>, -) -> CodegenResult<(u32, Option)> -where - I: IntoIterator, -{ - let mut xregs = TAIL_CLOBBERS - .into_iter() - .filter(|r| r.class() == RegClass::Int) - // We reserve `x0` for the return area pointer. For simplicity, we - // reserve it even when there is no return area pointer needed. This - // also means that identity functions don't have to shuffle arguments to - // different return registers because we shifted all argument register - // numbers down by one to make space for the return area pointer. - // - // Also, we cannot use all allocatable GPRs as arguments because we need - // at least one allocatable register for holding the callee address in - // indirect calls. So skip `x1` also, reserving it for that role. - .skip(2); - - let mut vregs = TAIL_CLOBBERS - .into_iter() - .filter(|r| r.class() == RegClass::Float); - - let mut next_stack: u32 = 0; - - // Get the next stack slot for the given type. - let stack = |next_stack: &mut u32, ty: ir::Type| { - *next_stack = align_to(*next_stack, ty.bytes()); - let offset = i64::from(*next_stack); - *next_stack += ty.bytes(); - ABIArgSlot::Stack { - offset, - ty, - extension: ir::ArgumentExtension::None, - } - }; - - // Get the next `x` register available, or a stack slot if all are in use. - let mut xreg = |next_stack: &mut u32, ty| { - xregs - .next() - .map(|reg| ABIArgSlot::Reg { - reg: reg.into(), - ty, - extension: ir::ArgumentExtension::None, - }) - .unwrap_or_else(|| stack(next_stack, ty)) - }; - - // Get the next `v` register available, or a stack slot if all are in use. - let mut vreg = |next_stack: &mut u32, ty| { - vregs - .next() - .map(|reg| ABIArgSlot::Reg { - reg: reg.into(), - ty, - extension: ir::ArgumentExtension::None, - }) - .unwrap_or_else(|| stack(next_stack, ty)) - }; - - for param in params { - assert!( - legal_type_for_machine(param.value_type), - "Invalid type for AArch64: {:?}", - param.value_type - ); - - match param.purpose { - ir::ArgumentPurpose::Normal | ir::ArgumentPurpose::VMContext => {} - ir::ArgumentPurpose::StructArgument(_) - | ir::ArgumentPurpose::StructReturn - | ir::ArgumentPurpose::StackLimit => unimplemented!( - "support for {:?} parameters is not implemented for the `tail` \ - calling convention yet", - param.purpose, - ), - } - - let (reg_classes, reg_types) = Inst::rc_for_type(param.value_type)?; - args.push(ABIArg::Slots { - slots: reg_classes - .iter() - .zip(reg_types) - .map(|(cls, ty)| match cls { - RegClass::Int => xreg(&mut next_stack, *ty), - RegClass::Float => vreg(&mut next_stack, *ty), - RegClass::Vector => unreachable!(), - }) - .collect(), - purpose: param.purpose, - }); - } - - let ret_ptr = if add_ret_area_ptr { - let idx = args.args().len(); - args.push(ABIArg::reg( - xreg_preg(0).into(), - types::I64, - ir::ArgumentExtension::None, - ir::ArgumentPurpose::Normal, - )); - Some(idx) - } else { - None - }; - - next_stack = align_to(next_stack, 16); - - // To avoid overflow issues, limit the arg/return size to something - // reasonable -- here, 128 MB. - if next_stack > STACK_ARG_RET_SIZE_LIMIT { - return Err(CodegenError::ImplLimitExceeded); - } - - Ok((next_stack, ret_ptr)) -} - -/// Is this type supposed to be seen on this machine? E.g. references of the -/// wrong width are invalid. -fn legal_type_for_machine(ty: Type) -> bool { - match ty { - R32 => false, - _ => true, - } -} - /// Is the given register saved in the prologue if clobbered, i.e., is it a /// callee-save? fn is_reg_saved_in_prologue( - call_conv: isa::CallConv, + _call_conv: isa::CallConv, enable_pinned_reg: bool, sig: &Signature, r: RealReg, ) -> bool { - if call_conv == isa::CallConv::Tail { - return false; - } - // FIXME: We need to inspect whether a function is returning Z or P regs too. let save_z_regs = sig .params @@ -1526,72 +1440,6 @@ const fn default_aapcs_clobbers() -> PRegSet { const DEFAULT_AAPCS_CLOBBERS: PRegSet = default_aapcs_clobbers(); -// NB: The `tail` calling convention clobbers all allocatable registers. -const TAIL_CLOBBERS: PRegSet = PRegSet::empty() - .with(xreg_preg(0)) - .with(xreg_preg(1)) - .with(xreg_preg(2)) - .with(xreg_preg(3)) - .with(xreg_preg(4)) - .with(xreg_preg(5)) - .with(xreg_preg(6)) - .with(xreg_preg(7)) - .with(xreg_preg(8)) - .with(xreg_preg(9)) - .with(xreg_preg(10)) - .with(xreg_preg(11)) - .with(xreg_preg(12)) - .with(xreg_preg(13)) - .with(xreg_preg(14)) - .with(xreg_preg(15)) - // Cranelift reserves x16 and x17 as unallocatable scratch registers. - // - // x18 can be used by the platform and therefore is not allocatable. - .with(xreg_preg(19)) - .with(xreg_preg(20)) - .with(xreg_preg(21)) - .with(xreg_preg(22)) - .with(xreg_preg(23)) - .with(xreg_preg(24)) - .with(xreg_preg(25)) - .with(xreg_preg(26)) - .with(xreg_preg(27)) - .with(xreg_preg(28)) - // NB: x29 is the FP, x30 is the link register, and x31 is the SP. None of - // these are allocatable. - .with(vreg_preg(0)) - .with(vreg_preg(1)) - .with(vreg_preg(2)) - .with(vreg_preg(3)) - .with(vreg_preg(4)) - .with(vreg_preg(5)) - .with(vreg_preg(6)) - .with(vreg_preg(7)) - .with(vreg_preg(8)) - .with(vreg_preg(9)) - .with(vreg_preg(10)) - .with(vreg_preg(11)) - .with(vreg_preg(12)) - .with(vreg_preg(13)) - .with(vreg_preg(14)) - .with(vreg_preg(15)) - .with(vreg_preg(16)) - .with(vreg_preg(17)) - .with(vreg_preg(18)) - .with(vreg_preg(19)) - .with(vreg_preg(20)) - .with(vreg_preg(21)) - .with(vreg_preg(22)) - .with(vreg_preg(23)) - .with(vreg_preg(24)) - .with(vreg_preg(25)) - .with(vreg_preg(26)) - .with(vreg_preg(27)) - .with(vreg_preg(28)) - .with(vreg_preg(29)) - .with(vreg_preg(30)) - .with(vreg_preg(31)); - fn create_reg_env(enable_pinned_reg: bool) -> MachineEnv { fn preg(r: Reg) -> PReg { r.to_real_reg().unwrap().into() diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle index 0e1d03334ffa..396510dc6118 100644 --- a/cranelift/codegen/src/isa/aarch64/inst.isle +++ b/cranelift/codegen/src/isa/aarch64/inst.isle @@ -185,7 +185,7 @@ (size OperandSize)) ;; A MOVK with a 16-bit immediate. Modifies its register; we - ;; model this with a seprate input `rn` and output `rd` virtual + ;; model this with a separate input `rn` and output `rd` virtual ;; register, with a regalloc constraint to tie them together. (MovK (rd WritableReg) @@ -331,6 +331,11 @@ ;; Consumption of speculative data barrier. (Csdb) + ;; FPU 32-bit move. + (FpuMove32 + (rd WritableReg) + (rn Reg)) + ;; FPU move. Note that this is distinct from a vector-register ;; move; moving just 64 bits seems to be significantly faster. (FpuMove64 @@ -402,6 +407,18 @@ (rn Reg) (rm Reg)) + ;; Floating-point load, half-precision (16 bit). + (FpuLoad16 + (rd WritableReg) + (mem AMode) + (flags MemFlags)) + + ;; Floating-point store, half-precision (16 bit). + (FpuStore16 + (rd Reg) + (mem AMode) + (flags MemFlags)) + ;; Floating-point load, single-precision (32 bit). (FpuLoad32 (rd WritableReg) @@ -478,6 +495,14 @@ (rd WritableReg) (rn Reg)) + ;; FP conditional select, 16 bit. + ;; Requires FEAT_FP16. + (FpuCSel16 + (rd WritableReg) + (rn Reg) + (rm Reg) + (cond Cond)) + ;; FP conditional select, 32 bit. (FpuCSel32 (rd WritableReg) @@ -499,8 +524,8 @@ (rn Reg)) ;; Move from a GPR to a vector register. The scalar value is parked in the lowest lane - ;; of the destination, and all other lanes are zeroed out. Currently only 32- and 64-bit - ;; transactions are supported. + ;; of the destination, and all other lanes are zeroed out. Currently 16-, 32- and 64-bit + ;; transactions are supported. 16-bit moves require FEAT_FP16. (MovToFpu (rd WritableReg) (rn Reg) @@ -833,6 +858,15 @@ (not_taken BranchTarget) (kind CondBrKind)) + ;; A conditional branch which tests the `bit` of `rn` and branches + ;; depending on `kind`. + (TestBitAndBranch + (kind TestBitAndBranchKind) + (taken BranchTarget) + (not_taken BranchTarget) + (rn Reg) + (bit u8)) + ;; A conditional trap: execute a `udf` if the condition is true. This is ;; one VCode instruction because it uses embedded control flow; it is ;; logically a single-in, single-out region, but needs to appear as one @@ -916,11 +950,6 @@ (Bti (targets BranchTargetType)) - ;; Marker, no-op in generated code: SP "virtual offset" is adjusted. This - ;; controls how AMode::NominalSPOffset args are lowered. - (VirtualSPOffsetAdj - (offset i64)) - ;; Meta-insn, no-op in generated code: emit constant/branch veneer island ;; at this point (with a guard jump around it) if less than the needed ;; space is available before the next branch deadline. See the `MachBuffer` @@ -947,8 +976,9 @@ ;; A call to the `ElfTlsGetAddr` libcall. Returns address of TLS symbol in x0. (ElfTlsGetAddr - (symbol ExternalName) - (rd WritableReg)) + (symbol BoxExternalName) + (rd WritableReg) + (tmp WritableReg)) (MachOTlsGetAddr (symbol ExternalName) @@ -971,6 +1001,263 @@ (end Reg) (step Imm12)))) +(attr MInst.FpuMove32 (tag float)) +(attr MInst.FpuMove64 (tag float)) +(attr MInst.FpuMove128 (tag float)) +(attr MInst.FpuMoveFromVec (tag float)) +(attr MInst.FpuExtend (tag float)) +(attr MInst.FpuRR (tag float)) +(attr MInst.FpuRRR (tag float)) +(attr MInst.FpuRRI (tag float)) +(attr MInst.FpuRRIMod (tag float)) +(attr MInst.FpuRRRR (tag float)) +(attr MInst.FpuCmp (tag float)) +(attr MInst.FpuLoad16 (tag float)) +(attr MInst.FpuLoad16 (tag narrowfloat)) +(attr MInst.FpuStore16 (tag float)) +(attr MInst.FpuStore16 (tag narrowfloat)) +(attr MInst.FpuLoad32 (tag float)) +(attr MInst.FpuStore32 (tag float)) +(attr MInst.FpuLoad64 (tag float)) +(attr MInst.FpuStore64 (tag float)) +(attr MInst.FpuLoad128 (tag float)) +(attr MInst.FpuLoad128 (tag i128)) +(attr MInst.FpuStore128 (tag float)) +(attr MInst.FpuStore128 (tag i128)) +(attr MInst.FpuLoadP64 (tag float)) +(attr MInst.FpuStoreP64 (tag float)) +(attr MInst.FpuLoadP128 (tag float)) +(attr MInst.FpuLoadP128 (tag i128)) +(attr MInst.FpuStoreP128 (tag float)) +(attr MInst.FpuStoreP128 (tag i128)) +(attr MInst.FpuToInt (tag float)) +(attr MInst.IntToFpu (tag float)) +(attr MInst.FpuCSel16 (tag float)) +(attr MInst.FpuCSel32 (tag float)) +(attr MInst.FpuCSel64 (tag float)) +(attr MInst.FpuRound (tag float)) +(attr MInst.MovToFpu (tag float)) +(attr MInst.FpuMoveFPImm (tag float)) + +(attr MInst.VecDup (tag vector)) +(attr MInst.VecDupFromFpu (tag vector)) +(attr MInst.VecDupFPImm (tag vector)) +(attr MInst.VecDupImm (tag vector)) +(attr MInst.VecExtend (tag vector)) +(attr MInst.VecMovElement (tag vector)) +(attr MInst.VecRRLong (tag vector)) +(attr MInst.VecRRNarrowLow (tag vector)) +(attr MInst.VecRRNarrowHigh (tag vector)) +(attr MInst.VecRRPair (tag vector)) +(attr MInst.VecRRRLong (tag vector)) +(attr MInst.VecRRRLongMod (tag vector)) +(attr MInst.VecRRPairLong (tag vector)) +(attr MInst.VecRRR (tag vector)) +(attr MInst.VecRRRMod (tag vector)) +(attr MInst.VecFmlaElem (tag vector)) +(attr MInst.VecMisc (tag vector)) +(attr MInst.VecLanes (tag vector)) +(attr MInst.VecShiftImm (tag vector)) +(attr MInst.VecShiftImmMod (tag vector)) +(attr MInst.VecExtract (tag vector)) +(attr MInst.VecTbl (tag vector)) +(attr MInst.VecTblExt (tag vector)) +(attr MInst.VecTbl2 (tag vector)) +(attr MInst.VecTbl2Ext (tag vector)) +(attr MInst.VecLoadReplicate (tag vector)) +(attr MInst.VecCSel (tag vector)) + +(attr MInst.LoadAcquire (tag atomics)) + +(model MInst + (type + (struct + (flags_in (named NZCV)) + (flags_out (named NZCV)) + ) + ) +) + +(spec (MInst.AluRRImmLogic alu_op size rd rn imml) + (provide + (=> + (and (= alu_op (ALUOp.Orr)) (= size (OperandSize.Size64))) + (= rd (bvor rn imml)) + ) + (=> + (and (= alu_op (ALUOp.Orr)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvor (extract 31 0 rn) (extract 31 0 imml)))) + ) + + (=> + (and (= alu_op (ALUOp.OrrNot)) (= size (OperandSize.Size64))) + (= rd (bvor rn (bvnot imml))) + ) + (=> + (and (= alu_op (ALUOp.OrrNot)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvor (extract 31 0 rn) (bvnot (extract 31 0 imml))))) + ) + + (=> + (and (= alu_op (ALUOp.And)) (= size (OperandSize.Size64))) + (= rd (bvand rn imml)) + ) + (=> + (and (= alu_op (ALUOp.And)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvand (extract 31 0 rn) (extract 31 0 imml)))) + ) + + (=> + (and (= alu_op (ALUOp.AndNot)) (= size (OperandSize.Size64))) + (= rd (bvand rn (bvnot imml))) + ) + (=> + (and (= alu_op (ALUOp.AndNot)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvand (extract 31 0 rn) (bvnot (extract 31 0 imml))))) + ) + + (=> + (and (= alu_op (ALUOp.Eor)) (= size (OperandSize.Size64))) + (= rd (bvxor rn imml)) + ) + (=> + (and (= alu_op (ALUOp.Eor)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvxor (extract 31 0 rn) (extract 31 0 imml)))) + ) + + (=> + (and (= alu_op (ALUOp.EorNot)) (= size (OperandSize.Size64))) + (= rd (bvxor rn (bvnot imml))) + ) + (=> + (and (= alu_op (ALUOp.EorNot)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvxor (extract 31 0 rn) (bvnot (extract 31 0 imml))))) + ) + ) + (require + ; Implemented (opcode, size) combinations. + (or + (and (= alu_op (ALUOp.Orr)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Orr)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.OrrNot)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.OrrNot)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.And)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.And)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.AndNot)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.AndNot)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.Eor)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Eor)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.EorNot)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.EorNot)) (= size (OperandSize.Size32))) + ) + ; REVIEW(mbm): require properties for logical immediates? + ) +) + + +(spec (MInst.AluRRImmShift alu_op size rd rn immshift) + (provide + (=> (and (= alu_op (ALUOp.Lsr)) (= size (OperandSize.Size64))) + (= rd (bvlshr rn (zero_ext 64 immshift))) + ) + (=> (and (= alu_op (ALUOp.Lsr)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvlshr (extract 31 0 rn) (zero_ext 32 immshift)))) + ) + + (=> (and (= alu_op (ALUOp.Asr)) (= size (OperandSize.Size64))) + (= rd (bvashr rn (zero_ext 64 immshift))) + ) + (=> (and (= alu_op (ALUOp.Asr)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvashr (extract 31 0 rn) (zero_ext 32 immshift)))) + ) + + (=> (and (= alu_op (ALUOp.Lsl)) (= size (OperandSize.Size64))) + (= rd (bvshl rn (zero_ext 64 immshift))) + ) + (=> (and (= alu_op (ALUOp.Lsl)) (= size (OperandSize.Size32))) + (= rd (zero_ext 64 (bvshl (extract 31 0 rn) (zero_ext 32 immshift)))) + ) + + (=> (and (= alu_op (ALUOp.RotR)) (= size (OperandSize.Size32))) + (with + (t1 t3 t4 t5) + (and + (= t1 (= (extract 4 0 immshift) #b00000)) + (if + t1 + (= t3 (extract 31 0 rn)) + (and + (not (= (extract 4 0 immshift) #b00000)) + (= + t4 + (bvor + (bvlshr (extract 31 0 rn) (zero_ext 32 (extract 4 0 immshift))) + (bvshl + (extract 31 0 rn) + (zero_ext 32 (bvsub #x20 (zero_ext 8 (extract 4 0 immshift))))))))) + (= t5 (if t1 t3 t4)) + (= rd (zero_ext 64 t5)))) + ) + (=> (and (= alu_op (ALUOp.RotR)) (= size (OperandSize.Size64))) + (with + (t1 t3 t4 t5) + (and + (= t1 (= (extract 5 0 immshift) #b000000)) + (if + t1 + (= t3 rn) + (and + (not (= (extract 5 0 immshift) #b000000)) + (= + t4 + (bvor + (bvlshr rn (zero_ext 64 (extract 5 0 immshift))) + (bvshl rn (zero_ext 64 (bvsub #x40 (zero_ext 8 (extract 5 0 immshift))))))))) + (= t5 (if t1 t3 t4)) + (= rd t5)))) + ) + (require + (or + (and (= alu_op (ALUOp.Lsr)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Lsr)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.Asr)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Asr)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.Lsl)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.Lsl)) (= size (OperandSize.Size32))) + + (and (= alu_op (ALUOp.RotR)) (= size (OperandSize.Size64))) + (and (= alu_op (ALUOp.RotR)) (= size (OperandSize.Size32))) + ) + ) +) + +(spec (MInst.TrapIf kind trap_code) + (modifies exec_trap trap_cond) + (provide + ; Conditions under which this instruction traps. + (= trap_cond + (match kind + ((Zero r) (bv_is_zero! r)) + ((NotZero r) (not (bv_is_zero! r))) + ((Cond cc) (cond_holds! cc (:flags_in result))) + ) + ) + + ; If this instruction traps, set the global trap state. + (=> trap_cond exec_trap) + ) +) + +;; Registers. +(model Reg (type (bv))) + ;; An ALU operation. This can be paired with several instruction formats ;; below (see `Inst`) in any combination. (type ALUOp @@ -1010,6 +1297,31 @@ (SbcS) )) +(spec (ALUOp.AndS) + (provide + (= result (ALUOp.AndS)))) + +(spec (ALUOp.Orr) + (provide + (= result (ALUOp.Orr)))) + + +(spec (ALUOp.Sub) + (provide + (= result (ALUOp.Sub)))) + +(spec (ALUOp.Lsr) + (provide + (= result (ALUOp.Lsr)))) + +(spec (ALUOp.OrrNot) + (provide + (= result (ALUOp.OrrNot)))) + +(spec (ALUOp.Lsl) + (provide + (= result (ALUOp.Lsl)))) + ;; An ALU operation with three arguments. (type ALUOp3 (enum @@ -1029,25 +1341,117 @@ (MovN) )) +(spec (MoveWideOp.MovZ) + (provide + (= result (MoveWideOp.MovZ)))) + +(spec (MoveWideOp.MovN) + (provide + (= result (MoveWideOp.MovN)))) + (type UImm5 (primitive UImm5)) +(model UImm5 (type (bv 5))) + (type Imm12 (primitive Imm12)) +(model Imm12 + (type + (struct + (bits (bv 12)) + (shift12 Bool) + ) + ) +) + (type ImmLogic (primitive ImmLogic)) +(model ImmLogic (type (bv 64))) (type ImmShift (primitive ImmShift)) +(model ImmShift (type (bv 6))) + (type ShiftOpAndAmt (primitive ShiftOpAndAmt)) +(model ShiftOpAndAmt + (type + (struct + (op (named ALUOp)) + (amt (bv 8)) + ) + ) +) + (type MoveWideConst (primitive MoveWideConst)) +(model MoveWideConst + (type + (struct + (shift (bv 2)) + (bits (bv 16)) + ) + ) +) + (type NZCV (primitive NZCV)) +(model NZCV + (type + (struct + (N (bv 1)) + (Z (bv 1)) + (C (bv 1)) + (V (bv 1)) + ) + ) +) + +(macro (n_set nzcv) (= (:N nzcv) #b1)) +(macro (n_clear nzcv) (= (:N nzcv) #b0)) +(macro (z_set nzcv) (= (:Z nzcv) #b1)) +(macro (z_clear nzcv) (= (:Z nzcv) #b0)) +(macro (c_set nzcv) (= (:C nzcv) #b1)) +(macro (c_clear nzcv) (= (:C nzcv) #b0)) +(macro (v_set nzcv) (= (:V nzcv) #b1)) +(macro (v_clear nzcv) (= (:V nzcv) #b0)) + (type ASIMDFPModImm (primitive ASIMDFPModImm)) +(model ASIMDFPModImm (type + (struct + (imm (bv 8)) + (size (named ScalarSize)) + ) +)) + (type ASIMDMovModImm (primitive ASIMDMovModImm)) -(type SImm7Scaled (primitive SImm7Scaled)) +(model ASIMDMovModImm (type + (struct + (imm (bv 8)) + ; TODO: shift: u8, + ; TODO: is_64bit: bool, + ; TODO: shift_ones: bool, + ) +)) +(type SImm7Scaled (primitive SImm7Scaled)) +(model SImm7Scaled (type (bv))) (type BoxCallInfo (primitive BoxCallInfo)) +(model BoxCallInfo (type (bv))) (type BoxCallIndInfo (primitive BoxCallIndInfo)) +(model BoxCallIndInfo (type (bv))) (type BoxReturnCallInfo (primitive BoxReturnCallInfo)) -(type CondBrKind (primitive CondBrKind)) +(model BoxReturnCallInfo (type (bv))) (type BranchTarget (primitive BranchTarget)) +(model BranchTarget (type (bv))) (type BoxJTSequenceInfo (primitive BoxJTSequenceInfo)) (type CodeOffset (primitive CodeOffset)) +(model CodeOffset (type (bv))) (type VecMachLabel extern (enum)) +(model VecMachLabel (type !)) + +(type CondBrKind extern + (enum + (Zero (r Reg)) + (NotZero (r Reg)) + (Cond (cc Cond)) +)) + +; Expect that branched on registers will be general-purpose registers. +(instantiate CondBrKind.Zero ((args (bv 64)) (ret (named CondBrKind)))) +(instantiate CondBrKind.NotZero ((args (bv 64)) (ret (named CondBrKind)))) (type ExtendOp extern (enum @@ -1076,8 +1480,11 @@ )) (type MemLabel extern (enum)) +(model MemLabel (type !)) (type SImm9 extern (enum)) +(model SImm9 (type (bv 9))) (type UImm12Scaled extern (enum)) +(model UImm12Scaled (type (bv 12))) ;; An addressing mode specified for a load/store operation. (type AMode @@ -1109,15 +1516,13 @@ ;; Register plus register offset, scaled by type's size. (RegScaled (rn Reg) - (rm Reg) - (ty Type)) + (rm Reg)) ;; Register plus register offset, scaled by type's size, with index ;; sign- or zero-extended first. (RegScaledExtended (rn Reg) (rm Reg) - (ty Type) (extendop ExtendOp)) ;; Register plus register offset, with index sign- or zero-extended @@ -1147,39 +1552,60 @@ ;; offsets with multiple instructions as necessary during code emission. (RegOffset (rn Reg) - (off i64) - (ty Type)) + (off i64)) ;; Offset from the stack pointer. (SPOffset - (off i64) - (ty Type)) + (off i64)) ;; Offset from the frame pointer. (FPOffset - (off i64) - (ty Type)) + (off i64)) ;; A reference to a constant which is placed outside of the function's ;; body, typically at the end. (Const (addr VCodeConstant)) - ;; Offset from the "nominal stack pointer", which is where the real SP is - ;; just after stack and spill slots are allocated in the function prologue. + ;; Offset from the beginning of the argument area to the argument + ;; referenced. This can only be determined when the function has been + ;; processed fully, as the size of the argument area after the prologue + ;; is only known once all return_call instructions in the function body + ;; have been processed. + (IncomingArg + (off i64)) + + ;; Offset into the slot area of the stack, which lies just above the + ;; outgoing argument area that's setup by the function prologue. ;; At emission time, this is converted to `SPOffset` with a fixup added to ;; the offset constant. The fixup is a running value that is tracked as ;; emission iterates through instructions in linear order, and can be ;; adjusted up and down with [Inst::VirtualSPOffsetAdj]. ;; ;; The standard ABI is in charge of handling this (by emitting the - ;; adjustment meta-instructions). It maintains the invariant that "nominal - ;; SP" is where the actual SP is after the function prologue and before - ;; clobber pushes. See the diagram in the documentation for - ;; [crate::isa::aarch64::abi](the ABI module) for more details. - (NominalSPOffset - (off i64) - (ty Type)))) + ;; adjustment meta-instructions). See the diagram in the documentation + ;; for [crate::isa::aarch64::abi](the ABI module) for more details. + (SlotOffset + (off i64)))) + +; TODO: handle loads from function's constant pool +(attr AMode.Const (tag amode_const)) + +; Type instantiations for register addressing modes. +; +; Register type is an unspecified bit-vector to support both general-purpose and +; vector register types. When the `Reg` type is used as an addressing mode we +; face two problems. Firstly, acted on in a way that type inference cannot +; always deduce the width. For example, some addressing registers only have bits +; extracted. Secondly, some variants such as `RegOffset` are only ever used +; from Rust. Type inference has no information to use to deduce the width of +; `Reg` fields in these cases. For these reasons we specify explicitly that +; addressing modes are general purpose registers, therefore 64-bit. +(instantiate AMode.RegReg ((args (bv 64) (bv 64)) (ret (named AMode)))) +(instantiate AMode.RegScaled ((args (bv 64) (bv 64)) (ret (named AMode)))) +(instantiate AMode.RegScaledExtended ((args (bv 64) (bv 64) (named ExtendOp)) (ret (named AMode)))) +(instantiate AMode.RegExtended ((args (bv 64) (bv 64) (named ExtendOp)) (ret (named AMode)))) +(instantiate AMode.RegOffset ((args (bv 64) (named i64)) (ret (named AMode)))) ;; A memory argument to a load/store-pair. (type PairAMode (enum @@ -1196,16 +1622,43 @@ )) (type FPUOpRI extern (enum)) +(model FPUOpRI (type + (struct + (size (bv 8)) + (amount (bv 8)) + (lane_size_in_bits (bv 8)) + ) +)) (type FPUOpRIMod extern (enum)) +(model FPUOpRIMod (type + (struct + (size (bv 8)) + (amount (bv 8)) + (lane_size_in_bits (bv 8)) + ) +)) (type OperandSize extern (enum Size32 Size64)) +(type TestBitAndBranchKind (enum (Z) (NZ))) + ;; Helper for calculating the `OperandSize` corresponding to a type (decl operand_size (Type) OperandSize) -(rule 1 (operand_size (fits_in_32 _ty)) (OperandSize.Size32)) -(rule (operand_size (fits_in_64 _ty)) (OperandSize.Size64)) +(spec (operand_size ty) + (provide + (= result (if (<= (:bits ty) 32) (OperandSize.Size32) (OperandSize.Size64)))) + (require + (or (= (:bits ty) 8) (= (:bits ty) 16) (= (:bits ty) 32) (= (:bits ty) 64)))) +(attr rule operand_size_32 (veri priority)) +(rule operand_size_32 1 (operand_size (fits_in_32 _ty)) (OperandSize.Size32)) +(rule operand_size_64 (operand_size (fits_in_64 _ty)) (OperandSize.Size64)) + +(decl diff_from_32 (Type) u8) +(attr diff_from_32 (veri chain)) +(rule (diff_from_32 $I8) 24) +(rule (diff_from_32 $I16) 16) (type ScalarSize extern (enum Size8 @@ -1216,17 +1669,29 @@ ;; Helper for calculating the `ScalarSize` corresponding to a type (decl scalar_size (Type) ScalarSize) - -(rule (scalar_size $I8) (ScalarSize.Size8)) -(rule (scalar_size $I16) (ScalarSize.Size16)) -(rule (scalar_size $I32) (ScalarSize.Size32)) -(rule (scalar_size $I64) (ScalarSize.Size64)) -(rule (scalar_size $I128) (ScalarSize.Size128)) - -(rule (scalar_size $F32) (ScalarSize.Size32)) -(rule (scalar_size $F64) (ScalarSize.Size64)) +(spec (scalar_size ty) + (provide (= result + (switch (:bits ty) + (8 (ScalarSize.Size8)) + (16 (ScalarSize.Size16)) + (32 (ScalarSize.Size32)) + (64 (ScalarSize.Size64)) + (128 (ScalarSize.Size128)) + ) + )) +) + +(rule scalar_size_i8 (scalar_size $I8) (ScalarSize.Size8)) +(rule scalar_size_i16 (scalar_size $I16) (ScalarSize.Size16)) +(rule scalar_size_i32 (scalar_size $I32) (ScalarSize.Size32)) +(rule scalar_size_i64 (scalar_size $I64) (ScalarSize.Size64)) +(rule scalar_size_i128 (scalar_size $I128) (ScalarSize.Size128)) + +(rule scalar_size_f32 (scalar_size $F32) (ScalarSize.Size32)) +(rule scalar_size_f64 (scalar_size $F64) (ScalarSize.Size64)) ;; Helper for calculating the `ScalarSize` lane type from vector type +(attr lane_size (tag vector)) (decl lane_size (Type) ScalarSize) (rule 1 (lane_size (multi_lane 8 _)) (ScalarSize.Size8)) (rule 1 (lane_size (multi_lane 16 _)) (ScalarSize.Size16)) @@ -1267,6 +1732,46 @@ (Nv) )) +(spec (Cond.Ne) + (provide + ( = result (Cond.Ne)))) + +(macro (cond_holds cc nzcv) + (match cc + ; EQ Equal. Z==1 + ((Eq) (z_set! nzcv)) + ; NE Not equal. Z==0 + ((Ne) (z_clear! nzcv)) + ; HS Unsigned higher or same (or carry set). C==1 + ((Hs) (c_set! nzcv)) + ; LO Unsigned lower (or carry clear). C==0 + ((Lo) (c_clear! nzcv)) + ; MI Negative. The mnemonic stands for "minus". N==1 + ((Mi) (n_set! nzcv)) + ; PL Positive or zero. The mnemonic stands for "plus". N==0 + ((Pl) (n_clear! nzcv)) + ; VS Signed overflow. The mnemonic stands for "V set". V==1 + ((Vs) (v_set! nzcv)) + ; VC No signed overflow. The mnemonic stands for "V clear". V==0 + ((Vc) (v_clear! nzcv)) + ; HI Unsigned higher. (C==1) && (Z==0) + ((Hi) (and (c_set! nzcv) (z_clear! nzcv))) + ; LS Unsigned lower or same. (C==0) || (Z==1) + ((Ls) (or (c_clear! nzcv) (z_set! nzcv))) + ; GE Signed greater than or equal. N==V + ((Ge) (= (:N nzcv) (:V nzcv))) + ; LT Signed less than. N!=V + ((Lt) (not (= (:N nzcv) (:V nzcv)))) + ; GT Signed greater than. (Z==0) && (N==V) + ((Gt) (and (z_clear! nzcv) (= (:N nzcv) (:V nzcv)))) + ; LE Signed less than or equal. (Z==1) || (N!=V) + ((Le) (or (z_set! nzcv) (not (= (:N nzcv) (:V nzcv))))) + ; AL (or omitted) Always executed. None tested. + ((Al) true) + ((Nv) false) + ) +) + (type VectorSize extern (enum (Size8x8) @@ -1279,6 +1784,7 @@ )) ;; Helper for calculating the `VectorSize` corresponding to a type +(attr vector_size (tag vector)) (decl vector_size (Type) VectorSize) (rule 1 (vector_size (multi_lane 8 8)) (VectorSize.Size8x8)) (rule 1 (vector_size (multi_lane 8 16)) (VectorSize.Size8x16)) @@ -1319,7 +1825,14 @@ ;; A floating-point unit (FPU) operation with three args. (type FPUOp3 (enum + ;; Multiply-add (MAdd) + ;; Multiply-sub + (MSub) + ;; Negated fused Multiply-add + (NMAdd) + ;; Negated fused Multiply-sub + (NMSub) )) ;; A conversion from an FP to an integer value. @@ -1686,35 +2199,171 @@ (decl use_lse () Inst) (extern extractor use_lse use_lse) -;; Extractor helpers for various immmediate constants ;;;;;;;;;;;;;;;;;;;;;;;;;; +(decl pure use_fp16 () bool) +(extern constructor use_fp16 use_fp16) +(spec (use_fp16) (provide (not result))) + +;; Extractor helpers for various immediate constants ;;;;;;;;;;;;;;;;;;;;;;;;;; + +; equals_move_wide_value expresses the property that the value n equals the +; computed value of the move wide immediate. +(macro (equals_move_wide_value n mov_wide_const) + (and + ; n equals the computed value + (= n + ; bits << (16 * shift) + (bvshl + (zero_ext 64 (:bits mov_wide_const)) + (bvmul (int2bv 64 16) (zero_ext 64 (:shift mov_wide_const))) + ) + ) + + ; The zero case can be expressed in multiple ways. Assert that it + ; must be the zero shift value, matching the Rust implementation. + (=> (bv_is_zero! n) (= (:shift mov_wide_const) #b00)) + ) +) (decl pure partial move_wide_const_from_u64 (Type u64) MoveWideConst) (extern constructor move_wide_const_from_u64 move_wide_const_from_u64) +(spec (move_wide_const_from_u64 ty n) + (provide + (let + ( + (nlow (bvand n (low_bits_mask! 64 (:bits ty)))) + ) + (equals_move_wide_value! nlow result) + ) + ) + (match + (let + ( + (nlow (bvand n (low_bits_mask! 64 (:bits ty)))) + ) + (or + (= nlow (bvand nlow #x000000000000ffff)) + (= nlow (bvand nlow #x00000000ffff0000)) + (= nlow (bvand nlow #x0000ffff00000000)) + (= nlow (bvand nlow #xffff000000000000)) + ) + ) + ) +) (decl pure partial move_wide_const_from_inverted_u64 (Type u64) MoveWideConst) (extern constructor move_wide_const_from_inverted_u64 move_wide_const_from_inverted_u64) +(spec (move_wide_const_from_inverted_u64 ty n_inverted) + (provide + (let + ( + (n (bvnot n_inverted)) + (nlow (bvand n (low_bits_mask! 64 (:bits ty)))) + ) + (equals_move_wide_value! nlow result) + ) + ) + (match + (let + ( + (n (bvnot n_inverted)) + (nlow (bvand n (low_bits_mask! 64 (:bits ty)))) + ) + (or + (= nlow (bvand nlow #x000000000000ffff)) + (= nlow (bvand nlow #x00000000ffff0000)) + (= nlow (bvand nlow #x0000ffff00000000)) + (= nlow (bvand nlow #xffff000000000000)) + ) + ) + ) +) (decl pure partial imm_logic_from_u64 (Type u64) ImmLogic) (extern constructor imm_logic_from_u64 imm_logic_from_u64) +; REVIEW(mbm): actually specify the requirements of logical immediates? +(spec (imm_logic_from_u64 ty n) + (provide (= result n)) + (match (or (= (:bits ty) 32) (= (:bits ty) 64))) +) + +(decl pure partial imm_size_from_type (Type) u16) +(extern constructor imm_size_from_type imm_size_from_type) +(spec (imm_size_from_type ty) + (match (or (= (:bits ty) 32) (= (:bits ty) 64))) + (provide (= result (int2bv 16 (:bits ty)))) +) (decl pure partial imm_logic_from_imm64 (Type Imm64) ImmLogic) (extern constructor imm_logic_from_imm64 imm_logic_from_imm64) +; REVIEW(mbm): actually specify the requirements of logical immediates? +(spec (imm_logic_from_imm64 ty imm64) + (provide (= result imm64)) + (match (or (= (:bits ty) 32) (= (:bits ty) 64)))) (decl pure partial imm_shift_from_imm64 (Type Imm64) ImmShift) (extern constructor imm_shift_from_imm64 imm_shift_from_imm64) +(spec (imm_shift_from_imm64 ty v) + (provide + (let + ((imm_value (bvand v (shift_mask! 64 (:bits ty))))) + (= result (extract 5 0 imm_value)) + ) + ) + (match + (let + ((imm_value (bvand v (shift_mask! 64 (:bits ty))))) + (bvult imm_value (int2bv 64 64)) + ) + ) +) (decl imm_shift_from_u8 (u8) ImmShift) (extern constructor imm_shift_from_u8 imm_shift_from_u8) +(spec (imm_shift_from_u8 n) + (provide (= result (extract 5 0 n))) + (require (bvult n #x40)) +) (decl imm12_from_u64 (Imm12) u64) (extern extractor imm12_from_u64 imm12_from_u64) +(spec (imm12_from_u64 imm12) + (provide + (= result + (zero_ext 64 + (if (:shift12 imm12) + (concat (:bits imm12) #x000) + (concat #x000 (:bits imm12)) + ) + ) + ) + ) + (match + (or + (= result (bvand result (zero_ext 64 #x000fff))) + (= result (bvand result (zero_ext 64 #xfff000))) + ) + ) +) (decl u8_into_uimm5 (u8) UImm5) (extern constructor u8_into_uimm5 u8_into_uimm5) +(spec (u8_into_uimm5 n) + (provide (= result (extract 4 0 n))) + (require (bvult n #x20)) +) (decl u8_into_imm12 (u8) Imm12) (extern constructor u8_into_imm12 u8_into_imm12) - +(spec (u8_into_imm12 n) + (provide + (not (:shift12 result)) + (= (:bits result) (zero_ext 12 n)) + ) +) + +(spec (u64_into_imm_logic ty a) + (provide (= result a)) + (require (or (= (:bits ty) 32) (= (:bits ty) 64)))) (decl u64_into_imm_logic (Type u64) ImmLogic) (extern constructor u64_into_imm_logic u64_into_imm_logic) @@ -1745,16 +2394,48 @@ ;; and the amount to shift by. (decl fpu_op_ri_ushr (u8 u8) FPUOpRI) (extern constructor fpu_op_ri_ushr fpu_op_ri_ushr) +(spec (fpu_op_ri_ushr ty_bits shift) + (provide + (= (:size result) ty_bits) + (= (:amount result) shift) + (= (:lane_size_in_bits result) ty_bits) + ) +) ;; Constructs an FPUOpRIMod.Sli* given the size in bits of the value (or lane) ;; and the amount to shift by. (decl fpu_op_ri_sli (u8 u8) FPUOpRIMod) (extern constructor fpu_op_ri_sli fpu_op_ri_sli) +(spec (fpu_op_ri_sli ty_bits shift) + (provide + (= (:size result) ty_bits) + (= (:amount result) shift) + (= (:lane_size_in_bits result) ty_bits) + ) +) (decl pure partial lshr_from_u64 (Type u64) ShiftOpAndAmt) (extern constructor lshr_from_u64 lshr_from_u64) (decl pure partial lshl_from_imm64 (Type Imm64) ShiftOpAndAmt) +(spec (lshl_from_imm64 ty imm) + (provide + (= (:op result) (ALUOp.Lsl)) + (= (:amt result) + ; imm & (ty - 1) + (bvand + (extract 7 0 imm) + (bvsub (int2bv 8 (:bits ty)) #x01) + ) + ) + ) + (match + ; imm < 64 + (bvult imm #x0000000000000040) + ; ty <= 255 + (<= (:bits ty) 255) + ) +) (extern constructor lshl_from_imm64 lshl_from_imm64) (decl pure partial lshl_from_u64 (Type u64) ShiftOpAndAmt) @@ -1765,6 +2446,13 @@ (decl integral_ty (Type) Type) (extern extractor integral_ty integral_ty) +(spec (integral_ty ty) + (provide (= result ty)) + (match (or + (= (:bits result) 8) + (= (:bits result) 16) + (= (:bits result) 32) + (= (:bits result) 64)))) (decl valid_atomic_transaction (Type) Type) (extern extractor valid_atomic_transaction valid_atomic_transaction) @@ -1776,45 +2464,165 @@ (extern constructor is_zero_uimm12 is_zero_uimm12) ;; Helper to go directly from a `Value`, when it's an `iconst`, to an `Imm12`. +(spec (imm12_from_value imm12) + (provide + (= + (zero_ext 128 result) + (zero_ext 128 + (if (:shift12 imm12) + (concat (:bits imm12) #x000) + (concat #x000 (:bits imm12)) + ) + ) + ) + ) + (match + (let + ( + (width 128) + (r (zero_ext width result)) + ) + (or + (= r (bvand r (zero_ext width #x000fff))) + (= r (bvand r (zero_ext width #xfff000))) + ) + ) + ) +) (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). (decl pure partial imm12_from_negated_value (Value) Imm12) -(rule - (imm12_from_negated_value (has_type ty (iconst n))) +(rule imm12_from_negated_value + (imm12_from_negated_value (has_type ty (iconst _ n))) (if-let (imm12_from_u64 imm) (i64_as_u64 (i64_neg (i64_sextend_imm64 ty n)))) imm) +(attr imm12_from_negated_value (veri chain)) ;; Helper type to represent a value and an extend operation fused together. (type ExtendedValue extern (enum)) +(model ExtendedValue (type + (struct + (val (named Value)) + (extend (named ExtendOp)) + ) +)) + +(macro (extend_op_src_width extend) + (match extend + ((UXTB) 8) + ((UXTH) 16) + ((UXTW) 32) + ((UXTX) 64) + ((SXTB) 8) + ((SXTH) 16) + ((SXTW) 32) + ((SXTX) 64) + ) +) + +(macro (extend_op_signed extend) + (match extend + ((UXTB) false) + ((UXTH) false) + ((UXTW) false) + ((UXTX) false) + ((SXTB) true) + ((SXTH) true) + ((SXTW) true) + ((SXTX) true) + ) +) + +(macro (is_zero_ext dst src) + (and + (>= (widthof dst) (widthof src)) + (= dst (conv_to (widthof dst) (zero_ext 64 src))) + ) +) + +(macro (is_sign_ext dst src) + (and + (>= (widthof dst) (widthof src)) + (= dst (conv_to (widthof dst) (sign_ext 64 src))) + ) +) + +(macro (is_ext dst src) (or (is_zero_ext! dst src) (is_sign_ext! dst src))) + (decl extended_value_from_value (ExtendedValue) Value) (extern extractor extended_value_from_value extended_value_from_value) +(spec (extended_value_from_value x) + ; Match when the result can be represented as an extend. + (match + (or + (with (b) (is_ext! result (as b (bv 8)))) + (with (h) (is_ext! result (as h (bv 16)))) + (with (w) (is_ext! result (as w (bv 32)))) + (with (x) (is_ext! result (as x (bv 64)))) + ) + ) + + ; Determine the ExtendedValue parameters. + (provide + ; Width of the input. + (= (widthof (:val x)) (extend_op_src_width! (:extend x))) + + ; Result equals the extension. + (if (extend_op_signed! (:extend x)) + (is_sign_ext! result (:val x)) + (is_zero_ext! result (:val x)) + ) + ) +) +(instantiate extended_value_from_value + ((args (struct (val (bv 8)) (extend (named ExtendOp)))) (ret (named Value))) + ((args (struct (val (bv 16)) (extend (named ExtendOp)))) (ret (named Value))) + ((args (struct (val (bv 32)) (extend (named ExtendOp)))) (ret (named Value))) + ((args (struct (val (bv 64)) (extend (named ExtendOp)))) (ret (named Value))) +) ;; Constructors used to poke at the fields of an `ExtendedValue`. (decl put_extended_in_reg (ExtendedValue) Reg) (extern constructor put_extended_in_reg put_extended_in_reg) +(spec (put_extended_in_reg x) + (provide (= result (conv_to (widthof result) (:val x)))) +) + (decl get_extended_op (ExtendedValue) ExtendOp) (extern constructor get_extended_op get_extended_op) +(spec (get_extended_op x) (provide (= result (:extend x)))) (decl nzcv (bool bool bool bool) NZCV) (extern constructor nzcv nzcv) +(spec (nzcv n z c v) + (provide + (= (:N result) (bool2bit! n)) + (= (:Z result) (bool2bit! z)) + (= (:C result) (bool2bit! c)) + (= (:V result) (bool2bit! v)) + ) +) (decl cond_br_zero (Reg) CondBrKind) (extern constructor cond_br_zero cond_br_zero) +(spec (cond_br_zero r) (provide (= result (CondBrKind.Zero r)))) (decl cond_br_not_zero (Reg) CondBrKind) (extern constructor cond_br_not_zero cond_br_not_zero) (decl cond_br_cond (Cond) CondBrKind) (extern constructor cond_br_cond cond_br_cond) +(spec (cond_br_cond cc) (provide (= result (CondBrKind.Cond cc)))) ;; Instruction creation helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helper for creating the zero register. +(spec (zero_reg) (provide (= (zero_ext (widthof result) #b0) result))) (decl zero_reg () Reg) (extern constructor zero_reg zero_reg) @@ -1828,6 +2636,7 @@ (extern constructor writable_link_reg writable_link_reg) (decl writable_zero_reg () WritableReg) +(spec (writable_zero_reg) (provide true)) ; zero register is just a write sink (extern constructor writable_zero_reg writable_zero_reg) (decl value_regs_zero () ValueRegs) @@ -1874,7 +2683,7 @@ ;; Helper for emitting `MInst.AluRRR` instructions. (decl alu_rrr (ALUOp Type Reg Reg) Reg) -(rule (alu_rrr op ty src1 src2) +(rule alu_rrr_to_emit (alu_rrr op ty src1 src2) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.AluRRR op (operand_size ty) dst src1 src2)))) dst)) @@ -1978,7 +2787,7 @@ ;; Helper for emitting `MInst.AluRRImm12` instructions. (decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg) -(rule (alu_rr_imm12 op ty src imm) +(rule alu_rr_imm12_to_emit (alu_rr_imm12 op ty src imm) (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.AluRRImm12 op (operand_size ty) dst src imm)))) dst)) @@ -1998,10 +2807,12 @@ (ProducesFlags.ProducesFlagsSideEffect (MInst.AluRRRShift (ALUOp.SubS) size (writable_zero_reg) src1 src2 shift))) +(attr cmp_rr_shift (veri chain)) ;; Helper for emitting `cmp` instructions, setting flags, with an arithmetic right-shifted ;; second operand register. (decl cmp_rr_shift_asr (OperandSize Reg Reg u64) ProducesFlags) +(attr cmp_rr_shift_asr (veri chain)) (rule (cmp_rr_shift_asr size src1 src2 shift_amount) (if-let shift (ashr_from_u64 $I64 shift_amount)) (ProducesFlags.ProducesFlagsSideEffect @@ -2203,9 +3014,20 @@ (_ Unit (emit (MInst.VecRRLong op dst src high_half)))) dst)) -;; Helper for emitting `MInst.FpuCSel32` / `MInst.FpuCSel64` +;; Helper for emitting `MInst.FpuCSel16` / `MInst.FpuCSel32` / `MInst.FpuCSel64` ;; instructions. (decl fpu_csel (Type Cond Reg Reg) ConsumesFlags) +(rule (fpu_csel $F16 cond if_true if_false) + (fpu_csel $F32 cond if_true if_false)) + +(attr rule fpu_csel_f16 (tag narrowfloat)) +(rule fpu_csel_f16 1 (fpu_csel $F16 cond if_true if_false) + (if-let $true (use_fp16)) + (let ((dst WritableReg (temp_writable_reg $F16))) + (ConsumesFlags.ConsumesFlagsReturnsReg + (MInst.FpuCSel16 dst if_true if_false cond) + dst))) + (rule (fpu_csel $F32 cond if_true if_false) (let ((dst WritableReg (temp_writable_reg $F32))) (ConsumesFlags.ConsumesFlagsReturnsReg @@ -2246,10 +3068,17 @@ ;; Helper for emitting `MInst.MovToFpu` instructions. (decl mov_to_fpu (Reg ScalarSize) Reg) -(rule (mov_to_fpu x size) +(attr mov_to_fpu (veri chain)) +(rule mov_to_fpu_base_case (mov_to_fpu x size) (let ((dst WritableReg (temp_writable_reg $I8X16)) (_ Unit (emit (MInst.MovToFpu dst x size)))) dst)) +(attr rule mov_to_fpu_16 (tag narrowfloat)) +(rule mov_to_fpu_16 1 (mov_to_fpu x (ScalarSize.Size16)) + (if-let $false (use_fp16)) + (let ((dst WritableReg (temp_writable_reg $I8X16)) + (_ Unit (emit (MInst.MovToFpu dst x (ScalarSize.Size32))))) + dst)) ;; Helper for emitting `MInst.FpuMoveFPImm` instructions. (decl fpu_move_fp_imm (ASIMDFPModImm ScalarSize) Reg) @@ -2407,10 +3236,10 @@ ;; Helpers for generating `add` instructions. (decl add (Type Reg Reg) Reg) -(rule (add ty x y) (alu_rrr (ALUOp.Add) ty x y)) +(rule add_to_alu_rrr (add ty x y) (alu_rrr (ALUOp.Add) ty x y)) (decl add_imm (Type Reg Imm12) Reg) -(rule (add_imm ty x y) (alu_rr_imm12 (ALUOp.Add) ty x y)) +(rule add_imm_to_alu_rr_imm12 (add_imm ty x y) (alu_rr_imm12 (ALUOp.Add) ty x y)) (decl add_extend (Type Reg ExtendedValue) Reg) (rule (add_extend ty x y) (alu_rr_extend_reg (ALUOp.Add) ty x y)) @@ -2704,6 +3533,9 @@ (rule (and_vec x y size) (vec_rrr (VecALUOp.And) x y size)) ;; Helpers for generating `eor` instructions. +(decl eor (Type Reg Reg) Reg) +(rule (eor ty x y) (alu_rrr (ALUOp.Eor) ty x y)) + (decl eor_vec (Reg Reg VectorSize) Reg) (rule (eor_vec x y size) (vec_rrr (VecALUOp.Eor) x y size)) @@ -2736,7 +3568,6 @@ (rule (sshr_vec_imm x amt size) (vec_shift_imm (VecShiftImmOp.Sshr) amt x size)) ;; Helpers for generating `rotr` instructions. - (decl a64_rotr (Type Reg Reg) Reg) (rule (a64_rotr ty x y) (alu_rrr (ALUOp.RotR) ty x y)) @@ -2828,6 +3659,11 @@ (let ((dst WritableReg (temp_writable_reg $I64)) (_ Unit (emit (MInst.ULoad64 dst amode flags)))) dst)) +(decl aarch64_fpuload16 (AMode MemFlags) Reg) +(rule (aarch64_fpuload16 amode flags) + (let ((dst WritableReg (temp_writable_reg $F64)) + (_ Unit (emit (MInst.FpuLoad16 dst amode flags)))) + dst)) (decl aarch64_fpuload32 (AMode MemFlags) Reg) (rule (aarch64_fpuload32 amode flags) (let ((dst WritableReg (temp_writable_reg $F64)) @@ -2864,6 +3700,9 @@ (decl aarch64_store64 (AMode MemFlags Reg) SideEffectNoResult) (rule (aarch64_store64 amode flags val) (SideEffectNoResult.Inst (MInst.Store64 val amode flags))) +(decl aarch64_fpustore16 (AMode MemFlags Reg) SideEffectNoResult) +(rule (aarch64_fpustore16 amode flags val) + (SideEffectNoResult.Inst (MInst.FpuStore16 val amode flags))) (decl aarch64_fpustore32 (AMode MemFlags Reg) SideEffectNoResult) (rule (aarch64_fpustore32 amode flags val) (SideEffectNoResult.Inst (MInst.FpuStore32 val amode flags))) @@ -2894,6 +3733,10 @@ (Sign) (Zero))) +(spec (ImmExtend.Zero) + (provide + (= result (ImmExtend.Zero)))) + ;; Arguments: ;; * Immediate type ;; * Way to extend the immediate value to the full width of the destination @@ -2905,64 +3748,90 @@ ;; all bits in the destination register in a defined state, i.e. smaller types ;; such as `I8` are either sign- or zero-extended. (decl imm (Type ImmExtend u64) Reg) +(attr imm (veri chain)) ;; Move wide immediate instructions; to simplify, we only match when we ;; are zero-extending the value. -(rule 3 (imm (integral_ty ty) (ImmExtend.Zero) k) +(rule imm_move_wide + 3 (imm (integral_ty ty) (ImmExtend.Zero) k) (if-let n (move_wide_const_from_u64 ty k)) - (movz n (operand_size ty))) -(rule 2 (imm (integral_ty (ty_32_or_64 ty)) (ImmExtend.Zero) k) + (add_range_fact + (movz n (operand_size ty)) + 64 k k)) +(rule imm_move_wide_inverted + 2 (imm (integral_ty (ty_32_or_64 ty)) (ImmExtend.Zero) k) (if-let n (move_wide_const_from_inverted_u64 ty k)) - (movn n (operand_size ty))) + (add_range_fact + (movn n (operand_size ty)) + 64 k k)) ;; 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) +(rule imm_zero_extend_imm_logic + 1 (imm (integral_ty ty) (ImmExtend.Zero) k) (if-let n (imm_logic_from_u64 ty k)) - (orr_imm ty (zero_reg) n)) + (if-let m (imm_size_from_type ty)) + (add_range_fact + (orr_imm ty (zero_reg) n) + m k k)) (decl load_constant64_full (Type ImmExtend u64) Reg) (extern constructor load_constant64_full load_constant64_full) +(spec (load_constant64_full ty extend value) + (provide + (= result (match extend + ((Sign) (sign_ext 64 (conv_to (:bits ty) value))) + ((Zero) (zero_ext 64 (conv_to (:bits ty) value))) + )) + ) +) ;; Fallback for integral 64-bit constants -(rule (imm (integral_ty ty) extend n) +(rule imm_load_constant64_full (imm (integral_ty ty) extend n) (load_constant64_full ty extend n)) ;; Sign extension helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Place a `Value` into a register, sign extending it to 32-bits +(attr put_in_reg_sext32 (veri chain)) (decl put_in_reg_sext32 (Value) Reg) -(rule -1 (put_in_reg_sext32 val @ (value_type (fits_in_32 ty))) +(attr put_in_reg_sext32 (veri chain)) +(rule put_in_reg_sext32_extend + -1 (put_in_reg_sext32 val @ (value_type (fits_in_32 ty))) (extend val $true (ty_bits ty) 32)) ;; 32/64-bit passthrough. -(rule (put_in_reg_sext32 val @ (value_type $I32)) val) -(rule (put_in_reg_sext32 val @ (value_type $I64)) val) +(rule put_in_reg_sext32_passthrough32 (put_in_reg_sext32 val @ (value_type $I32)) val) +(rule put_in_reg_sext32_passthrough64 (put_in_reg_sext32 val @ (value_type $I64)) val) ;; Place a `Value` into a register, zero extending it to 32-bits (decl put_in_reg_zext32 (Value) Reg) -(rule -1 (put_in_reg_zext32 val @ (value_type (fits_in_32 ty))) +(attr put_in_reg_zext32 (veri chain)) +(rule put_in_reg_zext32_extend + -1 (put_in_reg_zext32 val @ (value_type (fits_in_32 ty))) (extend val $false (ty_bits ty) 32)) ;; 32/64-bit passthrough. -(rule (put_in_reg_zext32 val @ (value_type $I32)) val) -(rule (put_in_reg_zext32 val @ (value_type $I64)) val) +(rule put_in_reg_zext32_passthrough32 (put_in_reg_zext32 val @ (value_type $I32)) val) +(rule put_in_reg_zext32_passthrough64 (put_in_reg_zext32 val @ (value_type $I64)) val) ;; Place a `Value` into a register, sign extending it to 64-bits (decl put_in_reg_sext64 (Value) Reg) -(rule 1 (put_in_reg_sext64 val @ (value_type (fits_in_32 ty))) +(attr put_in_reg_sext64 (veri chain)) +(rule put_in_reg_sext64_extend 1 (put_in_reg_sext64 val @ (value_type (fits_in_32 ty))) (extend val $true (ty_bits ty) 64)) ;; 64-bit passthrough. -(rule (put_in_reg_sext64 val @ (value_type $I64)) val) +(rule put_in_reg_sext64_passthrough (put_in_reg_sext64 val @ (value_type $I64)) val) ;; Place a `Value` into a register, zero extending it to 64-bits (decl put_in_reg_zext64 (Value) Reg) -(rule 1 (put_in_reg_zext64 val @ (value_type (fits_in_32 ty))) +(attr put_in_reg_zext64 (veri chain)) +(rule put_in_reg_zext64_extend 1 (put_in_reg_zext64 val @ (value_type (fits_in_32 ty))) (extend val $false (ty_bits ty) 64)) ;; 64-bit passthrough. -(rule (put_in_reg_zext64 val @ (value_type $I64)) val) +(rule put_in_reg_zext64_passthrough (put_in_reg_zext64 val @ (value_type $I64)) val) ;; Misc instruction helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2972,29 +3841,58 @@ reg)) (decl size_from_ty (Type) OperandSize) -(rule 1 (size_from_ty (fits_in_32 _ty)) (OperandSize.Size32)) -(rule (size_from_ty $I64) (OperandSize.Size64)) +(spec (size_from_ty ty) + (provide + (= result (if (<= (:bits ty) 32) (OperandSize.Size32) (OperandSize.Size64)))) + (require + (or + (= (:bits ty) 8) + (= (:bits ty) 16) + (= (:bits ty) 32) + (= (:bits ty) 64)))) +(rule size_from_ty_32 1 (size_from_ty (fits_in_32 _ty)) (OperandSize.Size32)) +(rule size_from_ty_64 (size_from_ty $I64) (OperandSize.Size64)) ;; Check for signed overflow. The only case is min_value / -1. ;; The following checks must be done in 32-bit or 64-bit, depending ;; on the input type. -(decl trap_if_div_overflow (Type Reg Reg) Reg) -(rule (trap_if_div_overflow ty x y) +(decl trap_if_div_overflow (Type Reg Reg Reg) Reg) +(attr trap_if_div_overflow (veri chain)) +(rule (trap_if_div_overflow ty xcheck x y) (let ( ;; Check RHS is -1. - (_ Unit (emit (MInst.AluRRImm12 (ALUOp.AddS) (operand_size ty) (writable_zero_reg) y (u8_into_imm12 1)))) + (check_rhs MInst (MInst.AluRRImm12 (ALUOp.AddS) (operand_size ty) (writable_zero_reg) y (u8_into_imm12 1))) ;; Check LHS is min_value, by subtracting 1 and branching if ;; there is overflow. - (_ Unit (emit (MInst.CCmpImm (size_from_ty ty) - x - (u8_into_uimm5 1) - (nzcv $false $false $false $false) - (Cond.Eq)))) - (_ Unit (emit (MInst.TrapIf (cond_br_cond (Cond.Vs)) - (trap_code_integer_overflow)))) + (check_lhs MInst (MInst.CCmpImm (size_from_ty ty) + xcheck + (u8_into_uimm5 1) + (nzcv $false $false $false $false) + (Cond.Eq))) + (trap_if MInst (MInst.TrapIf (cond_br_cond (Cond.Vs)) + (trap_code_integer_overflow))) ) - x)) + (with_flags_reg + (ProducesFlags.ProducesFlagsTwiceSideEffect check_rhs check_lhs) + (ConsumesFlags.ConsumesFlagsReturnsReg trap_if x) + ))) + +;; In the cases narrower than a register width, subtracting 1 from the +;; min_value will not cause overflow (e.g., I8's min_value of -128 stored in +;; a 32-bit register produces -129 with no overflow). However, if we left shift +;; x by (32 - ty), we then produce the 32-bit min_value for the respective min +;; values of I8 and I16. +;; E.g., I8's 0x00000080 left-shifted by 24 is 0x80000000, which overflows. +(decl intmin_check (Type Reg) Reg) +(attr intmin_check (veri chain)) + +(attr rule intmin_check_fits_in_16 (veri priority)) +(rule intmin_check_fits_in_16 (intmin_check (fits_in_16 ty) x) + (alu_rr_imm_shift (ALUOp.Lsl) ty x (imm_shift_from_u8 (diff_from_32 ty)))) + +;; In the I32 or I64 case, checking x itself against the min_value is fine. +(rule -1 (intmin_check ty x) x) ;; Check for unsigned overflow. (decl trap_if_overflow (ProducesFlags TrapCode) Reg) @@ -3005,47 +3903,51 @@ (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))) +(attr sink_atomic_load (veri chain)) ;; Helper for generating either an `AluRRR`, `AluRRRShift`, or `AluRRImmLogic` ;; instruction depending on the input. Note that this requires that the `ALUOp` ;; specified is commutative. (decl alu_rs_imm_logic_commutative (ALUOp Type Value Value) Reg) +(attr alu_rs_imm_logic_commutative (veri chain)) ;; Base case of operating on registers. (rule -1 (alu_rs_imm_logic_commutative op ty x y) (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)) ;; Same as `alu_rs_imm_logic_commutative` above, except that it doesn't require ;; that the operation is commutative. (decl alu_rs_imm_logic (ALUOp Type Value Value) Reg) +(attr alu_rs_imm_logic (veri chain)) (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)) + ;; Helper for generating i128 bitops which simply do the same operation to the ;; hi/lo registers. ;; @@ -3063,6 +3965,7 @@ (value_regs (alu_rrr op ty x_lo y_lo) (alu_rrr op ty x_hi y_hi)))) +(attr rule i128_alu_bitop (tag i128)) ;; Helper for emitting `MInst.VecLoadReplicate` instructions. (decl ld1r (Reg VectorSize MemFlags) Reg) @@ -3093,16 +3996,21 @@ ;; mid-end optimizations that fold constants into load/store immediate offsets ;; instead, but for now each backend needs to do this. (decl amode (Type Value i32) AMode) -(rule 0 (amode ty val offset) +(attr amode (veri chain)) +(rule amode_base_case + 0 (amode ty val offset) (amode_no_more_iconst ty val offset)) -(rule 1 (amode ty (iadd x (iconst (simm32 y))) offset) +(rule amode_iadd_const_right + 1 (amode ty (iadd _ x (i32_from_iconst y)) offset) (if-let new_offset (s32_add_fallible y offset)) (amode_no_more_iconst ty x new_offset)) -(rule 2 (amode ty (iadd (iconst (simm32 x)) y) offset) +(rule amode_iadd_const_left + 2 (amode ty (iadd _ (i32_from_iconst x) y) offset) (if-let new_offset (s32_add_fallible x offset)) (amode_no_more_iconst ty y new_offset)) (decl amode_no_more_iconst (Type Value i32) AMode) +(attr amode_no_more_iconst (veri chain)) ;; Base case: move the `offset` into a register and add it to `val` via the ;; amode (rule 0 (amode_no_more_iconst ty val offset) @@ -3110,10 +4018,12 @@ ;; Optimize cases where the `offset` provided fits into a immediates of ;; various kinds of addressing modes. -(rule 1 (amode_no_more_iconst ty val offset) +(rule amode_no_more_iconst_unscaled + 1 (amode_no_more_iconst ty val offset) (if-let simm9 (simm9_from_i64 offset)) (AMode.Unscaled val simm9)) -(rule 2 (amode_no_more_iconst ty val offset) +(rule amode_no_more_iconst_unsigned_offset + 2 (amode_no_more_iconst ty val offset) (if-let uimm12 (uimm12_scaled_from_i64 offset ty)) (AMode.UnsignedOffset val uimm12)) @@ -3124,15 +4034,20 @@ ;; 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 amode_no_more_iconst_iadd + 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 amode_no_more_iconst_iadd_uextend_right + 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 amode_no_more_iconst_iadd_sextend_right + 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 amode_no_more_iconst_iadd_uextend_left + 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 amode_no_more_iconst_iadd_sextend_left + 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 @@ -3142,30 +4057,40 @@ ;; 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) +(rule amode_no_more_iconst_reg_scaled_right + 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_shl 1 n))) - (amode_reg_scaled (amode_add x offset) y ty)) -(rule 7 (amode_no_more_iconst ty (iadd (ishl y (iconst (u64_from_imm64 n))) x) offset) + (amode_reg_scaled (amode_add x offset) y)) +(rule amode_no_more_iconst_reg_scaled_left + 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_shl 1 n))) - (amode_reg_scaled (amode_add x offset) y ty)) - -(decl amode_reg_scaled (Reg Value Type) AMode) -(rule 0 (amode_reg_scaled base index ty) - (AMode.RegScaled base index ty)) -(rule 1 (amode_reg_scaled base (uextend index @ (value_type $I32)) ty) - (AMode.RegScaledExtended base index ty (ExtendOp.UXTW))) -(rule 2 (amode_reg_scaled base (sextend index @ (value_type $I32)) ty) - (AMode.RegScaledExtended base index ty (ExtendOp.SXTW))) + (amode_reg_scaled (amode_add x offset) y)) + +(decl amode_reg_scaled (Reg Value) AMode) +(attr amode_reg_scaled (veri chain)) +(rule amode_reg_scaled_base + 0 (amode_reg_scaled base index) + (AMode.RegScaled base index)) +(rule amode_reg_scaled_uextend + 1 (amode_reg_scaled base (uextend _ index @ (value_type $I32))) + (AMode.RegScaledExtended base index (ExtendOp.UXTW))) +(rule amode_reg_scaled_sextend + 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 ;; select an appropriate `add` instruction to use. (decl amode_add (Reg i32) Reg) -(rule 0 (amode_add x y) +(attr amode_add (veri chain)) +(rule amode_add_base + 0 (amode_add x y) (add $I64 x (imm $I64 (ImmExtend.Zero) (i64_as_u64 y)))) -(rule 1 (amode_add x y) +(rule amode_add_imm12 + 1 (amode_add x y) (if-let (imm12_from_u64 imm12) (i64_as_u64 y)) (add_imm $I64 x imm12)) -(rule 2 (amode_add x 0) x) +(rule amode_add_zero + 2 (amode_add x 0) x) ;; Creates a `PairAMode` for the `Value` provided plus the `i32` constant ;; offset provided. @@ -3186,13 +4111,40 @@ (decl pure partial uimm12_scaled_from_i64 (i64 Type) UImm12Scaled) (extern constructor uimm12_scaled_from_i64 uimm12_scaled_from_i64) +(spec (uimm12_scaled_from_i64 value ty) + (match + (let + ( + (scale (bits2bytes! (int2bv 64 (:bits ty)))) + (limit (bvmul (int2bv 64 4095) scale)) + ) + (and + (bvsge value (bvzero! 64)) + (bvsle value limit) + (bv_is_zero! (bvand value (bvsub scale (bvone! 64)))) + ) + ) + ) + (provide + (= result (extract 11 0 (bvudiv value (bits2bytes! (int2bv 64 (:bits ty)))))) + ) +) (decl pure partial simm9_from_i64 (i64) SImm9) (extern constructor simm9_from_i64 simm9_from_i64) - +(spec (simm9_from_i64 value) + (provide (= value (sign_ext 64 result))) + (match + ; value >= -256 + (bvsge value (bvneg (int2bv 64 256))) + ; value <= 255 + (bvsle value (int2bv 64 255)) + ) +) (decl sink_load_into_addr (Type Inst) Reg) -(rule (sink_load_into_addr ty x @ (load _ addr (offset32 offset))) +(attr sink_load_into_addr (veri chain)) +(rule (sink_load_into_addr ty x @ (load _ _ addr (offset32 offset))) (let ((_ Unit (sink_inst x))) (add_imm_to_addr addr (i64_as_u64 offset)))) @@ -3201,20 +4153,51 @@ (rule 1 (add_imm_to_addr val (imm12_from_u64 imm)) (add_imm $I64 val imm)) (rule 0 (add_imm_to_addr val offset) (add $I64 val (imm $I64 (ImmExtend.Zero) offset))) +;; Lower a constant f16. +;; +;; Note that we must make sure that all bits outside the lowest 16 are set to 0 +;; because this function is also used to load wider constants (that have zeros +;; in their most significant bits). +(decl constant_f16 (u16) Reg) +(rule 3 (constant_f16 n) + (if-let $false (use_fp16)) + (lower_constant_f32 n)) +(rule 0 (constant_f16 n) (lower_constant_f16 n)) + +(decl lower_constant_f16 (u16) Reg) +(attr lower_constant_f16 (veri chain)) +(rule 2 (lower_constant_f16 0) + (vec_dup_imm (asimd_mov_mod_imm_zero (ScalarSize.Size32)) + $false + (VectorSize.Size32x2))) +(rule lower_constant_f16_fpu_move_fp_imm 1 (lower_constant_f16 n) + (if-let imm (asimd_fp_mod_imm_from_u64 n (ScalarSize.Size16))) + (fpu_move_fp_imm imm (ScalarSize.Size16))) +(rule lower_constant_f16_mov_to_fpu (lower_constant_f16 n) + (mov_to_fpu (imm $I16 (ImmExtend.Zero) n) (ScalarSize.Size16))) + ;; Lower a constant f32. ;; ;; Note that we must make sure that all bits outside the lowest 32 are set to 0 ;; because this function is also used to load wider constants (that have zeros ;; in their most significant bits). (decl constant_f32 (u32) Reg) -(rule 2 (constant_f32 0) +(attr constant_f32 (veri chain)) +(rule constant_f32_lower_constant_f16 1 (constant_f32 (u32_as_u16 n)) + (if-let $true (use_fp16)) + (lower_constant_f16 n)) +(rule constant_f32_base_case 0 (constant_f32 n) (lower_constant_f32 n)) + +(decl lower_constant_f32 (u32) Reg) +(attr lower_constant_f32 (veri chain)) +(rule 3 (lower_constant_f32 0) (vec_dup_imm (asimd_mov_mod_imm_zero (ScalarSize.Size32)) $false (VectorSize.Size32x2))) -(rule 1 (constant_f32 n) +(rule lower_constant_f32_fpu_move_fp_imm 2 (lower_constant_f32 n) (if-let imm (asimd_fp_mod_imm_from_u64 n (ScalarSize.Size32))) (fpu_move_fp_imm imm (ScalarSize.Size32))) -(rule (constant_f32 n) +(rule (lower_constant_f32 n) (mov_to_fpu (imm $I32 (ImmExtend.Zero) n) (ScalarSize.Size32))) ;; Lower a constant f64. @@ -3225,6 +4208,7 @@ ;; TODO: Treat as half of a 128 bit vector and consider replicated patterns. ;; Scalar MOVI might also be an option. (decl constant_f64 (u64) Reg) +(attr constant_f64 (veri chain)) (rule 4 (constant_f64 0) (vec_dup_imm (asimd_mov_mod_imm_zero (ScalarSize.Size32)) $false @@ -3242,6 +4226,10 @@ ;; Tests whether the low 32 bits in the input are all zero. (decl u64_low32_bits_unset (u64) u64) (extern extractor u64_low32_bits_unset u64_low32_bits_unset) +(spec (u64_low32_bits_unset n) + (match (bv_is_zero! (bvand result #x00000000ffffffff))) + (provide (= n result)) +) ;; Lower a constant f128. (decl constant_f128 (u128) Reg) @@ -3313,11 +4301,46 @@ ;; TODO: Port lower_fp_condcode() to ISLE. (extern constructor fp_cond_code fp_cond_code) +;; Ported from lower_fp_condcode. Note nontrivial mapping for lt and lte. +(spec (fp_cond_code cc) + (provide + (= result + (match cc + ((Equal) (Cond.Eq)) + ((NotEqual) (Cond.Ne)) + ((GreaterThanOrEqual) (Cond.Ge)) + ((GreaterThan) (Cond.Gt)) + ((LessThanOrEqual) (Cond.Ls)) + ((LessThan) (Cond.Mi)) + ) + ) + ) +) + ;; Lower an integer cond code. (decl cond_code (IntCC) Cond) ;; TODO: Port lower_condcode() to ISLE. (extern constructor cond_code cond_code) +(spec (cond_code cc) + (provide + (= result + (match cc + ((Equal) (Cond.Eq)) + ((NotEqual) (Cond.Ne)) + ((SignedGreaterThanOrEqual) (Cond.Ge)) + ((SignedGreaterThan) (Cond.Gt)) + ((SignedLessThanOrEqual) (Cond.Le)) + ((SignedLessThan) (Cond.Lt)) + ((UnsignedGreaterThanOrEqual) (Cond.Hs)) + ((UnsignedGreaterThan) (Cond.Hi)) + ((UnsignedLessThanOrEqual) (Cond.Ls)) + ((UnsignedLessThan) (Cond.Lo)) + ) + ) + ) +) + ;; Invert a condition code. (decl invert_cond (Cond) Cond) ;; TODO: Port cond.invert() to ISLE. @@ -3473,7 +4496,7 @@ ;; the frame record that corresponds to the current subroutine on ;; the stack; the presence of the record is guaranteed by the ;; `preserve_frame_pointers` setting. - (addr AMode (AMode.FPOffset 8 $I64)) + (addr AMode (AMode.FPOffset 8)) (_ Unit (emit (MInst.ULoad64 dst addr (mem_flags_trusted))))) dst)) @@ -3483,7 +4506,7 @@ ;; the frame record. Furthermore, we can use LR as a scratch register ;; because the function will set it to the return address immediately ;; before returning. - (let ((addr AMode (AMode.FPOffset 8 $I64)) + (let ((addr AMode (AMode.FPOffset 8)) (lr WritableReg (writable_link_reg)) (_ Unit (emit (MInst.ULoad64 lr addr (mem_flags_trusted)))) (_ Unit (emit (MInst.Xpaclri)))) @@ -3494,20 +4517,22 @@ (decl max_shift (Type) u8) (rule (max_shift $F64) 63) (rule (max_shift $F32) 31) +(attr max_shift (veri chain)) ;; Helper for generating `fcopysign` instruction sequences. - (decl fcopy_sign (Reg Reg Type) Reg) (rule 1 (fcopy_sign x y (ty_scalar_float ty)) (let ((dst WritableReg (temp_writable_reg $F64)) (tmp Reg (fpu_rri (fpu_op_ri_ushr (ty_bits ty) (max_shift ty)) y)) (_ Unit (emit (MInst.FpuRRIMod (fpu_op_ri_sli (ty_bits ty) (max_shift ty)) dst x tmp)))) dst)) -(rule (fcopy_sign x y ty @ (multi_lane _ _)) +(rule fcopy_sign_vec (fcopy_sign x y ty @ (multi_lane _ _)) (let ((dst WritableReg (temp_writable_reg $I8X16)) (tmp Reg (ushr_vec_imm y (max_shift (lane_type ty)) (vector_size ty))) (_ Unit (emit (MInst.VecShiftImmMod (VecShiftImmModOp.Sli) dst x tmp (vector_size ty) (max_shift (lane_type ty)))))) dst)) +(attr fcopy_sign (veri chain)) +(attr rule fcopy_sign_vec (tag vector)) ;; Helpers for generating `MInst.FpuToInt` instructions. @@ -3524,6 +4549,7 @@ ;; Checks that the value is not less than the minimum bound, ;; accepting a boolean (whether the type is signed), input type, ;; output type, and registers containing the source and minimum bound. +(attr fpu_to_int_underflow_check (veri chain)) (decl fpu_to_int_underflow_check (bool Type Type Reg Reg) Reg) (rule (fpu_to_int_underflow_check $true $F32 (fits_in_16 out_ty) src min) (let ((r ValueRegs @@ -3533,7 +4559,8 @@ (trap_code_integer_overflow)) src)))) (value_regs_get r 0))) -(rule (fpu_to_int_underflow_check $true $F64 (fits_in_32 out_ty) src min) +(rule fpu_to_int_underflow_check_signed_f64_fits_in_32 + (fpu_to_int_underflow_check $true $F64 (fits_in_32 out_ty) src min) (let ((r ValueRegs (with_flags (fpu_cmp (ScalarSize.Size64) src min) (ConsumesFlags.ConsumesFlagsReturnsReg @@ -3541,6 +4568,7 @@ (trap_code_integer_overflow)) src)))) (value_regs_get r 0))) +(attr rule fpu_to_int_underflow_check_signed_f64_fits_in_32 (veri priority)) (rule -1 (fpu_to_int_underflow_check $true in_ty _out_ty src min) (let ((r ValueRegs (with_flags (fpu_cmp (scalar_size in_ty) src min) @@ -3668,6 +4696,7 @@ dst)) (decl vec_cmp (Reg Reg Type Cond) Reg) +(attr vec_cmp (tag vector)) ;; Floating point Vs / Vc (rule (vec_cmp rn rm ty (Cond.Vc)) @@ -3761,7 +4790,8 @@ (decl elf_tls_get_addr (ExternalName) Reg) (rule (elf_tls_get_addr name) (let ((dst WritableReg (temp_writable_reg $I64)) - (_ Unit (emit (MInst.ElfTlsGetAddr name dst)))) + (tmp WritableReg (temp_writable_reg $I64)) + (_ Unit (emit (MInst.ElfTlsGetAddr (box_external_name name) dst tmp)))) dst)) (decl macho_tls_get_addr (ExternalName) Reg) @@ -3780,31 +4810,38 @@ ;; Materialize a `FlagsAndCC` into a boolean `ValueRegs`. (decl flags_and_cc_to_bool (FlagsAndCC) ValueRegs) +(attr flags_and_cc_to_bool (veri chain)) (rule (flags_and_cc_to_bool (FlagsAndCC.FlagsAndCC flags cc)) (with_flags flags (materialize_bool_result (cond_code cc)))) ;; Get the `ProducesFlags` out of a `FlagsAndCC`. (decl flags_and_cc_flags (FlagsAndCC) ProducesFlags) +(attr flags_and_cc_flags (veri chain)) (rule (flags_and_cc_flags (FlagsAndCC.FlagsAndCC flags _cc)) flags) ;; Get the `IntCC` out of a `FlagsAndCC`. (decl flags_and_cc_cc (FlagsAndCC) IntCC) +(attr flags_and_cc_cc (veri chain)) (rule (flags_and_cc_cc (FlagsAndCC.FlagsAndCC _flags cc)) cc) ;; Helpers for lowering `icmp` sequences. ;; `lower_icmp` contains shared functionality for lowering `icmp` ;; sequences, which `lower_icmp_into_{reg,flags}` extend from. (decl lower_icmp (IntCC Value Value Type) FlagsAndCC) +(attr lower_icmp (veri chain)) (decl lower_icmp_into_reg (IntCC Value Value Type Type) ValueRegs) +(attr lower_icmp_into_reg (veri chain)) (decl lower_icmp_into_flags (IntCC Value Value Type) FlagsAndCC) (decl lower_icmp_const (IntCC Value u64 Type) FlagsAndCC) +(attr lower_icmp_const (veri chain)) ;; For most cases, `lower_icmp_into_flags` is the same as `lower_icmp`, ;; except for some I128 cases (see below). (rule -1 (lower_icmp_into_flags cond x y ty) (lower_icmp cond x y ty)) ;; Vectors. ;; `icmp` into flags for vectors is invalid. -(rule 1 (lower_icmp_into_reg cond x y in_ty @ (multi_lane _ _) _out_ty) +(rule lower_icmp_into_reg_vec + 1 (lower_icmp_into_reg cond x y in_ty @ (multi_lane _ _) _out_ty) (let ((cond Cond (cond_code cond)) (rn Reg (put_in_reg x)) (rm Reg (put_in_reg y))) @@ -3812,31 +4849,44 @@ ;; Determines the appropriate extend op given the value type and the given ArgumentExtension. (decl lower_extend_op (Type ArgumentExtension) ExtendOp) -(rule (lower_extend_op $I8 (ArgumentExtension.Sext)) (ExtendOp.SXTB)) -(rule (lower_extend_op $I16 (ArgumentExtension.Sext)) (ExtendOp.SXTH)) -(rule (lower_extend_op $I8 (ArgumentExtension.Uext)) (ExtendOp.UXTB)) -(rule (lower_extend_op $I16 (ArgumentExtension.Uext)) (ExtendOp.UXTH)) +(attr lower_extend_op (veri chain)) +(rule lower_extend_op_8s (lower_extend_op $I8 (ArgumentExtension.Sext)) (ExtendOp.SXTB)) +(rule lower_extend_op_16s (lower_extend_op $I16 (ArgumentExtension.Sext)) (ExtendOp.SXTH)) +(rule lower_extend_op_8u (lower_extend_op $I8 (ArgumentExtension.Uext)) (ExtendOp.UXTB)) +(rule lower_extend_op_16u (lower_extend_op $I16 (ArgumentExtension.Uext)) (ExtendOp.UXTH)) ;; Integers <= 64-bits. -(rule -2 (lower_icmp_into_reg cond rn rm in_ty out_ty) +(rule lower_icmp_into_reg_int_to_64 + -2 (lower_icmp_into_reg cond rn rm in_ty out_ty) (if (ty_int_ref_scalar_64 in_ty)) (let ((cc Cond (cond_code cond))) (flags_and_cc_to_bool (lower_icmp cond rn rm in_ty)))) -(rule 1 (lower_icmp cond rn rm (fits_in_16 ty)) +(rule lower_icmp_16_signed + 1 (lower_icmp cond rn rm (fits_in_16 ty)) (if (signed_cond_code cond)) (let ((rn Reg (put_in_reg_sext32 rn))) (flags_and_cc (cmp_extend (operand_size ty) rn rm (lower_extend_op ty (ArgumentExtension.Sext))) cond))) -(rule -1 (lower_icmp cond rn (imm12_from_value rm) (fits_in_16 ty)) +(attr rule lower_icmp_16_signed (veri priority)) + +(rule lower_icmp_16_imm12 + -1 (lower_icmp cond rn (imm12_from_value rm) (fits_in_16 ty)) (let ((rn Reg (put_in_reg_zext32 rn))) (flags_and_cc (cmp_imm (operand_size ty) rn rm) cond))) -(rule -2 (lower_icmp cond rn rm (fits_in_16 ty)) + +(rule lower_icmp_16_extend + -2 (lower_icmp cond rn rm (fits_in_16 ty)) (let ((rn Reg (put_in_reg_zext32 rn))) (flags_and_cc (cmp_extend (operand_size ty) rn rm (lower_extend_op ty (ArgumentExtension.Uext))) cond))) -(rule -3 (lower_icmp cond rn (u64_from_iconst c) ty) +(attr rule lower_icmp_16_extend (veri priority)) + +(rule lower_icmp_const + -3 (lower_icmp cond rn (u64_from_iconst c) ty) (if (ty_int_ref_scalar_64 ty)) (lower_icmp_const cond rn c ty)) -(rule -4 (lower_icmp cond rn rm ty) + +(rule lower_icmp_base + -4 (lower_icmp cond rn rm ty) (if (ty_int_ref_scalar_64 ty)) (flags_and_cc (cmp (operand_size ty) rn rm) cond)) @@ -3846,21 +4896,25 @@ ;; A >= B + 1 ;; ==> A - 1 >= B ;; ==> A > B -(rule (lower_icmp_const (IntCC.UnsignedGreaterThanOrEqual) a b ty) +(rule lower_icmp_const_uge_odd + (lower_icmp_const (IntCC.UnsignedGreaterThanOrEqual) a b ty) (if (ty_int_ref_scalar_64 ty)) (if-let $true (u64_is_odd b)) (if-let (imm12_from_u64 imm) (u64_sub b 1)) (flags_and_cc (cmp_imm (operand_size ty) a imm) (IntCC.UnsignedGreaterThan))) -(rule (lower_icmp_const (IntCC.SignedGreaterThanOrEqual) a b ty) +(rule lower_icmp_const_sge_odd + (lower_icmp_const (IntCC.SignedGreaterThanOrEqual) a b ty) (if (ty_int_ref_scalar_64 ty)) (if-let $true (u64_is_odd b)) (if-let (imm12_from_u64 imm) (u64_sub b 1)) (flags_and_cc (cmp_imm (operand_size ty) a imm) (IntCC.SignedGreaterThan))) -(rule -1 (lower_icmp_const cond rn (imm12_from_u64 c) ty) +(rule lower_icmp_const_imm12 + -1 (lower_icmp_const cond rn (imm12_from_u64 c) ty) (if (ty_int_ref_scalar_64 ty)) (flags_and_cc (cmp_imm (operand_size ty) rn c) cond)) -(rule -2 (lower_icmp_const cond rn c ty) +(rule lower_icmp_const_base + -2 (lower_icmp_const cond rn c ty) (if (ty_int_ref_scalar_64 ty)) (flags_and_cc (cmp (operand_size ty) rn (imm ty (ImmExtend.Zero) c)) cond)) @@ -3889,10 +4943,14 @@ (ccmp (OperandSize.Size64) lhs_hi rhs_hi (nzcv $false $false $false $false) (Cond.Eq) cmp_inst))) -(rule (lower_icmp (IntCC.Equal) lhs rhs $I128) +(rule lower_icmp_eq_i128 + (lower_icmp (IntCC.Equal) lhs rhs $I128) (flags_and_cc (lower_icmp_i128_eq_ne lhs rhs) (IntCC.Equal))) -(rule (lower_icmp (IntCC.NotEqual) lhs rhs $I128) +(attr rule lower_icmp_eq_i128 (tag i128)) +(rule lower_icmp_ne_i128 + (lower_icmp (IntCC.NotEqual) lhs rhs $I128) (flags_and_cc (lower_icmp_i128_eq_ne lhs rhs) (IntCC.NotEqual))) +(attr rule lower_icmp_ne_i128 (tag i128)) ;; cmp lhs_lo, rhs_lo ;; cset tmp1, unsigned_cond @@ -3921,6 +4979,7 @@ (MInst.CSet tmp2 cond) (MInst.CSel dst (Cond.Eq) tmp1 tmp2) (value_reg dst)))) +(attr rule lower_icmp_i128_consumer (tag i128)) (decl lower_bmask (Type Type ValueRegs) ValueRegs) @@ -4013,8 +5072,10 @@ ;; Helpers for generating select instruction sequences. (decl lower_select (ProducesFlags Cond Type Value Value) ValueRegs) -(rule 2 (lower_select flags cond (ty_scalar_float ty) rn rm) +(rule 2 (lower_select flags cond (ty_scalar_float (fits_in_64 ty)) rn rm) (with_flags flags (fpu_csel ty cond rn rm))) +(rule 4 (lower_select flags cond $F128 rn rm) + (with_flags flags (vec_csel cond rn rm))) (rule 3 (lower_select flags cond (ty_vec128 ty) rn rm) (with_flags flags (vec_csel cond rn rm))) (rule (lower_select flags cond ty rn rm) @@ -4074,6 +5135,21 @@ (ConsumesFlags.ConsumesFlagsSideEffect (MInst.CondBr taken not_taken kind))) +;; Helper for emitting `MInst.TestBitAndBranch` instructions. +(decl test_branch (TestBitAndBranchKind BranchTarget BranchTarget Reg u8) SideEffectNoResult) +(rule (test_branch kind taken not_taken rn bit) + (SideEffectNoResult.Inst (MInst.TestBitAndBranch kind taken not_taken rn bit))) + +;; Helper for emitting `tbnz` instructions. +(decl tbnz (BranchTarget BranchTarget Reg u8) SideEffectNoResult) +(rule (tbnz taken not_taken rn bit) + (test_branch (TestBitAndBranchKind.NZ) taken not_taken rn bit)) + +;; Helper for emitting `tbz` instructions. +(decl tbz (BranchTarget BranchTarget Reg u8) SideEffectNoResult) +(rule (tbz taken not_taken rn bit) + (test_branch (TestBitAndBranchKind.Z) taken not_taken rn bit)) + ;; Helper for emitting `MInst.MovToNZCV` instructions. (decl mov_to_nzcv (Reg) ProducesFlags) (rule (mov_to_nzcv rn) @@ -4125,6 +5201,7 @@ ;; Helper for creating a zero value `ASIMDMovModImm` immediate. (decl asimd_mov_mod_imm_zero (ScalarSize) ASIMDMovModImm) (extern constructor asimd_mov_mod_imm_zero asimd_mov_mod_imm_zero) +(spec (asimd_mov_mod_imm_zero size) (provide (= (:imm result) #x00))) ;; Helper for fallibly creating an `ASIMDMovModImm` immediate from its parts. (decl pure partial asimd_mov_mod_imm_from_u64 (u64 ScalarSize) ASIMDMovModImm) @@ -4134,6 +5211,88 @@ (decl pure partial asimd_fp_mod_imm_from_u64 (u64 ScalarSize) ASIMDFPModImm) (extern constructor asimd_fp_mod_imm_from_u64 asimd_fp_mod_imm_from_u64) +; Compute the 32-bit value that corresponds to an 8-bit ASIMDFPModImm encoding. +(macro (asimd_fp_mod_imm_value32 imm) + (let ( + ; let imm = imm as u32; + (v (zero_ext 32 imm)) + ; let b0_5 = imm & 0b111111; + (b0_5 (bvand v #x0000003f)) + ; let b6 = (imm >> 6) & 1; + (b6 (bvand (bvlshr_int! v 6) #x00000001)) + ; let b6_inv = b6 ^ 1; + (b6_inv (bvxor b6 #x00000001)) + ; let b7 = (imm >> 7) & 1; + (b7 (bvand (bvlshr_int! v 7) #x00000001)) + ) + ; b0_5 << 19 | (b6 * 0b11111) << 25 | b6_inv << 30 | b7 << 31 + (bvor (bvshl_int! b0_5 19) + (bvor (bvshl_int! (bvmul b6 #x0000001f) 25) + (bvor (bvshl_int! b6_inv 30) + (bvshl_int! b7 31)))))) + +; Compute the 64-bit value that corresponds to an 8-bit ASIMDFPModImm encoding. +(macro (asimd_fp_mod_imm_value64 imm) + (let ( + ; let imm = imm as u64; + (v (zero_ext 64 imm)) + ; let b0_5 = imm & 0b111111; + (b0_5 (bvand v #x000000000000003f)) + ; let b6 = (imm >> 6) & 1; + (b6 (bvand (bvlshr_int! v 6) #x0000000000000001)) + ; let b6_inv = b6 ^ 1; + (b6_inv (bvxor b6 #x0000000000000001)) + ; let b7 = (imm >> 7) & 1; + (b7 (bvand (bvlshr_int! v 7) #x0000000000000001)) + ) + ; b0_5 << 48 | (b6 * 0b11111111) << 54 | b6_inv << 62 | b7 << 63 + (bvor (bvshl_int! b0_5 48) + (bvor (bvshl_int! (bvmul b6 #x00000000000000ff) 54) + (bvor (bvshl_int! b6_inv 62) + (bvshl_int! b7 63)))))) + +(spec (asimd_fp_mod_imm_from_u64 value size) + (provide + (= (:size result) size) + (= (:imm result) + (switch size + ((ScalarSize.Size32) + (let ( + (v (extract 31 0 value)) + (b0_5 (bvand (bvlshr_int! v 19) #x0000003f)) + (b6 (bvand (bvlshr_int! v 19) #x00000040)) + (b7 (bvand (bvlshr_int! v 24) #x00000080)) + (imm (extract 7 0 (bvor b0_5 (bvor b6 b7)))) + ) + imm)) + ((ScalarSize.Size64) + (let ( + (b0_5 (bvand (bvlshr_int! value 48) #x000000000000003f)) + (b6 (bvand (bvlshr_int! value 48) #x0000000000000040)) + (b7 (bvand (bvlshr_int! value 56) #x0000000000000080)) + (imm (extract 7 0 (bvor b0_5 (bvor b6 b7)))) + ) + imm))))) + (match + (switch size + ((ScalarSize.Size32) + (let ( + (v (extract 31 0 value)) + (b0_5 (bvand (bvlshr_int! v 19) #x0000003f)) + (b6 (bvand (bvlshr_int! v 19) #x00000040)) + (b7 (bvand (bvlshr_int! v 24) #x00000080)) + (imm (extract 7 0 (bvor b0_5 (bvor b6 b7)))) + ) + (= v (asimd_fp_mod_imm_value32! imm)))) + ((ScalarSize.Size64) + (let ( + (b0_5 (bvand (bvlshr_int! value 48) #x000000000000003f)) + (b6 (bvand (bvlshr_int! value 48) #x0000000000000040)) + (b7 (bvand (bvlshr_int! value 56) #x0000000000000080)) + (imm (extract 7 0 (bvor b0_5 (bvor b6 b7)))) + ) + (= value (asimd_fp_mod_imm_value64! imm))))))) + ;; Helper for creating a `VecDupFPImm` instruction (decl vec_dup_fp_imm (ASIMDFPModImm VectorSize) Reg) (rule (vec_dup_fp_imm imm size) diff --git a/cranelift/codegen/src/isa/aarch64/inst/args.rs b/cranelift/codegen/src/isa/aarch64/inst/args.rs index cf1d6d05a671..575ed00fa07d 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/args.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/args.rs @@ -1,24 +1,20 @@ //! AArch64 ISA definitions: instruction arguments. use crate::ir::types::*; -use crate::ir::Type; use crate::isa::aarch64::inst::*; -use crate::machinst::{ty_bits, MachLabel, PrettyPrint, Reg}; -use core::convert::Into; -use std::string::String; //============================================================================= // Instruction sub-components: shift and extend descriptors /// A shift operator for a register or immediate. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum ShiftOp { /// Logical shift left. LSL = 0b00, /// Logical shift right. LSR = 0b01, - /// Arithmentic shift right. + /// Arithmetic shift right. ASR = 0b10, /// Rotate right. ROR = 0b11, @@ -142,82 +138,17 @@ impl AMode { /// Memory reference using `reg1 + sizeof(ty) * reg2` as an address, with `reg2` sign- or /// zero-extended as per `op`. - pub fn reg_plus_reg_scaled_extended(reg1: Reg, reg2: Reg, ty: Type, op: ExtendOp) -> AMode { + pub fn reg_plus_reg_scaled_extended(reg1: Reg, reg2: Reg, op: ExtendOp) -> AMode { AMode::RegScaledExtended { rn: reg1, rm: reg2, - ty, extendop: op, } } - - pub(crate) fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self { - // This should match `memarg_operands()`. - match self { - &AMode::Unscaled { rn, simm9 } => AMode::Unscaled { - rn: allocs.next(rn), - simm9, - }, - &AMode::UnsignedOffset { rn, uimm12 } => AMode::UnsignedOffset { - rn: allocs.next(rn), - uimm12, - }, - &AMode::RegReg { rn, rm } => AMode::RegReg { - rn: allocs.next(rn), - rm: allocs.next(rm), - }, - &AMode::RegScaled { rn, rm, ty } => AMode::RegScaled { - rn: allocs.next(rn), - rm: allocs.next(rm), - ty, - }, - &AMode::RegScaledExtended { - rn, - rm, - ty, - extendop, - } => AMode::RegScaledExtended { - rn: allocs.next(rn), - rm: allocs.next(rm), - ty, - extendop, - }, - &AMode::RegExtended { rn, rm, extendop } => AMode::RegExtended { - rn: allocs.next(rn), - rm: allocs.next(rm), - extendop, - }, - &AMode::RegOffset { rn, off, ty } => AMode::RegOffset { - rn: allocs.next(rn), - off, - ty, - }, - &AMode::SPPreIndexed { .. } - | &AMode::SPPostIndexed { .. } - | &AMode::FPOffset { .. } - | &AMode::SPOffset { .. } - | &AMode::NominalSPOffset { .. } - | &AMode::Const { .. } - | AMode::Label { .. } => self.clone(), - } - } } pub use crate::isa::aarch64::lower::isle::generated_code::PairAMode; -impl PairAMode { - pub(crate) fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self { - // Should match `pairmemarg_operands()`. - match self { - &PairAMode::SignedOffset { reg, simm7 } => PairAMode::SignedOffset { - reg: allocs.next(reg), - simm7, - }, - &PairAMode::SPPreIndexed { .. } | &PairAMode::SPPostIndexed { .. } => self.clone(), - } - } -} - //============================================================================= // Instruction sub-components (conditions, branches and branch targets): // definitions @@ -340,178 +271,180 @@ impl BranchTarget { } } + /// Return the target's offset, if specified, or zero if label-based. + pub fn as_offset14_or_zero(self) -> u32 { + self.as_offset_bounded(14) + } + /// Return the target's offset, if specified, or zero if label-based. pub fn as_offset19_or_zero(self) -> u32 { - let off = match self { - BranchTarget::ResolvedOffset(off) => off >> 2, - _ => 0, - }; - assert!(off <= 0x3ffff); - assert!(off >= -0x40000); - (off as u32) & 0x7ffff + self.as_offset_bounded(19) } /// Return the target's offset, if specified, or zero if label-based. pub fn as_offset26_or_zero(self) -> u32 { + self.as_offset_bounded(26) + } + + fn as_offset_bounded(self, bits: u32) -> u32 { let off = match self { BranchTarget::ResolvedOffset(off) => off >> 2, _ => 0, }; - assert!(off <= 0x1ffffff); - assert!(off >= -0x2000000); - (off as u32) & 0x3ffffff + let hi = (1 << (bits - 1)) - 1; + let lo = -(1 << bits - 1); + assert!(off <= hi); + assert!(off >= lo); + (off as u32) & ((1 << bits) - 1) } } impl PrettyPrint for ShiftOpAndAmt { - fn pretty_print(&self, _: u8, _: &mut AllocationConsumer<'_>) -> String { + fn pretty_print(&self, _: u8) -> String { format!("{:?} {}", self.op(), self.amt().value()) } } impl PrettyPrint for ExtendOp { - fn pretty_print(&self, _: u8, _: &mut AllocationConsumer<'_>) -> String { - format!("{:?}", self) + fn pretty_print(&self, _: u8) -> String { + format!("{self:?}") } } impl PrettyPrint for MemLabel { - fn pretty_print(&self, _: u8, _: &mut AllocationConsumer<'_>) -> String { + fn pretty_print(&self, _: u8) -> String { match self { - MemLabel::PCRel(off) => format!("pc+{}", off), + MemLabel::PCRel(off) => format!("pc+{off}"), MemLabel::Mach(off) => format!("label({})", off.get()), } } } -fn shift_for_type(ty: Type) -> usize { - match ty.bytes() { +fn shift_for_type(size_bytes: u8) -> usize { + match size_bytes { 1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4, - _ => panic!("unknown type: {}", ty), + _ => panic!("unknown type size: {size_bytes}"), } } impl PrettyPrint for AMode { - fn pretty_print(&self, _: u8, allocs: &mut AllocationConsumer<'_>) -> String { + fn pretty_print(&self, size_bytes: u8) -> String { + debug_assert!(size_bytes != 0); match self { &AMode::Unscaled { rn, simm9 } => { - let reg = pretty_print_reg(rn, allocs); + let reg = pretty_print_reg(rn); if simm9.value != 0 { - let simm9 = simm9.pretty_print(8, allocs); - format!("[{}, {}]", reg, simm9) + let simm9 = simm9.pretty_print(8); + format!("[{reg}, {simm9}]") } else { - format!("[{}]", reg) + format!("[{reg}]") } } &AMode::UnsignedOffset { rn, uimm12 } => { - let reg = pretty_print_reg(rn, allocs); - if uimm12.value != 0 { - let uimm12 = uimm12.pretty_print(8, allocs); - format!("[{}, {}]", reg, uimm12) + let reg = pretty_print_reg(rn); + if uimm12.value() != 0 { + let uimm12 = uimm12.pretty_print(8); + format!("[{reg}, {uimm12}]") } else { - format!("[{}]", reg) + format!("[{reg}]") } } &AMode::RegReg { rn, rm } => { - let r1 = pretty_print_reg(rn, allocs); - let r2 = pretty_print_reg(rm, allocs); - format!("[{}, {}]", r1, r2) + let r1 = pretty_print_reg(rn); + let r2 = pretty_print_reg(rm); + format!("[{r1}, {r2}]") } - &AMode::RegScaled { rn, rm, ty } => { - let r1 = pretty_print_reg(rn, allocs); - let r2 = pretty_print_reg(rm, allocs); - let shift = shift_for_type(ty); - format!("[{}, {}, LSL #{}]", r1, r2, shift) + &AMode::RegScaled { rn, rm } => { + let r1 = pretty_print_reg(rn); + let r2 = pretty_print_reg(rm); + let shift = shift_for_type(size_bytes); + format!("[{r1}, {r2}, LSL #{shift}]") } - &AMode::RegScaledExtended { - rn, - rm, - ty, - extendop, - } => { - let shift = shift_for_type(ty); + &AMode::RegScaledExtended { rn, rm, extendop } => { + let shift = shift_for_type(size_bytes); let size = match extendop { ExtendOp::SXTW | ExtendOp::UXTW => OperandSize::Size32, _ => OperandSize::Size64, }; - let r1 = pretty_print_reg(rn, allocs); - let r2 = pretty_print_ireg(rm, size, allocs); - let op = extendop.pretty_print(0, allocs); - format!("[{}, {}, {} #{}]", r1, r2, op, shift) + let r1 = pretty_print_reg(rn); + let r2 = pretty_print_ireg(rm, size); + let op = extendop.pretty_print(0); + format!("[{r1}, {r2}, {op} #{shift}]") } &AMode::RegExtended { rn, rm, extendop } => { let size = match extendop { ExtendOp::SXTW | ExtendOp::UXTW => OperandSize::Size32, _ => OperandSize::Size64, }; - let r1 = pretty_print_reg(rn, allocs); - let r2 = pretty_print_ireg(rm, size, allocs); - let op = extendop.pretty_print(0, allocs); - format!("[{}, {}, {}]", r1, r2, op) + let r1 = pretty_print_reg(rn); + let r2 = pretty_print_ireg(rm, size); + let op = extendop.pretty_print(0); + format!("[{r1}, {r2}, {op}]") } - &AMode::Label { ref label } => label.pretty_print(0, allocs), + &AMode::Label { ref label } => label.pretty_print(0), &AMode::SPPreIndexed { simm9 } => { - let simm9 = simm9.pretty_print(8, allocs); - format!("[sp, {}]!", simm9) + let simm9 = simm9.pretty_print(8); + format!("[sp, {simm9}]!") } &AMode::SPPostIndexed { simm9 } => { - let simm9 = simm9.pretty_print(8, allocs); - format!("[sp], {}", simm9) + let simm9 = simm9.pretty_print(8); + format!("[sp], {simm9}") } AMode::Const { addr } => format!("[const({})]", addr.as_u32()), // Eliminated by `mem_finalize()`. &AMode::SPOffset { .. } | &AMode::FPOffset { .. } - | &AMode::NominalSPOffset { .. } + | &AMode::IncomingArg { .. } + | &AMode::SlotOffset { .. } | &AMode::RegOffset { .. } => { - panic!("Unexpected pseudo mem-arg mode: {:?}", self) + panic!("Unexpected pseudo mem-arg mode: {self:?}") } } } } impl PrettyPrint for PairAMode { - fn pretty_print(&self, _: u8, allocs: &mut AllocationConsumer<'_>) -> String { + fn pretty_print(&self, _: u8) -> String { match self { &PairAMode::SignedOffset { reg, simm7 } => { - let reg = pretty_print_reg(reg, allocs); + let reg = pretty_print_reg(reg); if simm7.value != 0 { - let simm7 = simm7.pretty_print(8, allocs); - format!("[{}, {}]", reg, simm7) + let simm7 = simm7.pretty_print(8); + format!("[{reg}, {simm7}]") } else { - format!("[{}]", reg) + format!("[{reg}]") } } &PairAMode::SPPreIndexed { simm7 } => { - let simm7 = simm7.pretty_print(8, allocs); - format!("[sp, {}]!", simm7) + let simm7 = simm7.pretty_print(8); + format!("[sp, {simm7}]!") } &PairAMode::SPPostIndexed { simm7 } => { - let simm7 = simm7.pretty_print(8, allocs); - format!("[sp], {}", simm7) + let simm7 = simm7.pretty_print(8); + format!("[sp], {simm7}") } } } } impl PrettyPrint for Cond { - fn pretty_print(&self, _: u8, _: &mut AllocationConsumer<'_>) -> String { - let mut s = format!("{:?}", self); + fn pretty_print(&self, _: u8) -> String { + let mut s = format!("{self:?}"); s.make_ascii_lowercase(); s } } impl PrettyPrint for BranchTarget { - fn pretty_print(&self, _: u8, _: &mut AllocationConsumer<'_>) -> String { + fn pretty_print(&self, _: u8) -> String { match self { &BranchTarget::Label(label) => format!("label{:?}", label.get()), - &BranchTarget::ResolvedOffset(off) => format!("{}", off), + &BranchTarget::ResolvedOffset(off) => format!("{off}"), } } } @@ -580,6 +513,14 @@ impl OperandSize { OperandSize::Size64 => 1, } } + + /// The maximum unsigned value representable in a value of this size. + pub fn max_value(&self) -> u64 { + match self { + OperandSize::Size32 => u32::MAX as u64, + OperandSize::Size64 => u64::MAX, + } + } } /// Type used to communicate the size of a scalar SIMD & FP operand. @@ -603,7 +544,7 @@ impl ScalarSize { match self { ScalarSize::Size8 | ScalarSize::Size16 | ScalarSize::Size32 => OperandSize::Size32, ScalarSize::Size64 => OperandSize::Size64, - _ => panic!("Unexpected operand_size request for: {:?}", self), + _ => panic!("Unexpected operand_size request for: {self:?}"), } } @@ -614,7 +555,7 @@ impl ScalarSize { ScalarSize::Size16 => 0b11, ScalarSize::Size32 => 0b00, ScalarSize::Size64 => 0b01, - _ => panic!("Unexpected scalar FP operand size: {:?}", self), + _ => panic!("Unexpected scalar FP operand size: {self:?}"), } } @@ -639,6 +580,17 @@ impl ScalarSize { ScalarSize::Size128 => ScalarSize::Size64, } } + + /// Return a type with the same size as this scalar. + pub fn ty(&self) -> Type { + match self { + ScalarSize::Size8 => I8, + ScalarSize::Size16 => I16, + ScalarSize::Size32 => I32, + ScalarSize::Size64 => I64, + ScalarSize::Size128 => I128, + } + } } /// Type used to communicate the size of a vector operand. @@ -671,7 +623,7 @@ impl VectorSize { (ScalarSize::Size32, false) => VectorSize::Size32x2, (ScalarSize::Size32, true) => VectorSize::Size32x4, (ScalarSize::Size64, true) => VectorSize::Size64x2, - _ => panic!("Unexpected scalar FP operand size: {:?}", size), + _ => panic!("Unexpected scalar FP operand size: {size:?}"), } } @@ -727,7 +679,7 @@ impl VectorSize { match self.lane_size() { ScalarSize::Size32 => 0b0, ScalarSize::Size64 => 0b1, - size => panic!("Unsupported floating-point size for vector op: {:?}", size), + size => panic!("Unsupported floating-point size for vector op: {size:?}"), } } } @@ -745,3 +697,15 @@ impl APIKey { 0xd503201f | (crm << 8) | (op2 << 5) } } + +pub use crate::isa::aarch64::lower::isle::generated_code::TestBitAndBranchKind; + +impl TestBitAndBranchKind { + /// Complements this branch condition to act on the opposite result. + pub fn complement(&self) -> TestBitAndBranchKind { + match self { + TestBitAndBranchKind::Z => TestBitAndBranchKind::NZ, + TestBitAndBranchKind::NZ => TestBitAndBranchKind::Z, + } + } +} diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index 2742134d1f0f..8f76dd56a9f4 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -1,14 +1,10 @@ //! AArch64 ISA: binary code emission. use cranelift_control::ControlPlane; -use regalloc2::Allocation; -use crate::binemit::{Reloc, StackMap}; -use crate::ir::{self, types::*, LibCall, MemFlags, RelSourceLoc, TrapCode}; +use crate::ir::{self, types::*}; use crate::isa::aarch64::inst::*; -use crate::machinst::{ty_bits, Reg, RegClass, Writable}; use crate::trace; -use core::convert::TryFrom; /// Memory addressing mode finalization: convert "special" modes (e.g., /// generic arbitrary stack offset) into real addressing modes, possibly by @@ -17,37 +13,51 @@ use core::convert::TryFrom; pub fn mem_finalize( sink: Option<&mut MachBuffer>, mem: &AMode, + access_ty: Type, state: &EmitState, ) -> (SmallVec<[Inst; 4]>, AMode) { match mem { - &AMode::RegOffset { off, ty, .. } - | &AMode::SPOffset { off, ty } - | &AMode::FPOffset { off, ty } - | &AMode::NominalSPOffset { off, ty } => { + &AMode::RegOffset { off, .. } + | &AMode::SPOffset { off } + | &AMode::FPOffset { off } + | &AMode::IncomingArg { off } + | &AMode::SlotOffset { off } => { let basereg = match mem { &AMode::RegOffset { rn, .. } => rn, - &AMode::SPOffset { .. } | &AMode::NominalSPOffset { .. } => stack_reg(), + &AMode::SPOffset { .. } + | &AMode::SlotOffset { .. } + | &AMode::IncomingArg { .. } => stack_reg(), &AMode::FPOffset { .. } => fp_reg(), _ => unreachable!(), }; - let adj = match mem { - &AMode::NominalSPOffset { .. } => { + let off = match mem { + &AMode::IncomingArg { .. } => { + let frame_layout = state.frame_layout(); + i64::from( + frame_layout.setup_area_size + + frame_layout.tail_args_size + + frame_layout.clobber_size + + frame_layout.fixed_frame_storage_size + + frame_layout.outgoing_args_size, + ) - off + } + &AMode::SlotOffset { .. } => { + let adj = i64::from(state.frame_layout().outgoing_args_size); trace!( - "mem_finalize: nominal SP offset {} + adj {} -> {}", + "mem_finalize: slot offset {} + adj {} -> {}", off, - state.virtual_sp_offset, - off + state.virtual_sp_offset + adj, + off + adj ); - state.virtual_sp_offset + off + adj } - _ => 0, + _ => off, }; - let off = off + adj; if let Some(simm9) = SImm9::maybe_from_i64(off) { let mem = AMode::Unscaled { rn: basereg, simm9 }; (smallvec![], mem) - } else if let Some(uimm12) = UImm12Scaled::maybe_from_i64(off, ty) { + } else if let Some(uimm12) = UImm12Scaled::maybe_from_i64(off, access_ty) { let mem = AMode::UnsignedOffset { rn: basereg, uimm12, @@ -154,24 +164,35 @@ fn enc_cbr(op_31_24: u32, off_18_0: u32, op_4: u32, cond: u32) -> u32 { (op_31_24 << 24) | (off_18_0 << 5) | (op_4 << 4) | cond } -fn enc_conditional_br( - taken: BranchTarget, - kind: CondBrKind, - allocs: &mut AllocationConsumer<'_>, -) -> u32 { +fn enc_conditional_br(taken: BranchTarget, kind: CondBrKind) -> u32 { match kind { - CondBrKind::Zero(reg) => { - let reg = allocs.next(reg); - enc_cmpbr(0b1_011010_0, taken.as_offset19_or_zero(), reg) - } - CondBrKind::NotZero(reg) => { - let reg = allocs.next(reg); - enc_cmpbr(0b1_011010_1, taken.as_offset19_or_zero(), reg) - } + CondBrKind::Zero(reg) => enc_cmpbr(0b1_011010_0, taken.as_offset19_or_zero(), reg), + CondBrKind::NotZero(reg) => enc_cmpbr(0b1_011010_1, taken.as_offset19_or_zero(), reg), CondBrKind::Cond(c) => enc_cbr(0b01010100, taken.as_offset19_or_zero(), 0b0, c.bits()), } } +fn enc_test_bit_and_branch( + kind: TestBitAndBranchKind, + taken: BranchTarget, + reg: Reg, + bit: u8, +) -> u32 { + assert!(bit < 64); + let op_31 = u32::from(bit >> 5); + let op_23_19 = u32::from(bit & 0b11111); + let op_30_24 = 0b0110110 + | match kind { + TestBitAndBranchKind::Z => 0, + TestBitAndBranchKind::NZ => 1, + }; + (op_31 << 31) + | (op_30_24 << 24) + | (op_23_19 << 19) + | (taken.as_offset14_or_zero() << 5) + | machreg_to_gpr(reg) +} + fn enc_move_wide(op: MoveWideOp, rd: Writable, imm: MoveWideConst, size: OperandSize) -> u32 { assert!(imm.shift <= 0b11); let op = match op { @@ -629,37 +650,28 @@ fn enc_asimd_mod_imm(rd: Writable, q_op: u32, cmode: u32, imm: u8) -> u32 { /// State carried between emissions of a sequence of instructions. #[derive(Default, Clone, Debug)] pub struct EmitState { - /// Addend to convert nominal-SP offsets to real-SP offsets at the current - /// program point. - pub(crate) virtual_sp_offset: i64, - /// Offset of FP from nominal-SP. - pub(crate) nominal_sp_to_fp: i64, - /// Safepoint stack map for upcoming instruction, as provided to `pre_safepoint()`. - stack_map: Option, - /// Current source-code location corresponding to instruction to be emitted. - cur_srcloc: RelSourceLoc, + /// The user stack map for the upcoming instruction, as provided to + /// `pre_safepoint()`. + user_stack_map: Option, + /// Only used during fuzz-testing. Otherwise, it is a zero-sized struct and /// optimized away at compiletime. See [cranelift_control]. ctrl_plane: ControlPlane, + + frame_layout: FrameLayout, } impl MachInstEmitState for EmitState { fn new(abi: &Callee, ctrl_plane: ControlPlane) -> Self { EmitState { - virtual_sp_offset: 0, - nominal_sp_to_fp: abi.frame_size() as i64, - stack_map: None, - cur_srcloc: Default::default(), + user_stack_map: None, ctrl_plane, + frame_layout: abi.frame_layout().clone(), } } - fn pre_safepoint(&mut self, stack_map: StackMap) { - self.stack_map = Some(stack_map); - } - - fn pre_sourceloc(&mut self, srcloc: RelSourceLoc) { - self.cur_srcloc = srcloc; + fn pre_safepoint(&mut self, user_stack_map: Option) { + self.user_stack_map = user_stack_map; } fn ctrl_plane_mut(&mut self) -> &mut ControlPlane { @@ -669,19 +681,19 @@ impl MachInstEmitState for EmitState { fn take_ctrl_plane(self) -> ControlPlane { self.ctrl_plane } + + fn frame_layout(&self) -> &FrameLayout { + &self.frame_layout + } } impl EmitState { - fn take_stack_map(&mut self) -> Option { - self.stack_map.take() + fn take_stack_map(&mut self) -> Option { + self.user_stack_map.take() } fn clear_post_insn(&mut self) { - self.stack_map = None; - } - - fn cur_srcloc(&self) -> RelSourceLoc { - self.cur_srcloc + self.user_stack_map = None; } } @@ -699,15 +711,7 @@ impl MachInstEmit for Inst { type State = EmitState; type Info = EmitInfo; - fn emit( - &self, - allocs: &[Allocation], - sink: &mut MachBuffer, - emit_info: &Self::Info, - state: &mut EmitState, - ) { - let mut allocs = AllocationConsumer::new(allocs); - + fn emit(&self, sink: &mut MachBuffer, emit_info: &Self::Info, state: &mut EmitState) { // N.B.: we *must* not exceed the "worst-case size" used to compute // where to insert islands, except when islands are explicitly triggered // (with an `EmitIsland`). We check this in debug builds. This is `mut` @@ -723,10 +727,6 @@ impl MachInstEmit for Inst { rn, rm, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); - debug_assert!(match alu_op { ALUOp::SDiv | ALUOp::UDiv | ALUOp::SMulH | ALUOp::UMulH => size == OperandSize::Size64, @@ -780,11 +780,6 @@ impl MachInstEmit for Inst { rn, ra, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); - let ra = allocs.next(ra); - let (top11, bit15) = match alu_op { ALUOp3::MAdd => (0b0_00_11011_000, 0), ALUOp3::MSub => (0b0_00_11011_000, 1), @@ -807,8 +802,6 @@ impl MachInstEmit for Inst { rn, ref imm12, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let top8 = match alu_op { ALUOp::Add => 0b000_10001, ALUOp::Sub => 0b010_10001, @@ -832,8 +825,6 @@ impl MachInstEmit for Inst { rn, ref imml, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (top9, inv) = match alu_op { ALUOp::Orr => (0b001_100100, false), ALUOp::And => (0b000_100100, false), @@ -845,7 +836,7 @@ impl MachInstEmit for Inst { _ => unimplemented!("{:?}", alu_op), }; let top9 = top9 | size.sf_bit() << 8; - let imml = if inv { imml.invert() } else { imml.clone() }; + let imml = if inv { imml.invert() } else { *imml }; sink.put4(enc_arith_rr_imml(top9, imml.enc_bits(), rn, rd)); } @@ -856,8 +847,6 @@ impl MachInstEmit for Inst { rn, ref immshift, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let amt = immshift.value(); let (top10, immr, imms) = match alu_op { ALUOp::RotR => (0b0001001110, machreg_to_gpr(rn), u32::from(amt)), @@ -895,9 +884,6 @@ impl MachInstEmit for Inst { rm, ref shiftop, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); let top11: u32 = match alu_op { ALUOp::Add => 0b000_01011000, ALUOp::AddS => 0b001_01011000, @@ -926,9 +912,6 @@ impl MachInstEmit for Inst { rm, extendop, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); let top11: u32 = match alu_op { ALUOp::Add => 0b00001011001, ALUOp::Sub => 0b01001011001, @@ -944,8 +927,6 @@ impl MachInstEmit for Inst { &Inst::BitRR { op, size, rd, rn, .. } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (op1, op2) = match op { BitOp::RBit => (0b00000, 0b000000), BitOp::Clz => (0b00000, 0b000100), @@ -966,15 +947,16 @@ impl MachInstEmit for Inst { | &Inst::ULoad64 { rd, ref mem, flags, .. } + | &Inst::FpuLoad16 { rd, ref mem, flags } | &Inst::FpuLoad32 { rd, ref mem, flags } | &Inst::FpuLoad64 { rd, ref mem, flags } | &Inst::FpuLoad128 { rd, ref mem, flags } => { - let rd = allocs.next_writable(rd); - let mem = mem.with_allocs(&mut allocs); - let (mem_insts, mem) = mem_finalize(Some(sink), &mem, state); + let mem = mem.clone(); + let access_ty = self.mem_type().unwrap(); + let (mem_insts, mem) = mem_finalize(Some(sink), &mem, access_ty, state); for inst in mem_insts.into_iter() { - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); } // ldst encoding helpers take Reg, not Writable. @@ -983,50 +965,45 @@ impl MachInstEmit for Inst { // This is the base opcode (top 10 bits) for the "unscaled // immediate" form (Unscaled). Other addressing modes will OR in // other values for bits 24/25 (bits 1/2 of this constant). - let (op, bits) = match self { - &Inst::ULoad8 { .. } => (0b0011100001, 8), - &Inst::SLoad8 { .. } => (0b0011100010, 8), - &Inst::ULoad16 { .. } => (0b0111100001, 16), - &Inst::SLoad16 { .. } => (0b0111100010, 16), - &Inst::ULoad32 { .. } => (0b1011100001, 32), - &Inst::SLoad32 { .. } => (0b1011100010, 32), - &Inst::ULoad64 { .. } => (0b1111100001, 64), - &Inst::FpuLoad32 { .. } => (0b1011110001, 32), - &Inst::FpuLoad64 { .. } => (0b1111110001, 64), - &Inst::FpuLoad128 { .. } => (0b0011110011, 128), + let op = match self { + Inst::ULoad8 { .. } => 0b0011100001, + Inst::SLoad8 { .. } => 0b0011100010, + Inst::ULoad16 { .. } => 0b0111100001, + Inst::SLoad16 { .. } => 0b0111100010, + Inst::ULoad32 { .. } => 0b1011100001, + Inst::SLoad32 { .. } => 0b1011100010, + Inst::ULoad64 { .. } => 0b1111100001, + Inst::FpuLoad16 { .. } => 0b0111110001, + Inst::FpuLoad32 { .. } => 0b1011110001, + Inst::FpuLoad64 { .. } => 0b1111110001, + Inst::FpuLoad128 { .. } => 0b0011110011, _ => unreachable!(), }; - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { + if let Some(trap_code) = flags.trap_code() { // Register the offset at which the actual load instruction starts. - sink.add_trap(TrapCode::HeapOutOfBounds); + sink.add_trap(trap_code); } match &mem { &AMode::Unscaled { rn, simm9 } => { - let reg = allocs.next(rn); + let reg = rn; sink.put4(enc_ldst_simm9(op, simm9, 0b00, reg, rd)); } &AMode::UnsignedOffset { rn, uimm12 } => { - let reg = allocs.next(rn); - if uimm12.value() != 0 { - assert_eq!(bits, ty_bits(uimm12.scale_ty())); - } + let reg = rn; sink.put4(enc_ldst_uimm12(op, uimm12, reg, rd)); } &AMode::RegReg { rn, rm } => { - let r1 = allocs.next(rn); - let r2 = allocs.next(rm); + let r1 = rn; + let r2 = rm; sink.put4(enc_ldst_reg( op, r1, r2, /* scaled = */ false, /* extendop = */ None, rd, )); } - &AMode::RegScaled { rn, rm, ty } - | &AMode::RegScaledExtended { rn, rm, ty, .. } => { - let r1 = allocs.next(rn); - let r2 = allocs.next(rm); - assert_eq!(bits, ty_bits(ty)); + &AMode::RegScaled { rn, rm } | &AMode::RegScaledExtended { rn, rm, .. } => { + let r1 = rn; + let r2 = rm; let extendop = match &mem { &AMode::RegScaled { .. } => None, &AMode::RegScaledExtended { extendop, .. } => Some(extendop), @@ -1037,8 +1014,8 @@ impl MachInstEmit for Inst { )); } &AMode::RegExtended { rn, rm, extendop } => { - let r1 = allocs.next(rn); - let r2 = allocs.next(rm); + let r1 = rn; + let r2 = rm; sink.put4(enc_ldst_reg( op, r1, @@ -1085,7 +1062,7 @@ impl MachInstEmit for Inst { &Inst::FpuLoad128 { .. } => { sink.put4(enc_ldst_imm19(0b10011100, offset, rd)); } - _ => panic!("Unspported size for LDR from constant pool!"), + _ => panic!("Unsupported size for LDR from constant pool!"), } } &AMode::SPPreIndexed { simm9 } => { @@ -1099,10 +1076,11 @@ impl MachInstEmit for Inst { // Eliminated by `mem_finalize()` above. &AMode::SPOffset { .. } | &AMode::FPOffset { .. } - | &AMode::NominalSPOffset { .. } + | &AMode::IncomingArg { .. } + | &AMode::SlotOffset { .. } | &AMode::Const { .. } | &AMode::RegOffset { .. } => { - panic!("Should not see {:?} here!", mem) + panic!("Should not see {mem:?} here!") } } } @@ -1111,56 +1089,54 @@ impl MachInstEmit for Inst { | &Inst::Store16 { rd, ref mem, flags } | &Inst::Store32 { rd, ref mem, flags } | &Inst::Store64 { rd, ref mem, flags } + | &Inst::FpuStore16 { rd, ref mem, flags } | &Inst::FpuStore32 { rd, ref mem, flags } | &Inst::FpuStore64 { rd, ref mem, flags } | &Inst::FpuStore128 { rd, ref mem, flags } => { - let rd = allocs.next(rd); - let mem = mem.with_allocs(&mut allocs); - let (mem_insts, mem) = mem_finalize(Some(sink), &mem, state); + let mem = mem.clone(); + let access_ty = self.mem_type().unwrap(); + let (mem_insts, mem) = mem_finalize(Some(sink), &mem, access_ty, state); for inst in mem_insts.into_iter() { - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); } - let (op, bits) = match self { - &Inst::Store8 { .. } => (0b0011100000, 8), - &Inst::Store16 { .. } => (0b0111100000, 16), - &Inst::Store32 { .. } => (0b1011100000, 32), - &Inst::Store64 { .. } => (0b1111100000, 64), - &Inst::FpuStore32 { .. } => (0b1011110000, 32), - &Inst::FpuStore64 { .. } => (0b1111110000, 64), - &Inst::FpuStore128 { .. } => (0b0011110010, 128), + let op = match self { + Inst::Store8 { .. } => 0b0011100000, + Inst::Store16 { .. } => 0b0111100000, + Inst::Store32 { .. } => 0b1011100000, + Inst::Store64 { .. } => 0b1111100000, + Inst::FpuStore16 { .. } => 0b0111110000, + Inst::FpuStore32 { .. } => 0b1011110000, + Inst::FpuStore64 { .. } => 0b1111110000, + Inst::FpuStore128 { .. } => 0b0011110010, _ => unreachable!(), }; - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { + if let Some(trap_code) = flags.trap_code() { // Register the offset at which the actual store instruction starts. - sink.add_trap(TrapCode::HeapOutOfBounds); + sink.add_trap(trap_code); } match &mem { &AMode::Unscaled { rn, simm9 } => { - let reg = allocs.next(rn); + let reg = rn; sink.put4(enc_ldst_simm9(op, simm9, 0b00, reg, rd)); } &AMode::UnsignedOffset { rn, uimm12 } => { - let reg = allocs.next(rn); - if uimm12.value() != 0 { - assert_eq!(bits, ty_bits(uimm12.scale_ty())); - } + let reg = rn; sink.put4(enc_ldst_uimm12(op, uimm12, reg, rd)); } &AMode::RegReg { rn, rm } => { - let r1 = allocs.next(rn); - let r2 = allocs.next(rm); + let r1 = rn; + let r2 = rm; sink.put4(enc_ldst_reg( op, r1, r2, /* scaled = */ false, /* extendop = */ None, rd, )); } - &AMode::RegScaled { rn, rm, .. } | &AMode::RegScaledExtended { rn, rm, .. } => { - let r1 = allocs.next(rn); - let r2 = allocs.next(rm); + &AMode::RegScaled { rn, rm } | &AMode::RegScaledExtended { rn, rm, .. } => { + let r1 = rn; + let r2 = rm; let extendop = match &mem { &AMode::RegScaled { .. } => None, &AMode::RegScaledExtended { extendop, .. } => Some(extendop), @@ -1171,8 +1147,8 @@ impl MachInstEmit for Inst { )); } &AMode::RegExtended { rn, rm, extendop } => { - let r1 = allocs.next(rn); - let r2 = allocs.next(rm); + let r1 = rn; + let r2 = rm; sink.put4(enc_ldst_reg( op, r1, @@ -1196,10 +1172,11 @@ impl MachInstEmit for Inst { // Eliminated by `mem_finalize()` above. &AMode::SPOffset { .. } | &AMode::FPOffset { .. } - | &AMode::NominalSPOffset { .. } + | &AMode::IncomingArg { .. } + | &AMode::SlotOffset { .. } | &AMode::Const { .. } | &AMode::RegOffset { .. } => { - panic!("Should not see {:?} here!", mem) + panic!("Should not see {mem:?} here!") } } } @@ -1210,18 +1187,14 @@ impl MachInstEmit for Inst { ref mem, flags, } => { - let rt = allocs.next(rt); - let rt2 = allocs.next(rt2); - let mem = mem.with_allocs(&mut allocs); - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { + let mem = mem.clone(); + if let Some(trap_code) = flags.trap_code() { // Register the offset at which the actual store instruction starts. - sink.add_trap(TrapCode::HeapOutOfBounds); + sink.add_trap(trap_code); } match &mem { &PairAMode::SignedOffset { reg, simm7 } => { assert_eq!(simm7.scale_ty, I64); - let reg = allocs.next(reg); sink.put4(enc_ldst_pair(0b1010100100, simm7, reg, rt, rt2)); } &PairAMode::SPPreIndexed { simm7 } => { @@ -1242,19 +1215,17 @@ impl MachInstEmit for Inst { ref mem, flags, } => { - let rt = allocs.next(rt.to_reg()); - let rt2 = allocs.next(rt2.to_reg()); - let mem = mem.with_allocs(&mut allocs); - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { + let rt = rt.to_reg(); + let rt2 = rt2.to_reg(); + let mem = mem.clone(); + if let Some(trap_code) = flags.trap_code() { // Register the offset at which the actual load instruction starts. - sink.add_trap(TrapCode::HeapOutOfBounds); + sink.add_trap(trap_code); } match &mem { &PairAMode::SignedOffset { reg, simm7 } => { assert_eq!(simm7.scale_ty, I64); - let reg = allocs.next(reg); sink.put4(enc_ldst_pair(0b1010100101, simm7, reg, rt, rt2)); } &PairAMode::SPPreIndexed { simm7 } => { @@ -1281,14 +1252,13 @@ impl MachInstEmit for Inst { ref mem, flags, } => { - let rt = allocs.next(rt.to_reg()); - let rt2 = allocs.next(rt2.to_reg()); - let mem = mem.with_allocs(&mut allocs); - let srcloc = state.cur_srcloc(); + let rt = rt.to_reg(); + let rt2 = rt2.to_reg(); + let mem = mem.clone(); - if !srcloc.is_default() && !flags.notrap() { + if let Some(trap_code) = flags.trap_code() { // Register the offset at which the actual load instruction starts. - sink.add_trap(TrapCode::HeapOutOfBounds); + sink.add_trap(trap_code); } let opc = match self { @@ -1300,7 +1270,6 @@ impl MachInstEmit for Inst { match &mem { &PairAMode::SignedOffset { reg, simm7 } => { assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16); - let reg = allocs.next(reg); sink.put4(enc_ldst_vec_pair(opc, 0b10, true, simm7, reg, rt, rt2)); } &PairAMode::SPPreIndexed { simm7 } => { @@ -1327,14 +1296,11 @@ impl MachInstEmit for Inst { ref mem, flags, } => { - let rt = allocs.next(rt); - let rt2 = allocs.next(rt2); - let mem = mem.with_allocs(&mut allocs); - let srcloc = state.cur_srcloc(); + let mem = mem.clone(); - if !srcloc.is_default() && !flags.notrap() { + if let Some(trap_code) = flags.trap_code() { // Register the offset at which the actual store instruction starts. - sink.add_trap(TrapCode::HeapOutOfBounds); + sink.add_trap(trap_code); } let opc = match self { @@ -1346,7 +1312,6 @@ impl MachInstEmit for Inst { match &mem { &PairAMode::SignedOffset { reg, simm7 } => { assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16); - let reg = allocs.next(reg); sink.put4(enc_ldst_vec_pair(opc, 0b10, false, simm7, reg, rt, rt2)); } &PairAMode::SPPreIndexed { simm7 } => { @@ -1362,8 +1327,6 @@ impl MachInstEmit for Inst { } } &Inst::Mov { size, rd, rm } => { - let rd = allocs.next_writable(rd); - let rm = allocs.next(rm); assert!(rd.to_reg().class() == rm.class()); assert!(rm.class() == RegClass::Int); @@ -1398,8 +1361,6 @@ impl MachInstEmit for Inst { } } &Inst::MovFromPReg { rd, rm } => { - let rd = allocs.next_writable(rd); - allocs.next_fixed_nonallocatable(rm); let rm: Reg = rm.into(); debug_assert!([ regs::fp_reg(), @@ -1411,12 +1372,10 @@ impl MachInstEmit for Inst { assert!(rm.class() == RegClass::Int); assert!(rd.to_reg().class() == rm.class()); let size = OperandSize::Size64; - Inst::Mov { size, rd, rm }.emit(&[], sink, emit_info, state); + Inst::Mov { size, rd, rm }.emit(sink, emit_info, state); } &Inst::MovToPReg { rd, rm } => { - allocs.next_fixed_nonallocatable(rd); let rd: Writable = Writable::from_reg(rd.into()); - let rm = allocs.next(rm); debug_assert!([ regs::fp_reg(), regs::stack_reg(), @@ -1427,36 +1386,25 @@ impl MachInstEmit for Inst { assert!(rd.to_reg().class() == RegClass::Int); assert!(rm.class() == rd.to_reg().class()); let size = OperandSize::Size64; - Inst::Mov { size, rd, rm }.emit(&[], sink, emit_info, state); + Inst::Mov { size, rd, rm }.emit(sink, emit_info, state); } &Inst::MovWide { op, rd, imm, size } => { - let rd = allocs.next_writable(rd); sink.put4(enc_move_wide(op, rd, imm, size)); } &Inst::MovK { rd, rn, imm, size } => { - let rn = allocs.next(rn); - let rd = allocs.next_writable(rd); debug_assert_eq!(rn, rd.to_reg()); sink.put4(enc_movk(rd, imm, size)); } &Inst::CSel { rd, rn, rm, cond } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); sink.put4(enc_csel(rd, rn, rm, cond, 0, 0)); } &Inst::CSNeg { rd, rn, rm, cond } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); sink.put4(enc_csel(rd, rn, rm, cond, 1, 1)); } &Inst::CSet { rd, cond } => { - let rd = allocs.next_writable(rd); sink.put4(enc_csel(rd, zero_reg(), zero_reg(), cond.invert(), 0, 1)); } &Inst::CSetm { rd, cond } => { - let rd = allocs.next_writable(rd); sink.put4(enc_csel(rd, zero_reg(), zero_reg(), cond.invert(), 1, 0)); } &Inst::CCmp { @@ -1466,8 +1414,6 @@ impl MachInstEmit for Inst { nzcv, cond, } => { - let rn = allocs.next(rn); - let rm = allocs.next(rm); sink.put4(enc_ccmp(size, rn, rm, nzcv, cond)); } &Inst::CCmpImm { @@ -1477,7 +1423,6 @@ impl MachInstEmit for Inst { nzcv, cond, } => { - let rn = allocs.next(rn); sink.put4(enc_ccmp_imm(size, rn, imm, nzcv, cond)); } &Inst::AtomicRMW { @@ -1488,13 +1433,8 @@ impl MachInstEmit for Inst { rn, flags, } => { - let rs = allocs.next(rs); - let rt = allocs.next_writable(rt); - let rn = allocs.next(rn); - - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { - sink.add_trap(TrapCode::HeapOutOfBounds); + if let Some(trap_code) = flags.trap_code() { + sink.add_trap(trap_code); } sink.put4(enc_acq_rel(ty, op, rs, rt, rn)); @@ -1533,9 +1473,8 @@ impl MachInstEmit for Inst { // again: sink.bind_label(again_label, &mut state.ctrl_plane); - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { - sink.add_trap(TrapCode::HeapOutOfBounds); + if let Some(trap_code) = flags.trap_code() { + sink.add_trap(trap_code); } sink.put4(enc_ldaxr(ty, x27wr, x25)); // ldaxr x27, [x25] @@ -1559,7 +1498,7 @@ impl MachInstEmit for Inst { from_bits, to_bits: size.bits(), } - .emit(&[], sink, emit_info, state); + .emit(sink, emit_info, state); } match op { @@ -1575,7 +1514,7 @@ impl MachInstEmit for Inst { rn: x27, rm: x26, } - .emit(&[], sink, emit_info, state); + .emit(sink, emit_info, state); Inst::AluRRR { alu_op: ALUOp::OrrNot, @@ -1584,7 +1523,7 @@ impl MachInstEmit for Inst { rn: xzr, rm: x28, } - .emit(&[], sink, emit_info, state); + .emit(sink, emit_info, state); } AtomicRMWLoopOp::Umin | AtomicRMWLoopOp::Umax @@ -1611,7 +1550,7 @@ impl MachInstEmit for Inst { rm: x26, extendop, } - .emit(&[], sink, emit_info, state); + .emit(sink, emit_info, state); } else { Inst::AluRRR { alu_op: ALUOp::SubS, @@ -1620,7 +1559,7 @@ impl MachInstEmit for Inst { rn: x27, rm: x26, } - .emit(&[], sink, emit_info, state); + .emit(sink, emit_info, state); } Inst::CSel { @@ -1629,7 +1568,7 @@ impl MachInstEmit for Inst { rn: x27, rm: x26, } - .emit(&[], sink, emit_info, state); + .emit(sink, emit_info, state); } _ => { // add/sub/and/orr/eor x28, x27, x26 @@ -1654,13 +1593,12 @@ impl MachInstEmit for Inst { rn: x27, rm: x26, } - .emit(&[], sink, emit_info, state); + .emit(sink, emit_info, state); } } - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { - sink.add_trap(TrapCode::HeapOutOfBounds); + if let Some(trap_code) = flags.trap_code() { + sink.add_trap(trap_code); } if op == AtomicRMWLoopOp::Xchg { sink.put4(enc_stlxr(ty, x24wr, x26, x25)); // stlxr w24, x26, [x25] @@ -1675,7 +1613,6 @@ impl MachInstEmit for Inst { sink.put4(enc_conditional_br( BranchTarget::Label(again_label), CondBrKind::NotZero(x24), - &mut AllocationConsumer::default(), )); sink.use_label_at_offset(br_offset, again_label, LabelUse::Branch19); } @@ -1687,22 +1624,17 @@ impl MachInstEmit for Inst { ty, flags, } => { - let rd = allocs.next_writable(rd); - let rs = allocs.next(rs); debug_assert_eq!(rd.to_reg(), rs); - let rt = allocs.next(rt); - let rn = allocs.next(rn); let size = match ty { I8 => 0b00, I16 => 0b01, I32 => 0b10, I64 => 0b11, - _ => panic!("Unsupported type: {}", ty), + _ => panic!("Unsupported type: {ty}"), }; - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { - sink.add_trap(TrapCode::HeapOutOfBounds); + if let Some(trap_code) = flags.trap_code() { + sink.add_trap(trap_code); } sink.put4(enc_cas(size, rd, rt, rn)); @@ -1735,9 +1667,8 @@ impl MachInstEmit for Inst { // again: sink.bind_label(again_label, &mut state.ctrl_plane); - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { - sink.add_trap(TrapCode::HeapOutOfBounds); + if let Some(trap_code) = flags.trap_code() { + sink.add_trap(trap_code); } // ldaxr x27, [x25] @@ -1759,13 +1690,11 @@ impl MachInstEmit for Inst { sink.put4(enc_conditional_br( BranchTarget::Label(out_label), CondBrKind::Cond(Cond::Ne), - &mut AllocationConsumer::default(), )); sink.use_label_at_offset(br_out_offset, out_label, LabelUse::Branch19); - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { - sink.add_trap(TrapCode::HeapOutOfBounds); + if let Some(trap_code) = flags.trap_code() { + sink.add_trap(trap_code); } sink.put4(enc_stlxr(ty, x24wr, x28, x25)); // stlxr w24, x28, [x25] @@ -1777,7 +1706,6 @@ impl MachInstEmit for Inst { sink.put4(enc_conditional_br( BranchTarget::Label(again_label), CondBrKind::NotZero(x24), - &mut AllocationConsumer::default(), )); sink.use_label_at_offset(br_again_offset, again_label, LabelUse::Branch19); @@ -1790,12 +1718,8 @@ impl MachInstEmit for Inst { rn, flags, } => { - let rn = allocs.next(rn); - let rt = allocs.next_writable(rt); - - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { - sink.add_trap(TrapCode::HeapOutOfBounds); + if let Some(trap_code) = flags.trap_code() { + sink.add_trap(trap_code); } sink.put4(enc_ldar(access_ty, rt, rn)); @@ -1806,12 +1730,8 @@ impl MachInstEmit for Inst { rn, flags, } => { - let rn = allocs.next(rn); - let rt = allocs.next(rt); - - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { - sink.add_trap(TrapCode::HeapOutOfBounds); + if let Some(trap_code) = flags.trap_code() { + sink.add_trap(trap_code); } sink.put4(enc_stlr(access_ty, rt, rn)); @@ -1822,19 +1742,16 @@ impl MachInstEmit for Inst { &Inst::Csdb {} => { sink.put4(0xd503229f); } + &Inst::FpuMove32 { rd, rn } => { + sink.put4(enc_fpurr(0b000_11110_00_1_000000_10000, rd, rn)); + } &Inst::FpuMove64 { rd, rn } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); sink.put4(enc_fpurr(0b000_11110_01_1_000000_10000, rd, rn)); } &Inst::FpuMove128 { rd, rn } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); sink.put4(enc_vecmov(/* 16b = */ true, rd, rn)); } &Inst::FpuMoveFromVec { rd, rn, idx, size } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (imm5, shift, mask) = match size.lane_size() { ScalarSize::Size32 => (0b00100, 3, 0b011), ScalarSize::Size64 => (0b01000, 4, 0b001), @@ -1850,8 +1767,6 @@ impl MachInstEmit for Inst { ); } &Inst::FpuExtend { rd, rn, size } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); sink.put4(enc_fpurr( 0b000_11110_00_1_000000_10000 | (size.ftype() << 12), rd, @@ -1864,8 +1779,6 @@ impl MachInstEmit for Inst { rd, rn, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let top22 = match fpu_op { FPUOp1::Abs => 0b000_11110_00_1_000001_10000, FPUOp1::Neg => 0b000_11110_00_1_000010_10000, @@ -1889,9 +1802,6 @@ impl MachInstEmit for Inst { rn, rm, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); let top22 = match fpu_op { FPUOp2::Add => 0b000_11110_00_1_00000_001010, FPUOp2::Sub => 0b000_11110_00_1_00000_001110, @@ -1903,34 +1813,27 @@ impl MachInstEmit for Inst { let top22 = top22 | size.ftype() << 12; sink.put4(enc_fpurrr(top22, rd, rn, rm)); } - &Inst::FpuRRI { fpu_op, rd, rn } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - match fpu_op { - FPUOpRI::UShr32(imm) => { - debug_assert_eq!(32, imm.lane_size_in_bits); - sink.put4( - 0b0_0_1_011110_0000000_00_0_0_0_1_00000_00000 - | imm.enc() << 16 - | machreg_to_vec(rn) << 5 - | machreg_to_vec(rd.to_reg()), - ) - } - FPUOpRI::UShr64(imm) => { - debug_assert_eq!(64, imm.lane_size_in_bits); - sink.put4( - 0b01_1_111110_0000000_00_0_0_0_1_00000_00000 - | imm.enc() << 16 - | machreg_to_vec(rn) << 5 - | machreg_to_vec(rd.to_reg()), - ) - } + &Inst::FpuRRI { fpu_op, rd, rn } => match fpu_op { + FPUOpRI::UShr32(imm) => { + debug_assert_eq!(32, imm.lane_size_in_bits); + sink.put4( + 0b0_0_1_011110_0000000_00_0_0_0_1_00000_00000 + | imm.enc() << 16 + | machreg_to_vec(rn) << 5 + | machreg_to_vec(rd.to_reg()), + ) } - } + FPUOpRI::UShr64(imm) => { + debug_assert_eq!(64, imm.lane_size_in_bits); + sink.put4( + 0b01_1_111110_0000000_00_0_0_0_1_00000_00000 + | imm.enc() << 16 + | machreg_to_vec(rn) << 5 + | machreg_to_vec(rd.to_reg()), + ) + } + }, &Inst::FpuRRIMod { fpu_op, rd, ri, rn } => { - let rd = allocs.next_writable(rd); - let ri = allocs.next(ri); - let rn = allocs.next(rn); debug_assert_eq!(rd.to_reg(), ri); match fpu_op { FPUOpRIMod::Sli64(imm) => { @@ -1961,19 +1864,16 @@ impl MachInstEmit for Inst { rm, ra, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); - let ra = allocs.next(ra); let top17 = match fpu_op { FPUOp3::MAdd => 0b000_11111_00_0_00000_0, + FPUOp3::MSub => 0b000_11111_00_0_00000_1, + FPUOp3::NMAdd => 0b000_11111_00_1_00000_0, + FPUOp3::NMSub => 0b000_11111_00_1_00000_1, }; let top17 = top17 | size.ftype() << 7; sink.put4(enc_fpurrrr(top17, rd, rn, rm, ra)); } &Inst::VecMisc { op, rd, rn, size } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (q, enc_size) = size.enc_size(); let (u, bits_12_16, size) = match op { VecMisc2::Not => (0b1, 0b00101, 0b00), @@ -2128,8 +2028,6 @@ impl MachInstEmit for Inst { sink.put4(enc_vec_rr_misc((q << 1) | u, size, bits_12_16, rd, rn)); } &Inst::VecLanes { op, rd, rn, size } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (q, size) = match size { VectorSize::Size8x8 => (0b0, 0b00), VectorSize::Size8x16 => (0b1, 0b00), @@ -2151,8 +2049,6 @@ impl MachInstEmit for Inst { size, imm, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (is_shr, mut template) = match op { VecShiftImmOp::Ushr => (true, 0b_001_011110_0000_000_000001_00000_00000_u32), VecShiftImmOp::Sshr => (true, 0b_000_011110_0000_000_000001_00000_00000_u32), @@ -2182,8 +2078,7 @@ impl MachInstEmit for Inst { (ScalarSize::Size16, false) if imm <= 15 => 0b_0010_000_u32 | imm, (ScalarSize::Size8, false) if imm <= 7 => 0b_0001_000_u32 | imm, _ => panic!( - "aarch64: Inst::VecShiftImm: emit: invalid op/size/imm {:?}, {:?}, {:?}", - op, size, imm + "aarch64: Inst::VecShiftImm: emit: invalid op/size/imm {op:?}, {size:?}, {imm:?}" ), }; let rn_enc = machreg_to_vec(rn); @@ -2198,10 +2093,7 @@ impl MachInstEmit for Inst { size, imm, } => { - let rd = allocs.next_writable(rd); - let ri = allocs.next(ri); debug_assert_eq!(rd.to_reg(), ri); - let rn = allocs.next(rn); let (is_shr, mut template) = match op { VecShiftImmModOp::Sli => (false, 0b_001_011110_0000_000_010101_00000_00000_u32), }; @@ -2229,8 +2121,7 @@ impl MachInstEmit for Inst { (ScalarSize::Size16, false) if imm <= 15 => 0b_0010_000_u32 | imm, (ScalarSize::Size8, false) if imm <= 7 => 0b_0001_000_u32 | imm, _ => panic!( - "aarch64: Inst::VecShiftImmMod: emit: invalid op/size/imm {:?}, {:?}, {:?}", - op, size, imm + "aarch64: Inst::VecShiftImmMod: emit: invalid op/size/imm {op:?}, {size:?}, {imm:?}" ), }; let rn_enc = machreg_to_vec(rn); @@ -2238,9 +2129,6 @@ impl MachInstEmit for Inst { sink.put4(template | (immh_immb << 16) | (rn_enc << 5) | rd_enc); } &Inst::VecExtract { rd, rn, rm, imm4 } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); if imm4 < 16 { let template = 0b_01_101110_000_00000_0_0000_0_00000_00000_u32; let rm_enc = machreg_to_vec(rm); @@ -2250,31 +2138,17 @@ impl MachInstEmit for Inst { template | (rm_enc << 16) | ((imm4 as u32) << 11) | (rn_enc << 5) | rd_enc, ); } else { - panic!( - "aarch64: Inst::VecExtract: emit: invalid extract index {}", - imm4 - ); + panic!("aarch64: Inst::VecExtract: emit: invalid extract index {imm4}"); } } &Inst::VecTbl { rd, rn, rm } => { - let rn = allocs.next(rn); - let rm = allocs.next(rm); - let rd = allocs.next_writable(rd); sink.put4(enc_tbl(/* is_extension = */ false, 0b00, rd, rn, rm)); } &Inst::VecTblExt { rd, ri, rn, rm } => { - let rn = allocs.next(rn); - let rm = allocs.next(rm); - let rd = allocs.next_writable(rd); - let ri = allocs.next(ri); debug_assert_eq!(rd.to_reg(), ri); sink.put4(enc_tbl(/* is_extension = */ true, 0b00, rd, rn, rm)); } &Inst::VecTbl2 { rd, rn, rn2, rm } => { - let rn = allocs.next(rn); - let rn2 = allocs.next(rn2); - let rm = allocs.next(rm); - let rd = allocs.next_writable(rd); assert_eq!(machreg_to_vec(rn2), (machreg_to_vec(rn) + 1) % 32); sink.put4(enc_tbl(/* is_extension = */ false, 0b01, rd, rn, rm)); } @@ -2285,23 +2159,14 @@ impl MachInstEmit for Inst { rn2, rm, } => { - let rn = allocs.next(rn); - let rn2 = allocs.next(rn2); - let rm = allocs.next(rm); - let rd = allocs.next_writable(rd); - let ri = allocs.next(ri); debug_assert_eq!(rd.to_reg(), ri); assert_eq!(machreg_to_vec(rn2), (machreg_to_vec(rn) + 1) % 32); sink.put4(enc_tbl(/* is_extension = */ true, 0b01, rd, rn, rm)); } &Inst::FpuCmp { size, rn, rm } => { - let rn = allocs.next(rn); - let rm = allocs.next(rm); sink.put4(enc_fcmp(size, rn, rm)); } &Inst::FpuToInt { op, rd, rn } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let top16 = match op { // FCVTZS (32/32-bit) FpuToIntOp::F32ToI32 => 0b000_11110_00_1_11_000, @@ -2323,8 +2188,6 @@ impl MachInstEmit for Inst { sink.put4(enc_fputoint(top16, rd, rn)); } &Inst::IntToFpu { op, rd, rn } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let top16 = match op { // SCVTF (32/32-bit) IntToFpuOp::I32ToF32 => 0b000_11110_00_1_00_010, @@ -2345,21 +2208,16 @@ impl MachInstEmit for Inst { }; sink.put4(enc_inttofpu(top16, rd, rn)); } + &Inst::FpuCSel16 { rd, rn, rm, cond } => { + sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size16)); + } &Inst::FpuCSel32 { rd, rn, rm, cond } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size32)); } &Inst::FpuCSel64 { rd, rn, rm, cond } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size64)); } &Inst::FpuRound { op, rd, rn } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let top22 = match op { FpuRoundMode::Minus32 => 0b000_11110_00_1_001_010_10000, FpuRoundMode::Minus64 => 0b000_11110_01_1_001_010_10000, @@ -2373,9 +2231,8 @@ impl MachInstEmit for Inst { sink.put4(enc_fround(top22, rd, rn)); } &Inst::MovToFpu { rd, rn, size } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let template = match size { + ScalarSize::Size16 => 0b000_11110_11_1_00_111_000000_00000_00000, ScalarSize::Size32 => 0b000_11110_00_1_00_111_000000_00000_00000, ScalarSize::Size64 => 0b100_11110_01_1_00_111_000000_00000_00000, _ => unreachable!(), @@ -2383,15 +2240,9 @@ impl MachInstEmit for Inst { sink.put4(template | (machreg_to_gpr(rn) << 5) | machreg_to_vec(rd.to_reg())); } &Inst::FpuMoveFPImm { rd, imm, size } => { - let rd = allocs.next_writable(rd); - let size_code = match size { - ScalarSize::Size32 => 0b00, - ScalarSize::Size64 => 0b01, - _ => unimplemented!(), - }; sink.put4( 0b000_11110_00_1_00_000_000100_00000_00000 - | size_code << 22 + | size.ftype() << 22 | ((imm.enc_bits() as u32) << 13) | machreg_to_vec(rd.to_reg()), ); @@ -2403,10 +2254,7 @@ impl MachInstEmit for Inst { idx, size, } => { - let rd = allocs.next_writable(rd); - let ri = allocs.next(ri); debug_assert_eq!(rd.to_reg(), ri); - let rn = allocs.next(rn); let (imm5, shift) = match size.lane_size() { ScalarSize::Size8 => (0b00001, 1), ScalarSize::Size16 => (0b00010, 2), @@ -2424,14 +2272,12 @@ impl MachInstEmit for Inst { ); } &Inst::MovFromVec { rd, rn, idx, size } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (q, imm5, shift, mask) = match size { ScalarSize::Size8 => (0b0, 0b00001, 1, 0b1111), ScalarSize::Size16 => (0b0, 0b00010, 2, 0b0111), ScalarSize::Size32 => (0b0, 0b00100, 3, 0b0011), ScalarSize::Size64 => (0b1, 0b01000, 4, 0b0001), - _ => panic!("Unexpected scalar FP operand size: {:?}", size), + _ => panic!("Unexpected scalar FP operand size: {size:?}"), }; debug_assert_eq!(idx & mask, idx); let imm5 = imm5 | ((idx as u32) << shift); @@ -2450,8 +2296,6 @@ impl MachInstEmit for Inst { size, scalar_size, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (imm5, shift, half) = match size { VectorSize::Size8x8 => (0b00001, 1, true), VectorSize::Size8x16 => (0b00001, 1, false), @@ -2478,8 +2322,6 @@ impl MachInstEmit for Inst { ); } &Inst::VecDup { rd, rn, size } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let q = size.is_128bits() as u32; let imm5 = match size.lane_size() { ScalarSize::Size8 => 0b00001, @@ -2497,8 +2339,6 @@ impl MachInstEmit for Inst { ); } &Inst::VecDupFromFpu { rd, rn, size, lane } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let q = size.is_128bits() as u32; let imm5 = match size.lane_size() { ScalarSize::Size8 => { @@ -2528,7 +2368,6 @@ impl MachInstEmit for Inst { ); } &Inst::VecDupFPImm { rd, imm, size } => { - let rd = allocs.next_writable(rd); let imm = imm.enc_bits(); let op = match size.lane_size() { ScalarSize::Size32 => 0, @@ -2545,7 +2384,6 @@ impl MachInstEmit for Inst { invert, size, } => { - let rd = allocs.next_writable(rd); let (imm, shift, shift_ones) = imm.value(); let (op, cmode) = match size.lane_size() { ScalarSize::Size8 => { @@ -2594,13 +2432,11 @@ impl MachInstEmit for Inst { high_half, lane_size, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let immh = match lane_size { ScalarSize::Size16 => 0b001, ScalarSize::Size32 => 0b010, ScalarSize::Size64 => 0b100, - _ => panic!("Unexpected VecExtend to lane size of {:?}", lane_size), + _ => panic!("Unexpected VecExtend to lane size of {lane_size:?}"), }; let u = match t { VecExtendOp::Sxtl => 0b0, @@ -2621,8 +2457,6 @@ impl MachInstEmit for Inst { rn, high_half, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (u, size, bits_12_16) = match op { VecRRLongOp::Fcvtl16 => (0b0, 0b00, 0b10111), VecRRLongOp::Fcvtl32 => (0b0, 0b01, 0b10111), @@ -2652,8 +2486,6 @@ impl MachInstEmit for Inst { lane_size, .. } => { - let rn = allocs.next(rn); - let rd = allocs.next_writable(rd); let high_half = match self { &Inst::VecRRNarrowLow { .. } => false, &Inst::VecRRNarrowHigh { .. } => true, @@ -2664,7 +2496,7 @@ impl MachInstEmit for Inst { ScalarSize::Size8 => 0b00, ScalarSize::Size16 => 0b01, ScalarSize::Size32 => 0b10, - _ => panic!("unsupported size: {:?}", lane_size), + _ => panic!("unsupported size: {lane_size:?}"), }; // Floats use a single bit, to encode either half or single. @@ -2697,10 +2529,7 @@ impl MachInstEmit for Inst { src_idx, size, } => { - let rd = allocs.next_writable(rd); - let ri = allocs.next(ri); debug_assert_eq!(rd.to_reg(), ri); - let rn = allocs.next(rn); let (imm5, shift) = match size.lane_size() { ScalarSize::Size8 => (0b00001, 1), ScalarSize::Size16 => (0b00010, 2), @@ -2722,8 +2551,6 @@ impl MachInstEmit for Inst { ); } &Inst::VecRRPair { op, rd, rn } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let bits_12_16 = match op { VecPairOp::Addp => 0b11011, }; @@ -2737,9 +2564,6 @@ impl MachInstEmit for Inst { alu_op, high_half, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); let (u, size, bit14) = match alu_op { VecRRRLongOp::Smull8 => (0b0, 0b00, 0b1), VecRRRLongOp::Smull16 => (0b0, 0b01, 0b1), @@ -2766,11 +2590,7 @@ impl MachInstEmit for Inst { alu_op, high_half, } => { - let rd = allocs.next_writable(rd); - let ri = allocs.next(ri); debug_assert_eq!(rd.to_reg(), ri); - let rn = allocs.next(rn); - let rm = allocs.next(rm); let (u, size, bit14) = match alu_op { VecRRRLongModOp::Umlal8 => (0b1, 0b00, 0b0), VecRRRLongModOp::Umlal16 => (0b1, 0b01, 0b0), @@ -2787,8 +2607,6 @@ impl MachInstEmit for Inst { )); } &Inst::VecRRPairLong { op, rd, rn } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (u, size) = match op { VecRRPairLongOp::Saddlp8 => (0b0, 0b0), VecRRPairLongOp::Uaddlp8 => (0b1, 0b0), @@ -2805,9 +2623,6 @@ impl MachInstEmit for Inst { alu_op, size, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); let (q, enc_size) = size.enc_size(); let is_float = match alu_op { VecALUOp::Fcmeq @@ -2916,11 +2731,7 @@ impl MachInstEmit for Inst { alu_op, size, } => { - let rd = allocs.next_writable(rd); - let ri = allocs.next(ri); debug_assert_eq!(rd.to_reg(), ri); - let rn = allocs.next(rn); - let rm = allocs.next(rm); let (q, _enc_size) = size.enc_size(); let (top11, bit15_10) = match alu_op { @@ -2943,11 +2754,7 @@ impl MachInstEmit for Inst { size, idx, } => { - let rd = allocs.next_writable(rd); - let ri = allocs.next(ri); debug_assert_eq!(rd.to_reg(), ri); - let rn = allocs.next(rn); - let rm = allocs.next(rm); let idx = u32::from(idx); let (q, _size) = size.enc_size(); @@ -2979,22 +2786,16 @@ impl MachInstEmit for Inst { size, flags, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (q, size) = size.enc_size(); - let srcloc = state.cur_srcloc(); - if !srcloc.is_default() && !flags.notrap() { + if let Some(trap_code) = flags.trap_code() { // Register the offset at which the actual load instruction starts. - sink.add_trap(TrapCode::HeapOutOfBounds); + sink.add_trap(trap_code); } sink.put4(enc_ldst_vec(q, size, rn, rd)); } &Inst::VecCSel { rd, rn, rm, cond } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); - let rm = allocs.next(rm); /* Emit this: b.cond else mov rd, rm @@ -3013,7 +2814,6 @@ impl MachInstEmit for Inst { sink.put4(enc_conditional_br( BranchTarget::Label(else_label), CondBrKind::Cond(cond), - &mut AllocationConsumer::default(), )); sink.use_label_at_offset(br_else_offset, else_label, LabelUse::Branch19); @@ -3036,11 +2836,9 @@ impl MachInstEmit for Inst { sink.bind_label(out_label, &mut state.ctrl_plane); } &Inst::MovToNZCV { rn } => { - let rn = allocs.next(rn); sink.put4(0xd51b4200 | machreg_to_gpr(rn)); } &Inst::MovFromNZCV { rd } => { - let rd = allocs.next_writable(rd); sink.put4(0xd53b4200 | machreg_to_gpr(rd.to_reg())); } &Inst::Extend { @@ -3050,8 +2848,6 @@ impl MachInstEmit for Inst { from_bits: 1, to_bits, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); assert!(to_bits <= 64); // Reduce zero-extend-from-1-bit to: // - and rd, rn, #1 @@ -3065,7 +2861,7 @@ impl MachInstEmit for Inst { rn, imml, } - .emit(&[], sink, emit_info, state); + .emit(sink, emit_info, state); } &Inst::Extend { rd, @@ -3074,14 +2870,12 @@ impl MachInstEmit for Inst { from_bits: 32, to_bits: 64, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let mov = Inst::Mov { size: OperandSize::Size32, rd, rm: rn, }; - mov.emit(&[], sink, emit_info, state); + mov.emit(sink, emit_info, state); } &Inst::Extend { rd, @@ -3090,8 +2884,6 @@ impl MachInstEmit for Inst { from_bits, to_bits, } => { - let rd = allocs.next_writable(rd); - let rn = allocs.next(rn); let (opc, size) = if signed { (0b00, OperandSize::from_bits(to_bits)) } else { @@ -3126,57 +2918,59 @@ impl MachInstEmit for Inst { if is_hint { sink.put4(key.enc_auti_hint()); - Inst::Ret {}.emit(&[], sink, emit_info, state); + Inst::Ret {}.emit(sink, emit_info, state); } else { sink.put4(0xd65f0bff | (op2 << 9)); // reta{key} } } &Inst::Call { ref info } => { - if let Some(s) = state.take_stack_map() { - sink.add_stack_map(StackMapExtent::UpcomingBytes(4), s); - } + let user_stack_map = state.take_stack_map(); sink.add_reloc(Reloc::Arm64Call, &info.dest, 0); sink.put4(enc_jump26(0b100101, 0)); - if info.opcode.is_call() { - sink.add_call_site(info.opcode); + if let Some(s) = user_stack_map { + let offset = sink.cur_offset(); + sink.push_user_stack_map(state, offset, s); } + sink.add_call_site(); - let callee_pop_size = i64::from(info.callee_pop_size); - state.virtual_sp_offset -= callee_pop_size; - trace!( - "call adjusts virtual sp offset by {callee_pop_size} -> {}", - state.virtual_sp_offset - ); + if info.callee_pop_size > 0 { + let callee_pop_size = + i32::try_from(info.callee_pop_size).expect("callee popped more than 2GB"); + for inst in AArch64MachineDeps::gen_sp_reg_adjust(-callee_pop_size) { + inst.emit(sink, emit_info, state); + } + } } &Inst::CallInd { ref info } => { - if let Some(s) = state.take_stack_map() { - sink.add_stack_map(StackMapExtent::UpcomingBytes(4), s); - } - let rn = allocs.next(info.rn); + let user_stack_map = state.take_stack_map(); + let rn = info.rn; sink.put4(0b1101011_0001_11111_000000_00000_00000 | (machreg_to_gpr(rn) << 5)); - if info.opcode.is_call() { - sink.add_call_site(info.opcode); + if let Some(s) = user_stack_map { + let offset = sink.cur_offset(); + sink.push_user_stack_map(state, offset, s); } + sink.add_call_site(); - let callee_pop_size = i64::from(info.callee_pop_size); - state.virtual_sp_offset -= callee_pop_size; - trace!( - "call adjusts virtual sp offset by {callee_pop_size} -> {}", - state.virtual_sp_offset - ); + if info.callee_pop_size > 0 { + let callee_pop_size = + i32::try_from(info.callee_pop_size).expect("callee popped more than 2GB"); + for inst in AArch64MachineDeps::gen_sp_reg_adjust(-callee_pop_size) { + inst.emit(sink, emit_info, state); + } + } } &Inst::ReturnCall { ref callee, ref info, } => { - emit_return_call_common_sequence(&mut allocs, sink, emit_info, state, info); + emit_return_call_common_sequence(sink, emit_info, state, info); // Note: this is not `Inst::Jump { .. }.emit(..)` because we // have different metadata in this case: we don't have a label // for the target, but rather a function relocation. sink.add_reloc(Reloc::Arm64Call, &**callee, 0); sink.put4(enc_jump26(0b000101, 0)); - sink.add_call_site(ir::Opcode::ReturnCall); + sink.add_call_site(); // `emit_return_call_common_sequence` emits an island if // necessary, so we can safely disable the worst-case-size check @@ -3184,16 +2978,14 @@ impl MachInstEmit for Inst { start_off = sink.cur_offset(); } &Inst::ReturnCallInd { callee, ref info } => { - let callee = allocs.next(callee); - - emit_return_call_common_sequence(&mut allocs, sink, emit_info, state, info); + emit_return_call_common_sequence(sink, emit_info, state, info); Inst::IndirectBr { rn: callee, targets: vec![], } - .emit(&[], sink, emit_info, state); - sink.add_call_site(ir::Opcode::ReturnCallIndirect); + .emit(sink, emit_info, state); + sink.add_call_site(); // `emit_return_call_common_sequence` emits an island if // necessary, so we can safely disable the worst-case-size check @@ -3209,12 +3001,35 @@ impl MachInstEmit for Inst { let cond_off = sink.cur_offset(); if let Some(l) = taken.as_label() { sink.use_label_at_offset(cond_off, l, LabelUse::Branch19); - let mut allocs_inv = allocs.clone(); + let inverted = enc_conditional_br(taken, kind.invert()).to_le_bytes(); + sink.add_cond_branch(cond_off, cond_off + 4, l, &inverted[..]); + } + sink.put4(enc_conditional_br(taken, kind)); + + // Unconditional part next. + let uncond_off = sink.cur_offset(); + if let Some(l) = not_taken.as_label() { + sink.use_label_at_offset(uncond_off, l, LabelUse::Branch26); + sink.add_uncond_branch(uncond_off, uncond_off + 4, l); + } + sink.put4(enc_jump26(0b000101, not_taken.as_offset26_or_zero())); + } + &Inst::TestBitAndBranch { + taken, + not_taken, + kind, + rn, + bit, + } => { + // Emit the conditional branch first + let cond_off = sink.cur_offset(); + if let Some(l) = taken.as_label() { + sink.use_label_at_offset(cond_off, l, LabelUse::Branch14); let inverted = - enc_conditional_br(taken, kind.invert(), &mut allocs_inv).to_le_bytes(); + enc_test_bit_and_branch(kind.complement(), taken, rn, bit).to_le_bytes(); sink.add_cond_branch(cond_off, cond_off + 4, l, &inverted[..]); } - sink.put4(enc_conditional_br(taken, kind, &mut allocs)); + sink.put4(enc_test_bit_and_branch(kind, taken, rn, bit)); // Unconditional part next. let uncond_off = sink.cur_offset(); @@ -3225,18 +3040,13 @@ impl MachInstEmit for Inst { sink.put4(enc_jump26(0b000101, not_taken.as_offset26_or_zero())); } &Inst::TrapIf { kind, trap_code } => { - let label = sink.defer_trap(trap_code, state.take_stack_map()); + let label = sink.defer_trap(trap_code); // condbr KIND, LABEL let off = sink.cur_offset(); - sink.put4(enc_conditional_br( - BranchTarget::Label(label), - kind, - &mut allocs, - )); + sink.put4(enc_conditional_br(BranchTarget::Label(label), kind)); sink.use_label_at_offset(off, label, LabelUse::Branch19); } &Inst::IndirectBr { rn, .. } => { - let rn = allocs.next(rn); sink.put4(enc_br(rn)); } &Inst::Nop0 => {} @@ -3248,19 +3058,14 @@ impl MachInstEmit for Inst { } &Inst::Udf { trap_code } => { sink.add_trap(trap_code); - if let Some(s) = state.take_stack_map() { - sink.add_stack_map(StackMapExtent::UpcomingBytes(4), s); - } sink.put_data(Inst::TRAP_OPCODE); } &Inst::Adr { rd, off } => { - let rd = allocs.next_writable(rd); assert!(off > -(1 << 20)); assert!(off < (1 << 20)); sink.put4(enc_adr(off, rd)); } &Inst::Adrp { rd, off } => { - let rd = allocs.next_writable(rd); assert!(off > -(1 << 20)); assert!(off < (1 << 20)); sink.put4(enc_adrp(off, rd)); @@ -3279,19 +3084,13 @@ impl MachInstEmit for Inst { ref targets, .. } => { - let ridx = allocs.next(ridx); - let rtmp1 = allocs.next_writable(rtmp1); - let rtmp2 = allocs.next_writable(rtmp2); // This sequence is *one* instruction in the vcode, and is expanded only here at // emission time, because we cannot allow the regalloc to insert spills/reloads in // the middle; we depend on hardcoded PC-rel addressing below. // Branch to default when condition code from prior comparison indicates. - let br = enc_conditional_br( - BranchTarget::Label(default), - CondBrKind::Cond(Cond::Hs), - &mut AllocationConsumer::default(), - ); + let br = + enc_conditional_br(BranchTarget::Label(default), CondBrKind::Cond(Cond::Hs)); // No need to inform the sink's branch folding logic about this branch, because it // will not be merged with any other branch, flipped, or elided (it is not preceded @@ -3309,25 +3108,24 @@ impl MachInstEmit for Inst { rn: zero_reg(), rm: ridx, }; - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); // Prevent any data value speculation. - Inst::Csdb.emit(&[], sink, emit_info, state); + Inst::Csdb.emit(sink, emit_info, state); // Load address of jump table let inst = Inst::Adr { rd: rtmp1, off: 16 }; - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); // Load value out of jump table let inst = Inst::SLoad32 { rd: rtmp2, mem: AMode::reg_plus_reg_scaled_extended( rtmp1.to_reg(), rtmp2.to_reg(), - I32, ExtendOp::UXTW, ), flags: MemFlags::trusted(), }; - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); // Add base of jump table to jump-table-sourced block offset let inst = Inst::AluRRR { alu_op: ALUOp::Add, @@ -3336,14 +3134,14 @@ impl MachInstEmit for Inst { rn: rtmp1.to_reg(), rm: rtmp2.to_reg(), }; - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); // Branch to computed address. (`targets` here is only used for successor queries // and is not needed for emission.) let inst = Inst::IndirectBr { rn: rtmp1.to_reg(), targets: vec![], }; - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); // Emit jump table (table of 32-bit offsets). let jt_off = sink.cur_offset(); for &target in targets.iter() { @@ -3366,8 +3164,6 @@ impl MachInstEmit for Inst { ref name, offset, } => { - let rd = allocs.next_writable(rd); - if emit_info.0.is_pic() { // See this CE Example for the variations of this with and without BTI & PAUTH // https://godbolt.org/z/ncqjbbvvn @@ -3379,7 +3175,7 @@ impl MachInstEmit for Inst { // adrp rd, symbol sink.add_reloc(Reloc::Aarch64AdrGotPage21, &**name, 0); let inst = Inst::Adrp { rd, off: 0 }; - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); // ldr rd, [rd, :got_lo12:X] sink.add_reloc(Reloc::Aarch64Ld64GotLo12Nc, &**name, 0); @@ -3388,7 +3184,7 @@ impl MachInstEmit for Inst { mem: AMode::reg(rd.to_reg()), flags: MemFlags::trusted(), }; - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); } else { // With absolute offsets we set up a load from a preallocated space, and then jump // over it. @@ -3405,37 +3201,36 @@ impl MachInstEmit for Inst { }, flags: MemFlags::trusted(), }; - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); let inst = Inst::Jump { dest: BranchTarget::ResolvedOffset(12), }; - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); sink.add_reloc(Reloc::Abs8, &**name, offset); sink.put8(0); } } &Inst::LoadAddr { rd, ref mem } => { - let rd = allocs.next_writable(rd); - let mem = mem.with_allocs(&mut allocs); - let (mem_insts, mem) = mem_finalize(Some(sink), &mem, state); + let mem = mem.clone(); + let (mem_insts, mem) = mem_finalize(Some(sink), &mem, I8, state); for inst in mem_insts.into_iter() { - inst.emit(&[], sink, emit_info, state); + inst.emit(sink, emit_info, state); } let (reg, index_reg, offset) = match mem { AMode::RegExtended { rn, rm, extendop } => { - let r = allocs.next(rn); + let r = rn; (r, Some((rm, extendop)), 0) } AMode::Unscaled { rn, simm9 } => { - let r = allocs.next(rn); + let r = rn; (r, None, simm9.value()) } AMode::UnsignedOffset { rn, uimm12 } => { - let r = allocs.next(rn); + let r = rn; (r, None, uimm12.value() as i32) } - _ => panic!("Unsupported case for LoadAddr: {:?}", mem), + _ => panic!("Unsupported case for LoadAddr: {mem:?}"), }; let abs_offset = if offset < 0 { -offset as u64 @@ -3454,7 +3249,7 @@ impl MachInstEmit for Inst { extendop, }; - add.emit(&[], sink, emit_info, state); + add.emit(sink, emit_info, state); } else if offset == 0 { if reg != rd.to_reg() { let mov = Inst::Mov { @@ -3463,7 +3258,7 @@ impl MachInstEmit for Inst { rm: reg, }; - mov.emit(&[], sink, emit_info, state); + mov.emit(sink, emit_info, state); } } else if let Some(imm12) = Imm12::maybe_from_u64(abs_offset) { let add = Inst::AluRRImm12 { @@ -3473,7 +3268,7 @@ impl MachInstEmit for Inst { rn: reg, imm12, }; - add.emit(&[], sink, emit_info, state); + add.emit(sink, emit_info, state); } else { // Use `tmp2` here: `reg` may be `spilltmp` if the `AMode` on this instruction // was initially an `SPOffset`. Assert that `tmp2` is truly free to use. Note @@ -3484,7 +3279,7 @@ impl MachInstEmit for Inst { debug_assert!(reg != tmp2_reg()); let tmp = writable_tmp2_reg(); for insn in Inst::load_constant(tmp, abs_offset, &mut |_| tmp).into_iter() { - insn.emit(&[], sink, emit_info, state); + insn.emit(sink, emit_info, state); } let add = Inst::AluRRR { alu_op, @@ -3493,7 +3288,7 @@ impl MachInstEmit for Inst { rn: reg, rm: tmp.to_reg(), }; - add.emit(&[], sink, emit_info, state); + add.emit(sink, emit_info, state); } } &Inst::Paci { key } => { @@ -3517,52 +3312,90 @@ impl MachInstEmit for Inst { sink.put4(0xd503241f | targets << 6); } - &Inst::VirtualSPOffsetAdj { offset } => { - trace!( - "virtual sp offset adjusted by {} -> {}", - offset, - state.virtual_sp_offset + offset, - ); - state.virtual_sp_offset += offset; - } &Inst::EmitIsland { needed_space } => { if sink.island_needed(needed_space + 4) { let jump_around_label = sink.get_label(); let jmp = Inst::Jump { dest: BranchTarget::Label(jump_around_label), }; - jmp.emit(&[], sink, emit_info, state); + jmp.emit(sink, emit_info, state); sink.emit_island(needed_space + 4, &mut state.ctrl_plane); sink.bind_label(jump_around_label, &mut state.ctrl_plane); } } - &Inst::ElfTlsGetAddr { ref symbol, rd } => { - let rd = allocs.next_writable(rd); + &Inst::ElfTlsGetAddr { + ref symbol, + rd, + tmp, + } => { assert_eq!(xreg(0), rd.to_reg()); + // See the original proposal for TLSDESC. + // http://www.fsfla.org/~lxoliva/writeups/TLS/paper-lk2006.pdf + // + // Implement the TLSDESC instruction sequence: + // adrp x0, :tlsdesc:tlsvar + // ldr tmp, [x0, :tlsdesc_lo12:tlsvar] + // add x0, x0, :tlsdesc_lo12:tlsvar + // blr tmp + // mrs tmp, tpidr_el0 + // add x0, x0, tmp + // // This is the instruction sequence that GCC emits for ELF GD TLS Relocations in aarch64 - // See: https://gcc.godbolt.org/z/KhMh5Gvra - - // adrp x0,
+ + + + + + + + + + "# + )?; + for ty in &self.prog.tyenv.types { + writeln!(output, "")?; + writeln!(output, r#""#, id = ty.id().index())?; + + // Name. + writeln!(output, r"", name = ty.name(&self.prog.tyenv))?; + + // Location. + writeln!(output, "", pos = self.pos(ty.pos()))?; + + // Model. + if let Some(model) = self.prog.specenv.type_model.get(&ty.id()) { + // TODO(mbm): link type model to source position + writeln!(output, "")?; + } else { + writeln!(output, "")?; + } + + writeln!(output, "")?; + } + writeln!( + output, + r#" + +
#NameLocationModel
{id}{name}{pos}{model}
+ "# + )?; + + self.footer(&mut output)?; + Ok(()) + } + + fn write_terms(&mut self) -> Result<()> { + let mut output = self.create(&self.terms_dir().join("index.html"))?; + self.header(&mut output, "Terms")?; + + // Terms. + let term_ids = (0..self.prog.termenv.terms.len()).map(TermId); + self.write_terms_list(&mut output, term_ids)?; + + self.footer(&mut output)?; + Ok(()) + } + + fn write_terms_list( + &self, + output: &mut dyn Write, + term_ids: impl Iterator, + ) -> Result<()> { + writeln!( + output, + r#" + + + + + + + + + + + "# + )?; + for term_id in term_ids { + let term = self.prog.term(term_id); + + writeln!(output, "")?; + writeln!(output, r#""#, id = term.id.index())?; + + // Name. + writeln!( + output, + r"", + name = self.prog.term_name(term.id) + )?; + + // Location. + writeln!(output, "", pos = self.pos(term.decl_pos))?; + + // Spec. + if let Some(spec) = self.prog.specenv.term_spec.get(&term.id) { + writeln!(output, "", pos = self.pos(spec.pos))?; + } else if self.chaining.should_chain(term_id) { + writeln!(output, "")?; + } else { + writeln!(output, "")?; + } + + writeln!(output, "")?; + } + writeln!( + output, + r#" + +
#NameLocationSpec
{id}{name}{pos}{pos}chained
+ "# + )?; + Ok(()) + } + + fn write_rules(&mut self) -> Result<()> { + let mut output = self.create(&self.rules_dir().join("index.html"))?; + self.header(&mut output, "Rules")?; + + // Rules. + let rule_ids = (0..self.prog.termenv.rules.len()).map(RuleId); + self.write_rules_list(&mut output, rule_ids)?; + + self.footer(&mut output)?; + Ok(()) + } + + fn write_rules_list( + &self, + output: &mut dyn Write, + rule_ids: impl Iterator, + ) -> Result<()> { + writeln!( + output, + r#" + + + + + + + + + "# + )?; + + for rule_id in rule_ids { + writeln!(output, "")?; + writeln!(output, r#""#, id = rule_id.index())?; + writeln!( + output, + "", + rule_ref = self.rule_ref(rule_id) + )?; + writeln!(output, "")?; + } + + writeln!( + output, + r#" + +
#Identifier
{id}{rule_ref}
+ "# + )?; + Ok(()) + } + + fn write_expansions(&mut self) -> Result<()> { + self.write_expansions_index()?; + for (id, expansion) in self.expansions.iter().enumerate() { + self.write_expansion(id, expansion)?; + } + Ok(()) + } + + fn write_expansions_index(&mut self) -> Result<()> { + let mut output = self.create(&self.expansions_dir().join("index.html"))?; + self.header(&mut output, "Expansions")?; + + // Expansions. + writeln!( + output, + r#" + + + + + + + + + + + "# + )?; + for (id, expansion) in self.expansions.iter().enumerate() { + writeln!(output, "")?; + + // ID + writeln!( + output, + r#""#, + link = self.link(&self.expansion_path(id)) + )?; + + // Root + writeln!( + output, + "", + term_ref = self.term_ref(expansion.term) + )?; + + // First Rule + let rule_id = expansion + .rules + .first() + .expect("expansion must have at least one rule"); + writeln!( + output, + "", + rule_ref = self.rule_ref(*rule_id) + )?; + + // Tags + let mut tags: Vec = expansion.tags(self.prog).iter().cloned().collect(); + tags.sort(); + writeln!(output, "", tags = tags.join(", "))?; + + writeln!(output, "")?; + } + writeln!( + output, + r#" + +
#RootFirst RuleTags
#{id}{term_ref}{rule_ref}{tags}
+ "# + )?; + + self.footer(&mut output)?; + Ok(()) + } + + fn write_expansion(&mut self, id: usize, expansion: &Expansion) -> Result<()> { + self.write_expansion_index(id, expansion)?; + if self.graphs { + self.write_expansion_graph(id, expansion)?; + } + Ok(()) + } + + fn write_expansion_index(&mut self, id: usize, expansion: &Expansion) -> Result<()> { + let mut output = self.create(&self.expansion_path(id))?; + + // Header. + let title = format!("Expansion: #{id}"); + self.header(&mut output, &title)?; + + // Term. + writeln!( + output, + "

Term: {term_ref}

", + term_ref = self.term_ref(expansion.term) + )?; + + // Rules + writeln!(output, "

Rules

")?; + self.write_rules_list(&mut output, expansion.rules.iter().copied())?; + + // Negated Rules + if !expansion.negated.is_empty() { + writeln!(output, "

Negated

")?; + self.write_rules_list(&mut output, expansion.negated.iter().copied())?; + } + + // Terms + writeln!(output, "

Terms

")?; + let terms = expansion.terms(self.prog); + self.write_terms_list(&mut output, terms.into_iter())?; + + // Bindings + writeln!(output, "

Bindings

")?; + if self.graphs { + writeln!( + output, + r#"

Graph: SVG, DOT.

"#, + svg_href = self.link(&self.expansion_graph_svg_path(id)), + dot_href = self.link(&self.expansion_graph_dot_path(id)), + )?; + } + + writeln!( + output, + r#" + + + + + "# + )?; + if !expansion.equals.is_empty() { + writeln!(output, "")?; + } + writeln!( + output, + r#" + + + + + + "# + )?; + let lookup_binding = + |binding_id: BindingId| expansion.bindings[binding_id.index()].clone().unwrap(); + for (i, binding) in expansion.bindings.iter().enumerate() { + let id: BindingId = i.try_into().unwrap(); + if let Some(binding) = binding { + writeln!(output, "")?; + let ty = binding_type(binding, expansion.term, self.prog, lookup_binding); + + // ID + writeln!(output, "", id = id.index())?; + + // Equals + if let Some(eq) = expansion.equals.find(id) { + if id != eq { + write!(output, "", eq.index())?; + } + } + + // Type + writeln!(output, "", ty = self.binding_type(&ty))?; + + // Binding + writeln!( + output, + "", + binding = binding_string(binding, expansion.term, self.prog, lookup_binding) + )?; + + writeln!(output, "")?; + } + } + + // TODO(mbm): Parameters + // TODO(mbm): Result + + writeln!( + output, + r#" + +
#=TypeBinding
{id}= {}{ty}{binding}
+ "# + )?; + + // Constraints + writeln!(output, "

Constraints

")?; + writeln!(output, "
    ")?; + for constrain in &expansion.constraints { + writeln!( + output, + "
  • {constrain}
  • ", + constrain = constrain_string(constrain, &self.prog.tyenv) + )?; + } + writeln!(output, "
")?; + + // Footer. + self.footer(&mut output)?; + + Ok(()) + } + + fn write_expansion_graph(&mut self, id: usize, expansion: &Expansion) -> Result<()> { + self.write_expansion_graph_dot(id, expansion)?; + self.generate_expansion_graph_svg(id)?; + Ok(()) + } + + fn write_expansion_graph_dot(&mut self, id: usize, expansion: &Expansion) -> Result<()> { + let mut output = self.create(&self.expansion_graph_dot_path(id))?; + + // Header. + writeln!(&mut output, "digraph {{")?; + writeln!(&mut output, "\tnode [shape=box, fontname=monospace];")?; + + // Binding nodes. + let lookup_binding = + |binding_id: BindingId| expansion.bindings[binding_id.index()].clone().unwrap(); + for (i, binding) in expansion.bindings.iter().enumerate() { + if let Some(binding) = binding { + writeln!( + &mut output, + "\tb{i} [label=\"{i}: {}\"];", + binding_string(binding, expansion.term, self.prog, lookup_binding) + )?; + } + } + + // Edges. + for (i, binding) in expansion.bindings.iter().enumerate() { + if let Some(binding) = binding { + for source in binding.sources() { + writeln!(&mut output, "\tb{i} -> b{j};", j = source.index())?; + } + } + } + + writeln!(&mut output, "}}")?; + + Ok(()) + } + + fn generate_expansion_graph_svg(&self, id: usize) -> Result<()> { + let dot_path = self.expansion_graph_dot_path(id); + let svg_path = self.expansion_graph_svg_path(id); + + // Invoke graphviz. + let status = std::process::Command::new("dot") + .current_dir(&self.root) + .arg("-Tsvg") + .arg("-o") + .arg(svg_path) + .arg(dot_path) + .status()?; + + if !status.success() { + bail!("dot exit status: {status}"); + } + + Ok(()) + } + + fn header(&self, output: &mut dyn Write, title: &str) -> io::Result<()> { + write!( + output, + r#" + + + + + {title} + + + +
+

{title}

+ "#, + style_path = self.link(&self.style_path()) + ) + } + + fn footer(&self, output: &mut dyn Write) -> io::Result<()> { + write!( + output, + r#" +
+ + + "# + ) + } + + fn binding_type(&self, ty: &BindingType) -> String { + match ty { + BindingType::Base(type_id) => self.type_ref(*type_id), + BindingType::Option(inner) => format!("Option({})", self.binding_type(inner)), + BindingType::Tuple(inners) => format!( + "({inners})", + inners = inners + .iter() + .map(|inner| self.binding_type(inner)) + .collect::>() + .join(", ") + ), + } + } + + fn type_ref(&self, type_id: TypeId) -> String { + let ty = self.prog.ty(type_id); + format!( + r#"{name}"#, + href = self.pos_href(ty.pos()), + name = self.prog.type_name(ty.id()) + ) + } + + fn term_ref(&self, term_id: TermId) -> String { + let term = self.prog.term(term_id); + format!( + r#"{name}"#, + href = self.pos_href(term.decl_pos), + name = self.prog.term_name(term_id) + ) + } + + fn rule_ref(&self, rule_id: RuleId) -> String { + let rule = self.prog.rule(rule_id); + format!( + r#"{identifier}"#, + href = self.pos_href(rule.pos), + identifier = rule.identifier(&self.prog.tyenv, &self.prog.files) + ) + } + + fn pos(&self, pos: Pos) -> String { + if pos.is_unknown() { + "<unknown>".to_string() + } else { + format!( + r#"{loc}"#, + href = self.pos_href(pos), + loc = self.loc(pos) + ) + } + } + + fn loc(&self, pos: Pos) -> String { + let path = PathBuf::from(&self.prog.files.file_names[pos.file]); + format!( + "{}:{}", + path.file_name().unwrap().to_string_lossy(), + self.line(pos) + ) + } + + fn pos_href(&self, pos: Pos) -> String { + format!( + "{}#{}", + self.link(&self.file_path(pos.file)), + self.line_url_fragment(self.line(pos)) + ) + } + + fn line_url_fragment(&self, n: usize) -> String { + format!("L{n}") + } + + fn line(&self, pos: Pos) -> usize { + self.prog + .files + .file_line_map(pos.file) + .unwrap() + .line(pos.offset) + } + + fn types_dir(&self) -> PathBuf { + PathBuf::from("type") + } + + fn terms_dir(&self) -> PathBuf { + PathBuf::from("term") + } + + fn rules_dir(&self) -> PathBuf { + PathBuf::from("rule") + } + + fn expansions_dir(&self) -> PathBuf { + PathBuf::from("expansion") + } + + fn expansion_dir(&self, id: usize) -> PathBuf { + self.expansions_dir().join(id.to_string()) + } + + fn expansion_path(&self, id: usize) -> PathBuf { + self.expansion_dir(id).join("index.html") + } + + fn expansion_graph_dot_path(&self, id: usize) -> PathBuf { + self.expansion_dir(id).join("graph.dot") + } + + fn expansion_graph_svg_path(&self, id: usize) -> PathBuf { + self.expansion_dir(id).join("graph.svg") + } + + fn file_dir(&self) -> PathBuf { + PathBuf::from("file") + } + + fn file_path(&self, id: usize) -> PathBuf { + self.file_dir().join(format!("{id}.html")) + } + + fn assets_dir(&self) -> PathBuf { + PathBuf::from("assets") + } + + fn asset_path(&self, name: &str) -> PathBuf { + self.assets_dir().join(name) + } + + fn style_path(&self) -> PathBuf { + self.asset_path("style.css") + } + + fn abs(&self, path: &Path) -> PathBuf { + self.root.join(path) + } + + fn link(&self, path: &Path) -> String { + assert!(path.is_relative()); + assert!(self.base.is_relative()); + + let mut comps = Vec::new(); + for _ in self.base.components() { + comps.push(Component::ParentDir); + } + comps.extend(path.components()); + let rel: PathBuf = comps.iter().map(|c| c.as_os_str()).collect(); + rel.display().to_string() + } + + fn create(&mut self, path: &Path) -> io::Result { + // Path expected to be relative to site root. + assert!(path.is_relative()); + + // Update base directory for relative links. + self.base = path.parent().expect("should have parent path").into(); + + // Create the file, and any parent directories if necessary. + log::info!("create: {}", path.display()); + let path = self.abs(path); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent)?; + } + File::create(&path) + } +} diff --git a/cranelift/isle/veri/veri/src/lib.rs b/cranelift/isle/veri/veri/src/lib.rs new file mode 100644 index 000000000000..1d58ca862faf --- /dev/null +++ b/cranelift/isle/veri/veri/src/lib.rs @@ -0,0 +1,36 @@ +// TODO(mbm): declare_id is copied from ISLE crate. move it to a common location? +macro_rules! declare_id { + ( + $(#[$attr:meta])* + $name:ident + ) => { + $(#[$attr])* + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $name(pub usize); + impl $name { + /// Get the index of this id. + pub fn index(self) -> usize { + self.0 + } + } + }; +} + +pub mod debug; +pub mod encoded; +pub mod expand; +pub mod explorer; +pub mod program; +pub mod reachability; +pub mod runner; +pub mod solver; +pub mod spec; +pub mod trie; +pub mod type_inference; +pub mod types; +pub mod veri; + +#[cfg(test)] +pub mod testing; + +include!(concat!(env!("OUT_DIR"), "/meta.rs")); diff --git a/cranelift/isle/veri/veri/src/program.rs b/cranelift/isle/veri/veri/src/program.rs new file mode 100644 index 000000000000..920b5b8f8362 --- /dev/null +++ b/cranelift/isle/veri/veri/src/program.rs @@ -0,0 +1,192 @@ +use crate::spec::{self, SpecEnv}; +use crate::trie; +use anyhow::{bail, Result}; +use cranelift_isle::ast::{Def, Ident}; +use cranelift_isle::error::{self, Errors, Span}; +use cranelift_isle::files::Files; +use cranelift_isle::lexer::Pos; +use cranelift_isle::sema::{ + self, Rule, RuleId, Term, TermEnv, TermId, Type, TypeEnv, TypeId, VariantId, +}; +use cranelift_isle::trie_again::{Overlap, RuleSet}; +use cranelift_isle::{lexer, parser}; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +pub struct Program { + pub files: Arc, + pub tyenv: TypeEnv, + pub termenv: TermEnv, + pub specenv: SpecEnv, + pub overlaps: HashMap>, +} + +impl Program { + pub fn from_files( + paths: &Vec, + expand_internal_extractors: bool, + ) -> Result { + let files = match Files::from_paths(paths) { + Ok(files) => files, + Err((path, err)) => { + bail!(Errors::from_io( + err, + format!("cannot read file {}", path.display()), + )) + } + }; + + let files = Arc::new(files); + + let mut defs = Vec::new(); + for (file, src) in files.file_texts.iter().enumerate() { + let lexer = match lexer::Lexer::new(file, src) { + Ok(lexer) => lexer, + Err(err) => bail!(Errors::new(vec![err], files)), + }; + + match parser::parse(lexer, files.clone()) { + Ok(mut ds) => defs.append(&mut ds), + Err(err) => bail!(Errors::new(vec![err], files)), + } + } + + let mut tyenv = match sema::TypeEnv::from_ast(&defs) { + Ok(type_env) => type_env, + Err(errs) => bail!(Errors::new(errs, files)), + }; + + let termenv = match sema::TermEnv::from_ast(&mut tyenv, &defs, expand_internal_extractors) { + Ok(term_env) => term_env, + Err(errs) => bail!(Errors::new(errs, files)), + }; + + let specenv = spec::SpecEnv::from_ast(&defs, &termenv, &tyenv)?; + + let overlaps = Self::build_overlaps(&defs, files.clone())?; + + Ok(Self { + files, + tyenv, + termenv, + specenv, + overlaps, + }) + } + + pub fn ty(&self, type_id: TypeId) -> &Type { + self.tyenv + .types + .get(type_id.index()) + .expect("invalid type id") + } + + pub fn type_name(&self, type_id: TypeId) -> &str { + self.ty(type_id).name(&self.tyenv) + } + + pub fn term(&self, term_id: TermId) -> &Term { + self.termenv + .terms + .get(term_id.index()) + .expect("invalid term id") + } + + pub fn term_name(&self, term_id: TermId) -> &str { + let term = self.term(term_id); + &self.tyenv.syms[term.name.index()] + } + + pub fn get_variant_term(&self, ty: TypeId, variant: VariantId) -> TermId { + self.termenv.get_variant_term(&self.tyenv, ty, variant) + } + + pub fn rule(&self, rule_id: RuleId) -> &Rule { + self.termenv + .rules + .get(rule_id.index()) + .expect("invalid rule id") + } + + pub fn rule_identifer(&self, rule_id: RuleId) -> String { + let rule = self.rule(rule_id); + rule.identifier(&self.tyenv, &self.files) + } + + pub fn rules_by_term(&self) -> HashMap> { + let mut rules: HashMap> = HashMap::new(); + for rule in &self.termenv.rules { + rules.entry(rule.root_term).or_default().push(rule.id); + } + rules + } + + pub fn get_rule_by_identifier(&self, id: &str) -> Option<&Rule> { + self.termenv + .rules + .iter() + .find(|r| r.identifier(&self.tyenv, &self.files) == id) + } + + pub fn get_term_by_name(&self, name: &str) -> Option { + let sym = Ident(name.to_string(), Pos::default()); + self.termenv.get_term_by_name(&self.tyenv, &sym) + } + + pub fn build_trie(&self) -> Result, Errors> { + trie::build_trie(&self.termenv, self.files.clone()) + } + + pub(crate) fn error_at_pos(&self, pos: Pos, msg: impl Into) -> Errors { + // In order to piggy back off the existing diagnostic error reporting in + // ISLE, we shoehorn our error type into one of the existing error + // categories. + // + // TODO(mbm): cleaner positional error reporting for the verifier + let err = error::Error::TypeError { + msg: msg.into(), + span: Span::new_single(pos), + }; + Errors::new(vec![err], self.files.clone()) + } + + fn build_overlaps(defs: &[Def], files: Arc) -> Result>> { + // Overlap checking relies on term environment constructed with internal + // extractor expansion enabled, so we need to generate it again. + let mut tyenv = match sema::TypeEnv::from_ast(defs) { + Ok(type_env) => type_env, + Err(errs) => bail!(Errors::new(errs, files)), + }; + + let expand_internal_extractors = true; + let termenv = match sema::TermEnv::from_ast(&mut tyenv, defs, expand_internal_extractors) { + Ok(term_env) => term_env, + Err(errs) => bail!(Errors::new(errs, files)), + }; + + // Check all pairs of rules for overlap. + let term_rule_sets = trie::build_trie(&termenv, files.clone())?; + let mut overlaps: HashMap> = HashMap::new(); + for (_, rule_set) in &term_rule_sets { + for rule in &rule_set.rules { + for other in &rule_set.rules { + // Ignore same or higher priority rules. + if other.prio <= rule.prio { + continue; + } + + // Check for overlap. + let overlap = rule.may_overlap(other); + if overlap == Overlap::No { + continue; + } + + // Record overlap. + overlaps.entry(rule.id).or_default().insert(other.id); + } + } + } + + Ok(overlaps) + } +} diff --git a/cranelift/isle/veri/veri/src/reachability.rs b/cranelift/isle/veri/veri/src/reachability.rs new file mode 100644 index 000000000000..4f8060f56e31 --- /dev/null +++ b/cranelift/isle/veri/veri/src/reachability.rs @@ -0,0 +1,69 @@ +use std::collections::{HashMap, HashSet}; + +use cranelift_isle::{ + sema::TermId, + trie_again::{Binding, RuleSet}, +}; + +pub struct Reachability { + reachable: HashMap>, +} + +impl Reachability { + pub fn build(term_rule_sets: &HashMap) -> Self { + let mut reachable = HashMap::new(); + for term_id in term_rule_sets.keys() { + reachable.insert(*term_id, search(*term_id, term_rule_sets)); + } + Self { reachable } + } + + /// Set of terms reachable from the the given source. + pub fn reachable(&self, source: TermId) -> &HashSet { + &self.reachable[&source] + } + + /// Report whether the term is included in a cycle. + pub fn is_cyclic(&self, term_id: TermId) -> bool { + self.reachable(term_id).contains(&term_id) + } +} + +/// Search for all terms reachable from the source. +fn search(source: TermId, term_rule_sets: &HashMap) -> HashSet { + let mut reachable = HashSet::new(); + let mut stack = vec![source]; + + while let Some(term_id) = stack.pop() { + if !term_rule_sets.contains_key(&term_id) { + continue; + } + + let used = used_terms(&term_rule_sets[&term_id]); + for used_term_id in used { + if reachable.contains(&used_term_id) { + continue; + } + reachable.insert(used_term_id); + stack.push(used_term_id); + } + } + + reachable +} + +pub fn used_terms(rule_set: &RuleSet) -> HashSet { + rule_set + .bindings + .iter() + .filter_map(binding_used_term) + .collect() +} + +pub fn binding_used_term(binding: &Binding) -> Option { + match binding { + Binding::Constructor { term, .. } | Binding::Extractor { term, .. } => Some(*term), + // TODO(mbm): make variant uses the variant constructor term? + _ => None, + } +} diff --git a/cranelift/isle/veri/veri/src/runner.rs b/cranelift/isle/veri/veri/src/runner.rs new file mode 100644 index 000000000000..e5c61e41d6e5 --- /dev/null +++ b/cranelift/isle/veri/veri/src/runner.rs @@ -0,0 +1,831 @@ +use std::{ + collections::{BTreeSet, HashMap}, + fs::File, + io::Write, + path::{Path, PathBuf}, + str::FromStr, + time::{self, Duration}, +}; + +use anyhow::{bail, format_err, Context as _, Error, Result}; +use cranelift_isle::{ + sema::{Term, TermId}, + trie_again::RuleSet, +}; +use rayon::prelude::*; +use serde::Serialize; + +use crate::{ + debug::print_expansion, + expand::{Chaining, Expander, Expansion}, + program::Program, + solver::{Applicability, Dialect, Solver, Verification}, + type_inference::{self, type_constraint_system, Assignment, Choice}, + veri::Conditions, + BUILD_PROFILE, GIT_VERSION, +}; + +const LOG_DIR: &str = ".veriisle"; + +#[derive(Debug, Clone, Copy)] +pub enum SolverBackend { + Z3, + CVC5, +} + +impl SolverBackend { + fn prog(&self) -> &str { + match self { + SolverBackend::Z3 => "z3", + SolverBackend::CVC5 => "cvc5", + } + } + + fn all() -> Vec { + vec![SolverBackend::Z3, SolverBackend::CVC5] + } + + fn dialect(&self) -> Dialect { + match self { + SolverBackend::Z3 => Dialect::Z3, + SolverBackend::CVC5 => Dialect::SMTLIB2, + } + } + + fn args(&self, timeout: Duration) -> Vec { + match self { + SolverBackend::Z3 => vec![ + "-smt2".to_string(), + "-in".to_string(), + format!("-t:{}", timeout.as_millis()), + ], + SolverBackend::CVC5 => vec![ + "--incremental".to_string(), + "--print-success".to_string(), + format!("--tlimit-per={ms}", ms = timeout.as_millis()), + "-".to_string(), + ], + } + } +} + +impl std::fmt::Display for SolverBackend { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.prog()) + } +} + +impl FromStr for SolverBackend { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + Ok(match s { + "z3" => SolverBackend::Z3, + "cvc5" => SolverBackend::CVC5, + _ => bail!("unknown solver backend"), + }) + } +} + +#[derive(Debug, Clone)] +pub enum ExpansionPredicate { + FirstRuleNamed, + Specified, + Tagged(String), + Root(String), + ContainsRule(String), + Not(Box), + And(Box, Box), +} + +impl FromStr for ExpansionPredicate { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(if let Some((p, q)) = s.split_once(',') { + ExpansionPredicate::And(Box::new(p.parse()?), Box::new(q.parse()?)) + } else if let Some(p) = s.strip_prefix("not:") { + ExpansionPredicate::Not(Box::new(p.parse()?)) + } else if s == "first-rule-named" { + ExpansionPredicate::FirstRuleNamed + } else if s == "specified" { + ExpansionPredicate::Specified + } else if let Some(tag) = s.strip_prefix("tag:") { + ExpansionPredicate::Tagged(tag.to_string()) + } else if let Some(term) = s.strip_prefix("root:") { + ExpansionPredicate::Root(term.to_string()) + } else if let Some(rule) = s.strip_prefix("rule:") { + ExpansionPredicate::ContainsRule(rule.to_string()) + } else { + bail!("invalid expansion predicate") + }) + } +} + +impl std::fmt::Display for ExpansionPredicate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ExpansionPredicate::FirstRuleNamed => write!(f, "first-rule-named"), + ExpansionPredicate::Specified => write!(f, "specified"), + ExpansionPredicate::Tagged(tag) => write!(f, "tag:{tag}"), + ExpansionPredicate::Root(term) => write!(f, "root:{term}"), + ExpansionPredicate::ContainsRule(rule) => write!(f, "rule:{rule}"), + ExpansionPredicate::Not(p) => write!(f, "not:{p}"), + ExpansionPredicate::And(p, q) => write!(f, "{p},{q}"), + } + } +} + +#[derive(Debug, Clone)] +pub struct Filter { + include: bool, + predicate: ExpansionPredicate, +} + +impl Filter { + fn new(include: bool, predicate: ExpansionPredicate) -> Self { + Self { include, predicate } + } + + fn include(predicate: ExpansionPredicate) -> Self { + Self::new(true, predicate) + } + + fn exclude(predicate: ExpansionPredicate) -> Self { + Self::new(false, predicate) + } +} + +impl FromStr for Filter { + type Err = Error; + + fn from_str(s: &str) -> Result { + let (include, p) = if let Some(p) = s.strip_prefix("include:") { + (true, p) + } else if let Some(p) = s.strip_prefix("exclude:") { + (false, p) + } else { + (true, s) + }; + Ok(Filter::new(include, p.parse()?)) + } +} + +impl std::fmt::Display for Filter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let include = if self.include { "include" } else { "exclude" }; + write!( + f, + "{include}:{predicate}", + include = include, + predicate = self.predicate + ) + } +} + +#[derive(Debug, Clone)] +pub struct SolverRule { + predicate: ExpansionPredicate, + solver_backend: SolverBackend, +} + +impl SolverRule { + /// Build a rule that selects the solver backend for expansions with an + /// explicit `solver_` tag. + fn solver_tag(solver_backend: SolverBackend) -> Self { + let tag = format!("solver_{}", solver_backend); + Self { + predicate: ExpansionPredicate::Tagged(tag), + solver_backend, + } + } + + /// Build rules for explicit selection of all solver backends. + fn solver_tag_rules() -> Vec { + SolverBackend::all() + .iter() + .map(|backend| Self::solver_tag(*backend)) + .collect() + } +} + +impl FromStr for SolverRule { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + if let Some((backend, predicate)) = s.split_once('=') { + Ok(Self { + predicate: predicate.parse()?, + solver_backend: backend.parse()?, + }) + } else { + bail!("invalid solver rule") + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "snake_case")] +pub enum Verdict { + Inapplicable, + Success, + Unknown, +} + +#[derive(Serialize)] +pub struct VerifyReport { + pub verdict: Verdict, + + pub init_time: Duration, + pub applicable_time: Duration, + #[serde(skip_serializing_if = "Option::is_none")] + pub verify_time: Option, +} + +#[derive(Serialize)] +pub struct TypeInstantationReport { + pub choices: Vec, + pub verify: VerifyReport, + pub duration: Duration, +} + +#[derive(Serialize)] +pub struct ExpansionReport { + pub id: usize, + pub description: String, + pub root: String, + pub rules: Vec, + pub chained: Vec, + pub terms: Vec, + pub tags: Vec, + pub solver: String, + /// Count of type instantiations that failed at type inference. + pub failed_type_inference: usize, + /// Solver reports from type instantiations. + pub type_instantiations: Vec, + pub duration: Duration, +} + +impl ExpansionReport { + fn from_expansion(id: usize, expansion: &Expansion, prog: &Program) -> Result { + // Description + let description = expansion_description(expansion, prog)?; + + // Root term. + let root = prog.term_name(expansion.term).to_string(); + + // Tags + let mut tags: Vec<_> = expansion.tags(prog).iter().cloned().collect(); + tags.sort(); + + // Rules + let mut rules = Vec::new(); + let mut chained = BTreeSet::new(); + for rule_id in &expansion.rules { + let rule = prog.rule(*rule_id); + rules.push(rule.identifier(&prog.tyenv, &prog.files)); + + if rule.root_term != expansion.term { + let root_term = prog.term_name(rule.root_term); + if !chained.contains(&root_term) { + chained.insert(root_term); + } + } + } + + // Terms + let terms: BTreeSet<_> = expansion + .terms(prog) + .iter() + .map(|term_id| prog.term_name(*term_id)) + .collect(); + + Ok(Self { + id, + root, + description, + rules, + chained: chained.iter().map(ToString::to_string).collect(), + terms: terms.iter().map(ToString::to_string).collect(), + tags, + solver: Default::default(), + failed_type_inference: 0, + type_instantiations: Vec::new(), + duration: Default::default(), + }) + } +} + +#[derive(Serialize)] +pub struct TermMetadata { + pub name: String, + pub class: String, + pub has_spec: bool, + pub tags: Vec, +} + +impl TermMetadata { + fn from_term(term: &Term, prog: &Program) -> Self { + let name = prog.term_name(term.id).to_string(); + let class = Self::classify_term(term); + let has_spec = prog.specenv.has_spec(term.id); + + let tags_set = prog + .specenv + .term_tags + .get(&term.id) + .cloned() + .unwrap_or_default(); + let mut tags: Vec<_> = tags_set.iter().cloned().collect(); + tags.sort(); + + Self { + name, + class, + has_spec, + tags, + } + } + + fn from_prog(prog: &Program) -> Vec { + let mut terms = Vec::new(); + for term in &prog.termenv.terms { + terms.push(Self::from_term(term, prog)); + } + terms + } + + fn classify_term(term: &Term) -> String { + if term.is_enum_variant() { + return "enum_variant".to_string(); + } + + if term.has_external_constructor() || term.has_external_extractor() { + return "external".to_string(); + } + + if term.has_extractor() { + return "extractor".to_string(); + } + + assert!(term.has_constructor()); + + "constructor".to_string() + } +} + +#[derive(Serialize)] +pub struct Report { + build_profile: String, + git_version: String, + args: Vec, + filters: Vec, + default_solver: String, + timeout: Duration, + duration: Duration, + num_threads: usize, + terms: Vec, + expansions: Vec, +} + +/// Runner orchestrates execution of the verification process over a set of +/// expansions. +pub struct Runner { + prog: Program, + term_rule_sets: HashMap, + + root_term: String, + filters: Vec, + default_solver_backend: SolverBackend, + solver_rules: Vec, + timeout: Duration, + log_dir: PathBuf, + skip_solver: bool, + results_to_log_dir: bool, + debug: bool, +} + +impl Runner { + pub fn from_files(inputs: &Vec, root_term: &str) -> Result { + let expand_internal_extractors = false; + let prog = Program::from_files(inputs, expand_internal_extractors)?; + let term_rule_sets: HashMap<_, _> = prog.build_trie()?.into_iter().collect(); + Ok(Self { + prog, + term_rule_sets, + root_term: root_term.to_string(), + filters: Vec::new(), + default_solver_backend: SolverBackend::CVC5, + solver_rules: Vec::new(), + timeout: Duration::from_secs(5), + log_dir: PathBuf::from(LOG_DIR), + results_to_log_dir: false, + skip_solver: false, + debug: false, + }) + } + + pub fn set_root_term(&mut self, term: &str) { + self.root_term = term.to_string(); + } + + pub fn filter(&mut self, filter: Filter) { + self.filters.push(filter); + } + + pub fn filters(&mut self, filters: &[Filter]) { + self.filters.extend(filters.iter().cloned()); + } + + pub fn include_first_rule_named(&mut self) { + self.filters + .push(Filter::include(ExpansionPredicate::FirstRuleNamed)); + } + + pub fn skip_tag(&mut self, tag: &str) { + self.filters + .push(Filter::exclude(ExpansionPredicate::Tagged(tag.to_string()))); + } + + pub fn target_rule(&mut self, id: &str) -> Result<()> { + self.filters + .push(Filter::include(ExpansionPredicate::ContainsRule( + id.to_string(), + ))); + Ok(()) + } + + // Configure the default solver to use if no solver rules apply. + pub fn set_default_solver_backend(&mut self, solver_backend: SolverBackend) { + self.default_solver_backend = solver_backend; + } + + // Use the given solver backend for expansions that satisfy the given + // predicate. If multiple rules match, the earlier one is used. If none + // match, the default is used. + pub fn add_solver_rule(&mut self, solver_rule: SolverRule) { + self.solver_rules.push(solver_rule); + } + + // Configure rules for explicit solver selection based on `solver_` tags. + pub fn add_solver_tag_rules(&mut self) { + self.solver_rules.extend(SolverRule::solver_tag_rules()); + } + + pub fn set_timeout(&mut self, timeout: Duration) { + self.timeout = timeout; + } + + pub fn set_log_dir(&mut self, path: PathBuf) { + self.log_dir = path; + } + + pub fn set_results_to_log_dir(&mut self, enabled: bool) { + self.results_to_log_dir = enabled; + } + + pub fn skip_solver(&mut self, skip: bool) { + self.skip_solver = skip; + } + + pub fn debug(&mut self, debug: bool) { + self.debug = debug; + } + + pub fn run(&self) -> Result<()> { + // Clean log directory. + if self.log_dir.exists() { + std::fs::remove_dir_all(&self.log_dir)?; + } + + // Start timer. + let num_threads = rayon::current_num_threads(); + let start = time::Instant::now(); + + // Generate expansions. + // TODO(mbm): don't hardcode the expansion configuration + let chaining = Chaining::new(&self.prog, &self.term_rule_sets)?; + chaining.validate()?; + let mut expander = Expander::new(&self.prog, &self.term_rule_sets, chaining); + expander.add_root_term_name(&self.root_term)?; + expander.set_prune_infeasible(true); + expander.expand(); + + // Process expansions. + let expansions = expander.expansions(); + log::info!("expansions: {n}", n = expansions.len()); + + let mut expansion_reports = expansions + .par_iter() + .enumerate() + .map(|(i, expansion)| -> Result> { + // Skip? + if !self.should_verify(expansion)? { + return Ok(None); + } + + // Verify + let expansion_log_dir = self.log_dir.join("expansions").join(format!("{:05}", i)); + let report = self.verify_expansion(expansion, i, expansion_log_dir.clone())?; + + Ok(Some(report)) + }) + .collect::>>()? + .into_iter() + .flatten() + .collect::>(); + + // End timer. + let duration = start.elapsed(); + + // Prepare report + expansion_reports.sort_by(|a, b| a.id.cmp(&b.id)); + let terms = TermMetadata::from_prog(&self.prog); + let report = Report { + build_profile: BUILD_PROFILE.to_string(), + git_version: GIT_VERSION.to_string(), + args: std::env::args().collect(), + filters: self.filters.iter().map(ToString::to_string).collect(), + default_solver: self.default_solver_backend.prog().to_string(), + timeout: self.timeout, + num_threads, + duration, + terms, + expansions: expansion_reports, + }; + + // Write + let output = Self::open_log_file(self.log_dir.clone(), "report.json")?; + serde_json::to_writer_pretty(output, &report)?; + + Ok(()) + } + + fn should_verify(&self, expansion: &Expansion) -> Result { + let mut verdict = None; + for filter in self.filters.iter() { + verdict = self.eval_filter(filter, expansion)?.or(verdict); + } + Ok(verdict.unwrap_or(false)) + } + + fn eval_filter(&self, filter: &Filter, expansion: &Expansion) -> Result> { + Ok(if self.eval_predicate(&filter.predicate, expansion)? { + Some(filter.include) + } else { + None + }) + } + + fn eval_predicate( + &self, + predicate: &ExpansionPredicate, + expansion: &Expansion, + ) -> Result { + Ok(match predicate { + ExpansionPredicate::FirstRuleNamed => { + let rule_id = expansion + .rules + .first() + .ok_or(format_err!("expansion should have at least one rule"))?; + let rule = self.prog.rule(*rule_id); + rule.name.is_some() + } + ExpansionPredicate::Specified => expansion + .terms(&self.prog) + .iter() + .all(|term_id| self.prog.specenv.has_spec(*term_id)), + ExpansionPredicate::Tagged(tag) => { + let tags = expansion.tags(&self.prog); + tags.contains(tag) + } + ExpansionPredicate::Root(term) => self.prog.term_name(expansion.term) == term, + ExpansionPredicate::ContainsRule(identifier) => { + let rule = self + .prog + .get_rule_by_identifier(&identifier) + .ok_or(format_err!("unknown rule '{identifier}'"))?; + expansion.rules.contains(&rule.id) + } + ExpansionPredicate::Not(p) => !self.eval_predicate(p, expansion)?, + ExpansionPredicate::And(p, q) => { + self.eval_predicate(p, expansion)? && self.eval_predicate(q, expansion)? + } + }) + } + + fn verify_expansion( + &self, + expansion: &Expansion, + id: usize, + log_dir: std::path::PathBuf, + ) -> Result { + let description = expansion_description(expansion, &self.prog)?; + let start = time::Instant::now(); + + // Results output. + let mut output: Box = if self.results_to_log_dir { + log::info!("#{id}\t{description}"); + Box::new(Self::open_log_file(log_dir.clone(), "results.out")?) + } else { + Box::new(std::io::stdout()) + }; + + writeln!(output, "#{id}\t{description}")?; + if self.debug { + print_expansion(&self.prog, expansion); + } + + // Verification conditions. + let conditions = Conditions::from_expansion(expansion, &self.prog)?; + if self.debug { + conditions.pretty_print(&self.prog); + } + + // Type constraints. + let system = type_constraint_system(&conditions); + if self.debug { + system.pretty_print(); + } + + // Infer types. + let type_solver = type_inference::Solver::new(); + let solutions = type_solver.solve(&system); + + // Initialize report. + let mut report = ExpansionReport::from_expansion(id, expansion, &self.prog)?; + + // Select solver. + let solver_backend = self.select_solver_backend(expansion)?; + report.solver = solver_backend.to_string(); + + for (i, solution) in solutions.iter().enumerate() { + let start_solution = time::Instant::now(); + + // Show type assignment. + let mut choices = Vec::new(); + for choice in &solution.choices { + let choice = match choice { + Choice::TermInstantiation(term_id, sig) => { + format!("{term}{sig}", term = self.prog.term_name(*term_id)) + } + }; + writeln!(output, "\t{choice}")?; + choices.push(choice); + } + writeln!(output, "\t\ttype solution status = {}", solution.status)?; + if self.debug { + println!("type assignment:"); + solution.assignment.pretty_print(&conditions); + } + + match &solution.status { + type_inference::Status::Solved => (), + type_inference::Status::Inapplicable(conflict) => { + log::debug!( + "inapplicable type inference: {diagnostic}", + diagnostic = conflict.diagnostic(&conditions, &self.prog.files) + ); + report.failed_type_inference += 1; + continue; + } + type_inference::Status::Underconstrained => { + bail!("underconstrained type inference") + } + type_inference::Status::TypeError(confict) => { + return Err(conditions.error_at_expr( + &self.prog, + confict.x, + confict.reason.clone(), + )); + } + } + + // Verify. + if self.skip_solver { + println!("skip solver"); + continue; + } + + let solution_log_dir = log_dir.join(format!("{:03}", i)); + let verify_report = self + .verify_expansion_type_instantiation( + &conditions, + &solution.assignment, + solver_backend, + solution_log_dir, + &mut output, + ) + .context(format!("verify expansion: {id}"))?; + + // Append to report. + let duration = start_solution.elapsed(); + report.type_instantiations.push(TypeInstantationReport { + choices, + verify: verify_report, + duration, + }); + } + + // End timer + report.duration = start.elapsed(); + + Ok(report) + } + + fn select_solver_backend(&self, expansion: &Expansion) -> Result { + for solver_rule in &self.solver_rules { + if self.eval_predicate(&solver_rule.predicate, expansion)? { + return Ok(solver_rule.solver_backend); + } + } + Ok(self.default_solver_backend) + } + + fn verify_expansion_type_instantiation( + &self, + conditions: &Conditions, + assignment: &Assignment, + solver_backend: SolverBackend, + log_dir: std::path::PathBuf, + output: &mut dyn Write, + ) -> Result { + let start = time::Instant::now(); + + // Solve. + let binary = solver_backend.prog(); + let args = solver_backend.args(self.timeout); + let replay_file = Self::open_log_file(log_dir, "solver.smt2")?; + let smt = easy_smt::ContextBuilder::new() + .solver(binary, &args) + .replay_file(Some(replay_file)) + .build()?; + + let mut solver = Solver::new(smt, &self.prog, conditions, assignment)?; + solver.set_dialect(solver_backend.dialect()); + solver.encode()?; + let init_time = start.elapsed(); + + // Applicability check. + let start = time::Instant::now(); + let applicability = solver.check_assumptions_feasibility()?; + let applicable_time = start.elapsed(); + + writeln!(output, "\t\tapplicability = {applicability}")?; + match applicability { + Applicability::Applicable => (), + Applicability::Inapplicable => { + return Ok(VerifyReport { + verdict: Verdict::Inapplicable, + init_time, + applicable_time, + verify_time: None, + }) + } + Applicability::Unknown => bail!("could not prove applicability"), + }; + + // Verify. + let start = time::Instant::now(); + let verification = solver.check_verification_condition()?; + let verify_time = Some(start.elapsed()); + + writeln!(output, "\t\tverification = {verification}")?; + Ok(match verification { + Verification::Failure(model) => { + println!("model:"); + conditions.print_model(&model, &self.prog)?; + bail!("verification failed"); + } + Verification::Success => VerifyReport { + verdict: Verdict::Success, + init_time, + applicable_time, + verify_time, + }, + Verification::Unknown => VerifyReport { + verdict: Verdict::Unknown, + init_time, + applicable_time, + verify_time, + }, + }) + } + + fn open_log_file>(log_dir: std::path::PathBuf, name: P) -> Result { + std::fs::create_dir_all(&log_dir)?; + let path = log_dir.join(name); + let file = File::create(&path)?; + Ok(file) + } +} + +/// Human-readable description of an expansion. +fn expansion_description(expansion: &Expansion, prog: &Program) -> Result { + let rule_id = expansion + .rules + .first() + .ok_or(format_err!("expansion should have at least one rule"))?; + let rule = prog.rule(*rule_id); + Ok(rule.identifier(&prog.tyenv, &prog.files)) +} diff --git a/cranelift/isle/veri/veri/src/solver.rs b/cranelift/isle/veri/veri/src/solver.rs new file mode 100644 index 000000000000..41aec640f203 --- /dev/null +++ b/cranelift/isle/veri/veri/src/solver.rs @@ -0,0 +1,1006 @@ +use std::{cmp::Ordering, iter::zip}; + +use anyhow::{bail, format_err, Context as _, Error, Result}; +use easy_smt::{Context, Response, SExpr, SExprData}; +use num_bigint::BigUint; +use num_traits::Num as _; + +use crate::{ + program::Program, + type_inference::Assignment, + types::{Const, Type, Width}, + veri::{Conditions, Expr, ExprId, Model}, +}; + +use crate::encoded::cls::*; +use crate::encoded::clz::*; +use crate::encoded::popcnt::*; +use crate::encoded::rev::*; + +#[derive(Debug, PartialEq, Eq)] +pub enum Applicability { + Applicable, + Inapplicable, + Unknown, +} + +impl std::fmt::Display for Applicability { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(match self { + Applicability::Applicable => "applicable", + Applicability::Inapplicable => "inapplicable", + Applicability::Unknown => "unknown", + }) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Verification { + Success, + Failure(Model), + Unknown, +} + +impl std::fmt::Display for Verification { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(match self { + Verification::Success => "success", + Verification::Failure(_) => "failure", + Verification::Unknown => "unknown", + }) + } +} + +enum RotationDirection { + Left, + Right, +} + +static UNSPECIFIED_SORT: &str = "Unspecified"; +static UNIT_SORT: &str = "Unit"; + +static ROUND_NEAREST_TIES_TO_EVEN: &str = "roundNearestTiesToEven"; +static ROUND_TOWARD_ZERO: &str = "roundTowardZero"; +static ROUND_TOWARD_POSITIVE: &str = "roundTowardPositive"; +static ROUND_TOWARD_NEGATIVE: &str = "roundTowardNegative"; +static ROUNDING_MODE: &str = ROUND_NEAREST_TIES_TO_EVEN; + +/// SMT Dialect. +#[derive(Default, Debug, Clone, Copy)] +pub enum Dialect { + /// SMT-LIB2 standard. + #[default] + SMTLIB2, + /// SMT-LIB2 with Z3 extensions. + Z3, +} + +pub struct Solver<'a> { + smt: Context, + dialect: Dialect, + prog: &'a Program, + conditions: &'a Conditions, + assignment: &'a Assignment, + tmp_idx: usize, +} + +impl Drop for Solver<'_> { + fn drop(&mut self) { + // Attempt clean exit. + let _ = self.exit(); + } +} + +impl<'a> Solver<'a> { + pub fn new( + smt: Context, + prog: &'a Program, + conditions: &'a Conditions, + assignment: &'a Assignment, + ) -> Result { + let mut solver = Self { + smt, + dialect: Dialect::default(), + prog, + conditions, + assignment, + tmp_idx: 0, + }; + solver.prelude()?; + Ok(solver) + } + + pub fn set_dialect(&mut self, dialect: Dialect) { + self.dialect = dialect; + } + + fn prelude(&mut self) -> Result<()> { + // Set logic. Required for some SMT solvers. + self.smt.set_logic("ALL")?; + + // Declare sorts for special-case types. + self.smt.declare_sort(UNSPECIFIED_SORT, 0)?; + self.smt.declare_sort(UNIT_SORT, 0)?; + + Ok(()) + } + + pub fn encode(&mut self) -> Result<()> { + // Expressions + for (i, expr) in self.conditions.exprs.iter().enumerate() { + let x = ExprId(i); + self.declare_expr(x)?; + if !expr.is_variable() { + self.assign_expr(x, expr)?; + } + } + + Ok(()) + } + + pub fn check_assumptions_feasibility(&mut self) -> Result { + // Enter solver context frame. + self.smt.push()?; + + // Assumptions + let assumptions = self.all(&self.conditions.assumptions); + self.smt.assert(assumptions)?; + + // Check + let verdict = match self.check()? { + Response::Sat => Applicability::Applicable, + Response::Unsat => Applicability::Inapplicable, + Response::Unknown => Applicability::Unknown, + }; + + // Leave solver context frame. + self.smt.pop()?; + + Ok(verdict) + } + + pub fn check_verification_condition(&mut self) -> Result { + // Enter solver context frame. + self.smt.push()?; + + // Verification Condition + self.verification_condition()?; + + // Check + let verdict = match self.check()? { + Response::Sat => Verification::Failure(self.model()?), + Response::Unsat => Verification::Success, + Response::Unknown => Verification::Unknown, + }; + + // Leave solver context frame. + self.smt.pop()?; + + Ok(verdict) + } + + fn check(&mut self) -> Result { + // Send check-sat command. Prefer (check-sat-using default) for Z3. + let cmd = self.smt.list(match self.dialect { + Dialect::SMTLIB2 => vec![self.smt.atoms().check_sat], + Dialect::Z3 => vec![self.smt.atom("check-sat-using"), self.smt.atom("default")], + }); + + self.smt.raw_send(cmd)?; + + // Parse response. + let resp = self.smt.raw_recv()?; + let atoms = self.smt.atoms(); + if resp == atoms.sat { + Ok(Response::Sat) + } else if resp == atoms.unsat { + Ok(Response::Unsat) + } else if resp == atoms.unknown { + Ok(Response::Unknown) + } else { + bail!("bad solver check response: {}", self.smt.display(resp)) + } + } + + pub fn exit(&mut self) -> Result<()> { + // Send (exit) command. + let exit = self.smt.list(vec![self.smt.atom("exit")]); + self.smt.raw_send(exit)?; + + // Expect success response. + let resp = self.smt.raw_recv()?; + let atoms = self.smt.atoms(); + if resp != atoms.success { + bail!("bad solver exit: {}", self.smt.display(resp)) + } + Ok(()) + } + + pub fn model(&mut self) -> Result { + let xs: Vec<_> = (0..self.conditions.exprs.len()).map(ExprId).collect(); + let expr_atoms = xs.iter().map(|x| self.expr_atom(*x)).collect(); + let values = self.smt.get_value(expr_atoms)?; + let consts = values + .iter() + .map(|(_, v)| self.const_from_sexpr(*v)) + .collect::>>()?; + Ok(zip(xs, consts).collect()) + } + + fn declare_expr(&mut self, x: ExprId) -> Result<()> { + // Determine expression type value. + let tv = self.assignment.try_assignment(x)?; + + // Map to corresponding SMT2 type. + let sort = self.type_to_sort(&tv.ty())?; + + // Declare. + self.smt.declare_const(self.expr_name(x), sort)?; + + Ok(()) + } + + fn type_to_sort(&self, ty: &Type) -> Result { + match *ty { + Type::BitVector(Width::Bits(width)) => { + Ok(self.smt.bit_vec_sort(self.smt.numeral(width))) + } + Type::Int => Ok(self.smt.int_sort()), + Type::Bool => Ok(self.smt.bool_sort()), + Type::Unspecified => Ok(self.smt.atom(UNSPECIFIED_SORT)), + Type::Unit => Ok(self.smt.atom(UNIT_SORT)), + Type::Unknown | Type::BitVector(Width::Unknown) => { + bail!("no smt2 sort for non-concrete type {ty}") + } + } + } + + fn assign_expr(&mut self, x: ExprId, expr: &Expr) -> Result<()> { + let lhs = self.smt.atom(self.expr_name(x)); + let rhs = self + .expr_to_smt(expr) + .map_err(|err| self.error(x, err.to_string()))?; + Ok(self.smt.assert( + self.smt + .named(format!("expr{}", x.index()), self.smt.eq(lhs, rhs)), + )?) + } + + fn expr_to_smt(&mut self, expr: &Expr) -> Result { + match *expr { + Expr::Variable(_) => unreachable!("variables have no corresponding expression"), + Expr::Const(ref c) => Ok(self.constant(c)), + Expr::Not(x) => Ok(self.smt.not(self.expr_atom(x))), + Expr::And(x, y) => Ok(self.smt.and(self.expr_atom(x), self.expr_atom(y))), + Expr::Or(x, y) => Ok(self.smt.or(self.expr_atom(x), self.expr_atom(y))), + Expr::Imp(x, y) => Ok(self.smt.imp(self.expr_atom(x), self.expr_atom(y))), + Expr::Eq(x, y) => Ok(self.smt.eq(self.expr_atom(x), self.expr_atom(y))), + Expr::Lt(x, y) => Ok(self.smt.lt(self.expr_atom(x), self.expr_atom(y))), + Expr::Lte(x, y) => Ok(self.smt.lte(self.expr_atom(x), self.expr_atom(y))), + Expr::BVUgt(x, y) => Ok(self.smt.bvugt(self.expr_atom(x), self.expr_atom(y))), + Expr::BVUge(x, y) => Ok(self.smt.bvuge(self.expr_atom(x), self.expr_atom(y))), + Expr::BVUlt(x, y) => Ok(self.smt.bvult(self.expr_atom(x), self.expr_atom(y))), + Expr::BVUle(x, y) => Ok(self.smt.bvule(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSgt(x, y) => Ok(self.smt.bvsgt(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSge(x, y) => Ok(self.smt.bvsge(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSlt(x, y) => Ok(self.smt.bvslt(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSle(x, y) => Ok(self.smt.bvsle(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSaddo(x, y) => Ok(self.smt.list(vec![ + self.smt.atom("bvsaddo"), + self.expr_atom(x), + self.expr_atom(y), + ])), + Expr::BVNot(x) => Ok(self.smt.bvnot(self.expr_atom(x))), + Expr::Cls(x) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("cls semantics require known width")?; + let xe = self.expr_atom(x); + let id = x.index(); + match width { + 8 => Ok(cls8(&mut self.smt, xe, id)), + 16 => Ok(cls16(&mut self.smt, xe, id)), + 32 => Ok(cls32(&mut self.smt, xe, id)), + 64 => Ok(cls64(&mut self.smt, xe, id)), + _ => unimplemented!("unexpected CLS width"), + } + } + Expr::Clz(x) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("clz semantics require known width")?; + let xe = self.expr_atom(x); + let id: usize = x.index(); + match width { + 1 => Ok(clz1(&mut self.smt, xe, id)), + 8 => Ok(clz8(&mut self.smt, xe, id)), + 16 => Ok(clz16(&mut self.smt, xe, id)), + 32 => Ok(clz32(&mut self.smt, xe, id)), + 64 => Ok(clz64(&mut self.smt, xe, id)), + _ => unimplemented!("unexpected CLZ width"), + } + } + Expr::Rev(x) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("cls semantics require known width")?; + let xe = self.expr_atom(x); + let id: usize = x.index(); + match width { + 1 => Ok(rev1(&mut self.smt, xe, id)), + 8 => Ok(rev8(&mut self.smt, xe, id)), + 16 => Ok(rev16(&mut self.smt, xe, id)), + 32 => Ok(rev32(&mut self.smt, xe, id)), + 64 => Ok(rev64(&mut self.smt, xe, id)), + _ => unimplemented!("unexpected CLS width"), + } + } + Expr::Popcnt(x) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("popcnt semantics require known width")?; + let xe = self.expr_atom(x); + let id = x.index(); + match width { + 8 | 16 | 32 | 64 => Ok(popcnt(&mut self.smt, width, xe, id)), + _ => unimplemented!("unexpected Popcnt width"), + } + } + + Expr::Add(x, y) => Ok(self.smt.plus(self.expr_atom(x), self.expr_atom(y))), + Expr::Sub(x, y) => Ok(self.smt.sub(self.expr_atom(x), self.expr_atom(y))), + Expr::Mul(x, y) => Ok(self.smt.times(self.expr_atom(x), self.expr_atom(y))), + + Expr::BVNeg(x) => Ok(self.smt.bvneg(self.expr_atom(x))), + Expr::BVAdd(x, y) => Ok(self.smt.bvadd(self.expr_atom(x), self.expr_atom(y))), + Expr::BVOr(x, y) => Ok(self.smt.bvor(self.expr_atom(x), self.expr_atom(y))), + Expr::BVXor(x, y) => Ok(self.smt.bvxor(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSub(x, y) => Ok(self.smt.bvsub(self.expr_atom(x), self.expr_atom(y))), + Expr::BVMul(x, y) => Ok(self.smt.bvmul(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSDiv(x, y) => Ok(self.smt.list(vec![ + self.smt.atom("bvsdiv"), + self.expr_atom(x), + self.expr_atom(y), + ])), + Expr::BVUDiv(x, y) => Ok(self.smt.bvudiv(self.expr_atom(x), self.expr_atom(y))), + Expr::BVSRem(x, y) => Ok(self.smt.bvsrem(self.expr_atom(x), self.expr_atom(y))), + Expr::BVURem(x, y) => Ok(self.smt.bvurem(self.expr_atom(x), self.expr_atom(y))), + Expr::BVAnd(x, y) => Ok(self.smt.bvand(self.expr_atom(x), self.expr_atom(y))), + Expr::BVShl(x, y) => Ok(self.smt.bvshl(self.expr_atom(x), self.expr_atom(y))), + Expr::BVLShr(x, y) => Ok(self.smt.bvlshr(self.expr_atom(x), self.expr_atom(y))), + Expr::BVAShr(x, y) => Ok(self.smt.bvashr(self.expr_atom(x), self.expr_atom(y))), + Expr::BVRotl(x, y) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("target of rotl expression should be a bit-vector of known width")?; + let xs = self.expr_atom(x); + let ys = self.expr_atom(y); + Ok(self.encode_rotate(RotationDirection::Left, xs, ys, width)) + } + Expr::BVRotr(x, y) => { + let width = self + .assignment + .try_bit_vector_width(x) + .context("target of rotr expression should be a bit-vector of known width")?; + let xs = self.expr_atom(x); + let ys = self.expr_atom(y); + Ok(self.encode_rotate(RotationDirection::Right, xs, ys, width)) + } + Expr::Conditional(c, t, e) => { + Ok(self + .smt + .ite(self.expr_atom(c), self.expr_atom(t), self.expr_atom(e))) + } + Expr::BVZeroExt(w, x) => self.bv_zero_ext(w, x), + Expr::BVSignExt(w, x) => self.bv_sign_ext(w, x), + Expr::BVConvTo(w, x) => self.bv_conv_to(w, x), + Expr::BVExtract(h, l, x) => Ok(self.extract(h, l, self.expr_atom(x))), + Expr::BVConcat(x, y) => Ok(self.smt.concat(self.expr_atom(x), self.expr_atom(y))), + Expr::Int2BV(w, x) => self.int_to_bv(w, x), + Expr::BV2Nat(x) => Ok(self + .smt + .list(vec![self.smt.atom("bv2nat"), self.expr_atom(x)])), + Expr::ToFP(w, x) => self.to_fp_from_expr(w, x, true), + Expr::ToFPUnsigned(w, x) => self.to_fp_from_expr(w, x, false), + Expr::ToFPFromFP(w, x) => self.to_fp_from_fp(w, x), + Expr::FPToUBV(w, x) => self.fp_to_bv(w, x, false), + Expr::FPToSBV(w, x) => self.fp_to_bv(w, x, true), + Expr::WidthOf(x) => self.width_of(x), + Expr::FPPositiveInfinity(x) => Ok(self.fp_value("+oo", x)?), + Expr::FPNegativeInfinity(x) => Ok(self.fp_value("-oo", x)?), + Expr::FPPositiveZero(x) => Ok(self.fp_value("+zero", x)?), + Expr::FPNegativeZero(x) => Ok(self.fp_value("-zero", x)?), + Expr::FPNaN(x) => Ok(self.fp_value("NaN", x)?), + Expr::FPEq(x, y) => Ok(self.fp_test("fp.eq", x, y)?), + Expr::FPNe(x, y) => { + let test_eq = self.fp_test("fp.eq", x, y)?; + Ok(self.smt.not(test_eq)) + } + Expr::FPLt(x, y) => Ok(self.fp_test("fp.lt", x, y)?), + Expr::FPGt(x, y) => Ok(self.fp_test("fp.gt", x, y)?), + Expr::FPLe(x, y) => Ok(self.fp_test("fp.leq", x, y)?), + Expr::FPGe(x, y) => Ok(self.fp_test("fp.geq", x, y)?), + Expr::FPAdd(x, y) => Ok(self.fp_rounding_binary("fp.add", x, y)?), + Expr::FPSub(x, y) => Ok(self.fp_rounding_binary("fp.sub", x, y)?), + Expr::FPMul(x, y) => Ok(self.fp_rounding_binary("fp.mul", x, y)?), + Expr::FPDiv(x, y) => Ok(self.fp_rounding_binary("fp.div", x, y)?), + Expr::FPMin(x, y) => Ok(self.fp_binary("fp.min", x, y)?), + Expr::FPMax(x, y) => Ok(self.fp_binary("fp.max", x, y)?), + Expr::FPNeg(x) => Ok(self.fp_unary("fp.neg", x)?), + Expr::FPCeil(x) => { + Ok(self.fp_rounding_unary("fp.roundToIntegral", ROUND_TOWARD_POSITIVE, x)?) + } + Expr::FPFloor(x) => { + Ok(self.fp_rounding_unary("fp.roundToIntegral", ROUND_TOWARD_NEGATIVE, x)?) + } + Expr::FPSqrt(x) => { + Ok(self.fp_rounding_unary("fp.sqrt", ROUND_NEAREST_TIES_TO_EVEN, x)?) + } + Expr::FPTrunc(x) => { + Ok(self.fp_rounding_unary("fp.roundToIntegral", ROUND_TOWARD_ZERO, x)?) + } + Expr::FPNearest(x) => { + Ok(self.fp_rounding_unary("fp.roundToIntegral", ROUND_NEAREST_TIES_TO_EVEN, x)?) + } + Expr::FPIsZero(x) => Ok(self.fp_unary_predicate("fp.isZero", x)?), + Expr::FPIsInfinite(x) => Ok(self.fp_unary_predicate("fp.isInfinite", x)?), + Expr::FPIsNaN(x) => Ok(self.fp_unary_predicate("fp.isNaN", x)?), + Expr::FPIsNegative(x) => Ok(self.fp_unary_predicate("fp.isNegative", x)?), + Expr::FPIsPositive(x) => Ok(self.fp_unary_predicate("fp.isPositive", x)?), + } + } + + fn constant(&self, constant: &Const) -> SExpr { + match *constant { + Const::Bool(true) => self.smt.true_(), + Const::Bool(false) => self.smt.false_(), + Const::Int(v) => self.smt.numeral(v), + Const::BitVector(w, ref v) => self.smt.atom(format!("#b{v:0>w$b}")), + Const::Unspecified => unimplemented!("constant of unspecified type"), + } + } + + fn bv_zero_ext(&self, w: ExprId, x: ExprId) -> Result { + // TODO(mbm): dedupe logic with bv_sign_ext and bv_conv_to? + + // Destination width expression should have known integer value. + let dst: usize = self + .assignment + .try_int_value(w) + .context("destination width of zero_ext expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + // Expression type should be a bit-vector of known width. + let src = self + .assignment + .try_bit_vector_width(x) + .context("source of zero_ext expression should be a bit-vector of known width")?; + + // Build zero_extend expression. + let padding = dst + .checked_sub(src) + .ok_or(format_err!("cannot zero extend to smaller width"))?; + Ok(self.zero_extend(padding, self.expr_atom(x))) + } + + fn bv_sign_ext(&self, w: ExprId, x: ExprId) -> Result { + // TODO(mbm): dedupe logic with bv_conv_to? + + // Destination width expression should have known integer value. + let dst: usize = self + .assignment + .try_int_value(w) + .context("destination width of sign_ext expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + // Expression type should be a bit-vector of known width. + let src = self + .assignment + .try_bit_vector_width(x) + .context("source of sign_ext expression should be a bit-vector of known width")?; + + // Build sign_extend expression. + let padding = dst + .checked_sub(src) + .ok_or(format_err!("cannot sign extend to smaller width"))?; + Ok(self.sign_extend(padding, self.expr_atom(x))) + } + + fn bv_conv_to(&mut self, w: ExprId, x: ExprId) -> Result { + // Destination width expression should have known integer value. + let dst: usize = self + .assignment + .try_int_value(w) + .context("destination width of conv_to expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + // Expression type should be a bit-vector of known width. + let src = self + .assignment + .try_bit_vector_width(x) + .context("source of conv_to expression should be a bit-vector of known width")?; + + // Handle depending on source and destination widths. + match dst.cmp(&src) { + Ordering::Greater => { + let padding = self.declare_bit_vec("conv_to_padding", dst - src)?; + Ok(self.smt.concat(padding, self.expr_atom(x))) + } + Ordering::Less => { + // QUESTION(mbm): conv_to smaller destination: safe to discard high bits? + let high_bit = dst.checked_sub(1).unwrap(); + Ok(self.extract(high_bit, 0, self.expr_atom(x))) + } + Ordering::Equal => Ok(self.expr_atom(x)), + } + } + + fn int_to_bv(&self, w: ExprId, x: ExprId) -> Result { + // Destination width expression should have known integer value. + let width: usize = self + .assignment + .try_int_value(w) + .context("destination width of int2bv expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + // Build int2bv expression. + Ok(self.int2bv(width, self.expr_atom(x))) + } + + fn width_of(&self, x: ExprId) -> Result { + // QUESTION(mbm): should width_of expressions be elided or replaced after type inference? + + // Expression type should be a bit-vector of known width. + let width = self + .assignment + .try_bit_vector_width(x) + .context("target of width_of expression should be a bit-vector of known width")?; + + // Substitute known constant width. + Ok(self.smt.numeral(width)) + } + + fn verification_condition(&mut self) -> Result<()> { + // (not ( => )) + let assumptions = self.all(&self.conditions.assumptions); + let assertions = self.all(&self.conditions.assertions); + let vc = self.smt.imp(assumptions, assertions); + self.smt.assert(self.smt.not(vc))?; + Ok(()) + } + + /// Zero-extend an SMT bit vector to a wider bit vector by adding `padding` + /// zeroes to the front. + fn zero_extend(&self, padding: usize, v: SExpr) -> SExpr { + if padding == 0 { + return v; + } + self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("zero_extend"), + self.smt.numeral(padding), + ]), + v, + ]) + } + + /// Sign-extend an SMT bit vector to a wider bit vector. + fn sign_extend(&self, padding: usize, v: SExpr) -> SExpr { + if padding == 0 { + return v; + } + self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("sign_extend"), + self.smt.numeral(padding), + ]), + v, + ]) + } + + fn extract(&self, high_bit: usize, low_bit: usize, v: SExpr) -> SExpr { + assert!(low_bit <= high_bit); + self.smt + .extract(high_bit.try_into().unwrap(), low_bit.try_into().unwrap(), v) + } + + /// Convert an SMT integer to a bit vector of a given width. + fn int2bv(&self, width: usize, value: SExpr) -> SExpr { + self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("int2bv"), + self.smt.numeral(width), + ]), + value, + ]) + } + + /// Floating point special values. + fn fp_value(&mut self, op: &str, w: ExprId) -> Result { + let width = self + .assignment + .try_int_value(w) + .context("floating point constant width should have known integer value")? + .try_into()?; + let (eb, sb) = Self::fp_exponent_significand_bits(width)?; + let result_fp = self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom(op), + self.smt.numeral(eb), + self.smt.numeral(sb), + ]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point unary operand without rounding. + fn fp_unary(&mut self, op: &str, x: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + + // Unary expression. + let result_fp = self.smt.list(vec![self.smt.atom(op), x]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point test operation without rounding, to boolean. + fn fp_test(&mut self, op: &str, x: ExprId, y: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + let y = self.to_fp(self.expr_atom(y), width)?; + + // Binary result, no conversion needed after test + Ok(self.smt.list(vec![self.smt.atom(op), x, y])) + } + + /// Floating point binary operand without rounding. + fn fp_binary(&mut self, op: &str, x: ExprId, y: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + let y = self.to_fp(self.expr_atom(y), width)?; + + // Binary expression. + let result_fp = self.smt.list(vec![self.smt.atom(op), x, y]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point unary operand with rounding. + fn fp_rounding_unary(&mut self, op: &str, rounding_mode: &str, x: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + + // Unary expression. + let result_fp = self + .smt + .list(vec![self.smt.atom(op), self.smt.atom(rounding_mode), x]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point binary operand with rounding. + fn fp_rounding_binary(&mut self, op: &str, x: ExprId, y: ExprId) -> Result { + // Convert to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + let y = self.to_fp(self.expr_atom(y), width)?; + + // Binary expression. + let result_fp = self + .smt + .list(vec![self.smt.atom(op), self.smt.atom(ROUNDING_MODE), x, y]); + + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec(op, width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, result_fp))?; + + Ok(result) + } + + /// Floating point unary predicate. + fn fp_unary_predicate(&mut self, op: &str, x: ExprId) -> Result { + // Convert operand to floating point. + let width = self + .assignment + .try_bit_vector_width(x) + .context("floating point expression must be a bit-vector of known width")?; + + let x = self.to_fp(self.expr_atom(x), width)?; + + // Emit expression. + Ok(self.smt.list(vec![self.smt.atom(op), x])) + } + + /// Represent an expression in SMT-LIB floating-point type. + fn to_fp(&self, x: SExpr, width: usize) -> Result { + let (eb, sb) = Self::fp_exponent_significand_bits(width)?; + Ok(self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("to_fp"), + self.smt.numeral(eb), + self.smt.numeral(sb), + ]), + x, + ])) + } + + fn to_fp_from_expr(&mut self, w: ExprId, xid: ExprId, signed: bool) -> Result { + // Destination width expression should have known integer value. + let width: usize = self + .assignment + .try_int_value(w) + .context("destination width of to_fp expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + let x = self.expr_atom(xid); + let (eb, sb) = Self::fp_exponent_significand_bits(width)?; + let fp = self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt + .atom(if signed { "to_fp" } else { "to_fp_unsigned" }), + self.smt.numeral(eb), + self.smt.numeral(sb), + ]), + self.smt.atom(ROUNDING_MODE), + x, + ]); + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec("conv", width)?; + let result_as_fp = self.to_fp(result, width)?; + self.smt.assert(self.smt.eq(result_as_fp, fp))?; + + Ok(result) + } + + fn fp_to_bv(&mut self, w: ExprId, x: ExprId, signed: bool) -> Result { + // Destination width expression should have known integer value. + let width: usize = self + .assignment + .try_int_value(w) + .context("destination width of fp_to_bv expression should have known integer value")? + .try_into() + .expect("width should be representable as usize"); + + let x = self.to_fp(self.expr_atom(x), width)?; + + let fp: SExpr = self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt + .atom(if signed { "fp.to_sbv" } else { "fp.to_ubv" }), + self.smt.numeral(width), + ]), + self.smt.atom(ROUNDING_MODE), + x, + ]); + + Ok(fp) + } + + fn to_fp_from_fp(&mut self, w: ExprId, xid: ExprId) -> Result { + // Destination width expression should have known integer value. + let new_width: usize = self + .assignment + .try_int_value(w) + .context( + "destination width of to_fp_from_fp expression should have known integer value", + )? + .try_into() + .expect("width should be representable as usize"); + + // Convert operand to floating point. + let width = self + .assignment + .try_bit_vector_width(xid) + .context("floating point expression must be a bit-vector of known width")?; + let x = self.to_fp(self.expr_atom(xid), width)?; + + let (eb, sb) = Self::fp_exponent_significand_bits(new_width)?; + let fp = self.smt.list(vec![ + self.smt.list(vec![ + self.smt.atoms().und, + self.smt.atom("to_fp"), + self.smt.numeral(eb), + self.smt.numeral(sb), + ]), + self.smt.atom(ROUNDING_MODE), + x, + ]); + // Return bit-vector that's equal to the expression as a floating point. + let result = self.declare_bit_vec("conv", new_width)?; + let result_as_fp = self.to_fp(result, new_width)?; + self.smt.assert(self.smt.eq(result_as_fp, fp))?; + + Ok(result) + } + + fn fp_exponent_significand_bits(width: usize) -> Result<(usize, usize)> { + Ok(match width { + 32 => (8, 24), + 64 => (11, 53), + _ => bail!("unsupported floating-point width"), + }) + } + + /// Parse a constant SMT expression. + fn const_from_sexpr(&self, sexpr: SExpr) -> Result { + match self.smt.get(sexpr) { + SExprData::Atom(a) => Self::const_from_literal(a), + SExprData::List(exprs) => self.const_from_qualified_abstract_value(exprs), + } + } + + /// Parse a constant from an SMT literal. + fn const_from_literal(atom: &str) -> Result { + if atom == "true" { + Ok(Const::Bool(true)) + } else if atom == "false" { + Ok(Const::Bool(false)) + } else if let Some(x) = atom.strip_prefix("#x") { + Ok(Const::BitVector( + x.len() * 4, + BigUint::from_str_radix(x, 16)?, + )) + } else if let Some(x) = atom.strip_prefix("#b") { + Ok(Const::BitVector(x.len(), BigUint::from_str_radix(x, 2)?)) + } else if atom.starts_with(|c: char| c.is_ascii_digit()) { + Ok(Const::Int(atom.parse()?)) + } else { + bail!("unsupported smt literal: {atom}") + } + } + + /// Parse a constant value of a declared sort from an SMT qualified abstract value. + fn const_from_qualified_abstract_value(&self, exprs: &[SExpr]) -> Result { + // This logic is specific to CVC5's representation of declared sort + // abstract values. Z3 uses a different format. This function is + // therefore careful to check for the exact format it expects from CVC5. + + // Expect a list of atoms. + let atoms = exprs + .iter() + .map(|e| match self.smt.get(*e) { + SExprData::Atom(a) => Ok(a), + SExprData::List(_) => bail!("expected atom in qualified identifier"), + }) + .collect::>>()?; + + // Expect the list to be of the form (as @ ). + let ["as", value, sort] = atoms.as_slice() else { + bail!("unsupported qualified identifier: {atoms:?}"); + }; + + // Expect an abstract value. + if !value.starts_with('@') { + bail!("expected qualified identifier constant to have abstract value"); + } + + // Construct constant based on the sort. + if sort == &UNSPECIFIED_SORT { + Ok(Const::Unspecified) + } else if sort == &UNIT_SORT { + todo!("unit sort") + } else { + bail!("unknown sort: '{sort}'"); + } + } + + fn all(&self, xs: &[ExprId]) -> SExpr { + self.smt.and_many(xs.iter().map(|x| self.expr_atom(*x))) + } + + fn expr_atom(&self, x: ExprId) -> SExpr { + self.smt.atom(self.expr_name(x)) + } + + fn expr_name(&self, x: ExprId) -> String { + let expr = &self.conditions.exprs[x.index()]; + if let Expr::Variable(v) = expr { + format!( + "{}_{}", + self.conditions.variables[v.index()].name, + x.index() + ) + } else { + format!("e{}", x.index()) + } + } + + fn declare_bit_vec(&mut self, name: &str, n: usize) -> Result { + let name = format!("${name}{}", self.tmp_idx); + self.tmp_idx += 1; + let sort = self.smt.bit_vec_sort(self.smt.numeral(n)); + self.smt.declare_const(&name, sort)?; + Ok(self.smt.atom(name)) + } + + fn encode_rotate( + &self, + op: RotationDirection, + source: SExpr, + amount: SExpr, + width: usize, + ) -> SExpr { + // SMT bitvector rotate_left requires that the rotate amount be + // statically specified. Instead, to use a dynamic amount, desugar + // to shifts and bit arithmetic. + let width_as_bv = self.smt.binary(width.try_into().unwrap(), width); + let wrapped_amount = self.smt.bvurem(amount, width_as_bv); + let wrapped_delta = self.smt.bvsub(width_as_bv, wrapped_amount); + match op { + RotationDirection::Left => self.smt.bvor( + self.smt.bvshl(source, wrapped_amount), + self.smt.bvlshr(source, wrapped_delta), + ), + RotationDirection::Right => self.smt.bvor( + self.smt.bvshl(source, wrapped_delta), + self.smt.bvlshr(source, wrapped_amount), + ), + } + } + + fn error(&self, x: ExprId, msg: impl Into) -> Error { + self.conditions.error_at_expr(self.prog, x, msg) + } +} diff --git a/cranelift/isle/veri/veri/src/spec.rs b/cranelift/isle/veri/veri/src/spec.rs new file mode 100644 index 000000000000..e18a75bb29a7 --- /dev/null +++ b/cranelift/isle/veri/veri/src/spec.rs @@ -0,0 +1,1109 @@ +use anyhow::{bail, format_err, Ok, Result}; +use cranelift_isle::{ + ast::{self, AttrKind, AttrTarget, Def, Ident, Model, ModelType, Modifies, SpecOp}, + lexer::Pos, + sema::{ReturnKind, RuleId, Sym, Term, TermEnv, TermId, TypeEnv, TypeId}, +}; +use std::{ + collections::{hash_map::Entry, HashMap, HashSet}, + fmt::Debug, +}; + +use crate::types::Field; +use crate::types::Enum; +use crate::types::{Compound, Const}; + +// QUESTION(mbm): do we need this layer independent of AST spec types and Veri-IR? + +/// Positioned attaches positional information to a wrapped object. +#[derive(Clone)] +pub struct Positioned { + pub pos: Pos, + pub x: X, +} + +impl Positioned { + fn new(pos: Pos, x: X) -> Box { + Box::new(Self { pos, x }) + } +} + +impl Debug for Positioned { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Positioned") + .field(&self.pos) + .field(&self.x) + .finish() + } +} + +pub type Expr = Box>; + +/// Spec expression. +#[derive(Debug, Clone)] +pub enum ExprKind { + // TODO(mbm): plumb positional information through spec expressions + + // Terminal nodes + Var(Ident), + Const(Const), + As(Expr, Compound), + Constructor(Constructor), + Field(Ident, Expr), + Discriminator(Ident, Expr), + + // Get the width of a bitvector + WidthOf(Expr), + + // Boolean operations + // QUESTION(mbm): would it be preferable to use the Binary(Opcode, ExprBox, ExprBox) form instead? + Not(Expr), + And(Vec), + Or(Vec), + Imp(Expr, Expr), + Eq(Expr, Expr), + Lt(Expr, Expr), + Lte(Expr, Expr), + Gt(Expr, Expr), + Gte(Expr, Expr), + + BVSgt(Expr, Expr), + BVSge(Expr, Expr), + BVSlt(Expr, Expr), + BVSle(Expr, Expr), + BVUgt(Expr, Expr), + BVUge(Expr, Expr), + BVUlt(Expr, Expr), + BVUle(Expr, Expr), + + BVSaddo(Expr, Expr), + + // Integer arithmetic + Add(Expr, Expr), + Sub(Expr, Expr), + Mul(Expr, Expr), + + // Bitvector operations + + // Unary operators + BVNeg(Expr), + BVNot(Expr), + Cls(Expr), + Clz(Expr), + Rev(Expr), + Popcnt(Expr), + + // Binary operators + BVAdd(Expr, Expr), + BVSub(Expr, Expr), + BVMul(Expr, Expr), + BVUDiv(Expr, Expr), + BVSDiv(Expr, Expr), + BVURem(Expr, Expr), + BVSRem(Expr, Expr), + BVAnd(Expr, Expr), + BVOr(Expr, Expr), + BVXor(Expr, Expr), + BVRotl(Expr, Expr), + BVRotr(Expr, Expr), + BVShl(Expr, Expr), + BVLShr(Expr, Expr), + BVAShr(Expr, Expr), + + // Conversions + BVZeroExt(Expr, Expr), + BVSignExt(Expr, Expr), + // Conversion to wider/narrower bits, without an explicit extend. + BVConvTo(Expr, Expr), + ToFP(Expr, Expr), + ToFPUnsigned(Expr, Expr), + ToFPFromFP(Expr, Expr), + FPToUBV(Expr, Expr), + FPToSBV(Expr, Expr), + + // Extract specified bits + BVExtract(usize, usize, Expr), + + // Concatenate bitvectors. + BVConcat(Vec), + BVReplicate(Expr, usize), + + // Convert between integers and bitvector. + Int2BV(Expr, Expr), + BV2Nat(Expr), + + // Floating point. + FPPositiveInfinity(Expr), + FPNegativeInfinity(Expr), + FPPositiveZero(Expr), + FPNegativeZero(Expr), + FPNaN(Expr), + FPEq(Expr, Expr), + FPNe(Expr, Expr), + FPLt(Expr, Expr), + FPGt(Expr, Expr), + FPLe(Expr, Expr), + FPGe(Expr, Expr), + FPAdd(Expr, Expr), + FPSub(Expr, Expr), + FPMul(Expr, Expr), + FPDiv(Expr, Expr), + FPMin(Expr, Expr), + FPMax(Expr, Expr), + FPNeg(Expr), + FPCeil(Expr), + FPFloor(Expr), + FPSqrt(Expr), + FPTrunc(Expr), + FPNearest(Expr), + FPIsZero(Expr), + FPIsInfinite(Expr), + FPIsNaN(Expr), + FPIsNegative(Expr), + FPIsPositive(Expr), + + // Conditional if-then-else + Conditional(Expr, Expr, Expr), + // Switch + Switch(Expr, Vec<(Expr, Expr)>), + // Match + Match(Expr, Vec), + // Let bindings + Let(Vec<(Ident, Expr)>, Expr), + // With scope. + With(Vec, Expr), + // Macro definition. + Macro(Vec, Expr), + // Macro expansion. + Expand(Ident, Vec), +} + +macro_rules! unary_expr { + ($expr:path, $args:ident, $pos:ident) => {{ + // TODO(mbm): return error instead of assert + assert_eq!( + $args.len(), + 1, + "Unexpected number of args for unary operator at {:?}", + $pos + ); + $expr(expr_from_ast(&$args[0])) + }}; +} + +macro_rules! binary_expr { + ($expr:path, $args:ident, $pos:ident) => {{ + // TODO(mbm): return error instead of assert + assert_eq!( + $args.len(), + 2, + "Unexpected number of args for binary operator at {:?}", + $pos + ); + $expr(expr_from_ast(&$args[0]), expr_from_ast(&$args[1])) + }}; +} + +macro_rules! ternary_expr { + ($expr:path, $args:ident, $pos:ident) => {{ + // TODO(mbm): return error instead of assert + assert_eq!( + $args.len(), + 3, + "Unexpected number of args for ternary operator at {:?}", + $pos + ); + $expr( + expr_from_ast(&$args[0]), + expr_from_ast(&$args[1]), + expr_from_ast(&$args[2]), + ) + }}; +} + +macro_rules! variadic_expr { + ($expr:path, $args:ident, $pos:ident) => {{ + // TODO(mbm): return error instead of assert + assert!( + $args.len() >= 1, + "Unexpected number of args for variadic binary operator {:?}", + $pos + ); + $expr(exprs_from_ast($args)) + }}; +} + +fn expr_from_ast(expr: &ast::SpecExpr) -> Expr { + Positioned::new(expr.pos(), ExprKind::from_ast(expr)) +} + +fn exprs_from_ast(exprs: &[ast::SpecExpr]) -> Vec { + exprs.iter().map(expr_from_ast).collect() +} + +fn var_from_ident(ident: Ident) -> Expr { + Positioned::new(ident.1, ExprKind::Var(ident)) +} + +impl ExprKind { + fn from_ast(expr: &ast::SpecExpr) -> ExprKind { + match expr { + ast::SpecExpr::ConstInt { val, pos: _ } => ExprKind::Const(Const::Int(*val)), + ast::SpecExpr::ConstBool { val, pos: _ } => ExprKind::Const(Const::Bool(*val)), + ast::SpecExpr::ConstBitVec { val, width, pos: _ } => { + ExprKind::Const(Const::BitVector(*width, (*val).into())) + } + ast::SpecExpr::Var { var, pos: _ } => ExprKind::Var(var.clone()), + ast::SpecExpr::As { x, ty, pos: _ } => { + ExprKind::As(expr_from_ast(x), Compound::from_ast(ty)) + } + ast::SpecExpr::Field { field, x, pos: _ } => { + ExprKind::Field(field.clone(), expr_from_ast(x)) + } + ast::SpecExpr::Discriminator { variant, x, pos: _ } => { + ExprKind::Discriminator(variant.clone(), expr_from_ast(x)) + } + ast::SpecExpr::Op { op, args, pos } => match op { + // Unary + SpecOp::Not => unary_expr!(ExprKind::Not, args, pos), + SpecOp::BVNot => unary_expr!(ExprKind::BVNot, args, pos), + SpecOp::BVNeg => unary_expr!(ExprKind::BVNeg, args, pos), + SpecOp::Cls => unary_expr!(ExprKind::Cls, args, pos), + SpecOp::Rev => unary_expr!(ExprKind::Rev, args, pos), + SpecOp::Clz => unary_expr!(ExprKind::Clz, args, pos), + SpecOp::Popcnt => unary_expr!(ExprKind::Popcnt, args, pos), + + // Variadic binops + SpecOp::And => variadic_expr!(ExprKind::And, args, pos), + SpecOp::Or => variadic_expr!(ExprKind::Or, args, pos), + + // Binary + SpecOp::Eq => binary_expr!(ExprKind::Eq, args, pos), + SpecOp::Lt => binary_expr!(ExprKind::Lt, args, pos), + SpecOp::Lte => binary_expr!(ExprKind::Lte, args, pos), + SpecOp::Gt => binary_expr!(ExprKind::Gt, args, pos), + SpecOp::Gte => binary_expr!(ExprKind::Gte, args, pos), + SpecOp::Imp => binary_expr!(ExprKind::Imp, args, pos), + SpecOp::Add => binary_expr!(ExprKind::Add, args, pos), + SpecOp::Sub => binary_expr!(ExprKind::Sub, args, pos), + SpecOp::Mul => binary_expr!(ExprKind::Mul, args, pos), + SpecOp::BVAnd => binary_expr!(ExprKind::BVAnd, args, pos), + SpecOp::BVOr => binary_expr!(ExprKind::BVOr, args, pos), + SpecOp::BVXor => binary_expr!(ExprKind::BVXor, args, pos), + SpecOp::BVAdd => binary_expr!(ExprKind::BVAdd, args, pos), + SpecOp::BVSub => binary_expr!(ExprKind::BVSub, args, pos), + SpecOp::BVMul => binary_expr!(ExprKind::BVMul, args, pos), + SpecOp::BVSdiv => binary_expr!(ExprKind::BVSDiv, args, pos), + SpecOp::BVUdiv => binary_expr!(ExprKind::BVUDiv, args, pos), + SpecOp::BVUrem => binary_expr!(ExprKind::BVURem, args, pos), + SpecOp::BVSrem => binary_expr!(ExprKind::BVSRem, args, pos), + SpecOp::BVShl => binary_expr!(ExprKind::BVShl, args, pos), + SpecOp::BVLshr => binary_expr!(ExprKind::BVLShr, args, pos), + SpecOp::BVAshr => binary_expr!(ExprKind::BVAShr, args, pos), + SpecOp::BVUle => binary_expr!(ExprKind::BVUle, args, pos), + SpecOp::BVUlt => binary_expr!(ExprKind::BVUlt, args, pos), + SpecOp::BVUgt => binary_expr!(ExprKind::BVUgt, args, pos), + SpecOp::BVUge => binary_expr!(ExprKind::BVUge, args, pos), + SpecOp::BVSlt => binary_expr!(ExprKind::BVSlt, args, pos), + SpecOp::BVSle => binary_expr!(ExprKind::BVSle, args, pos), + SpecOp::BVSgt => binary_expr!(ExprKind::BVSgt, args, pos), + SpecOp::BVSge => binary_expr!(ExprKind::BVSge, args, pos), + SpecOp::BVSaddo => binary_expr!(ExprKind::BVSaddo, args, pos), + SpecOp::Rotr => binary_expr!(ExprKind::BVRotr, args, pos), + SpecOp::Rotl => binary_expr!(ExprKind::BVRotl, args, pos), + + // Conversions + SpecOp::ZeroExt => binary_expr!(ExprKind::BVZeroExt, args, pos), + SpecOp::SignExt => binary_expr!(ExprKind::BVSignExt, args, pos), + SpecOp::ConvTo => binary_expr!(ExprKind::BVConvTo, args, pos), + SpecOp::Concat => variadic_expr!(ExprKind::BVConcat, args, pos), + SpecOp::Replicate => { + // TODO(mbm): return error instead of assert + assert_eq!( + args.len(), + 2, + "Unexpected number of args for extract operator at {pos:?}", + ); + let repeat = spec_expr_to_usize(&args[1]).unwrap(); + assert!( + repeat > 0, + "Unexpected repeat count for replicate operator at {pos:?}", + ); + ExprKind::BVReplicate(expr_from_ast(&args[0]), repeat) + } + SpecOp::Extract => { + // TODO(mbm): return error instead of assert + assert_eq!( + args.len(), + 3, + "Unexpected number of args for extract operator at {pos:?}", + ); + ExprKind::BVExtract( + spec_expr_to_usize(&args[0]).unwrap(), + spec_expr_to_usize(&args[1]).unwrap(), + expr_from_ast(&args[2]), + ) + } + SpecOp::Int2BV => binary_expr!(ExprKind::Int2BV, args, pos), + SpecOp::BV2Nat => unary_expr!(ExprKind::BV2Nat, args, pos), + SpecOp::WidthOf => unary_expr!(ExprKind::WidthOf, args, pos), + SpecOp::ToFP => binary_expr!(ExprKind::ToFP, args, pos), + SpecOp::ToFPUnsigned => binary_expr!(ExprKind::ToFPUnsigned, args, pos), + SpecOp::ToFPFromFP => binary_expr!(ExprKind::ToFPFromFP, args, pos), + SpecOp::FPToUBV => binary_expr!(ExprKind::FPToUBV, args, pos), + SpecOp::FPToSBV => binary_expr!(ExprKind::FPToSBV, args, pos), + + // Floating point (IEEE) + SpecOp::FPPositiveInfinity => unary_expr!(ExprKind::FPPositiveInfinity, args, pos), + SpecOp::FPNegativeInfinity => unary_expr!(ExprKind::FPNegativeInfinity, args, pos), + SpecOp::FPPositiveZero => unary_expr!(ExprKind::FPPositiveZero, args, pos), + SpecOp::FPNegativeZero => unary_expr!(ExprKind::FPNegativeZero, args, pos), + SpecOp::FPNaN => unary_expr!(ExprKind::FPNaN, args, pos), + SpecOp::FPEq => binary_expr!(ExprKind::FPEq, args, pos), + SpecOp::FPNe => binary_expr!(ExprKind::FPNe, args, pos), + SpecOp::FPLt => binary_expr!(ExprKind::FPLt, args, pos), + SpecOp::FPGt => binary_expr!(ExprKind::FPGt, args, pos), + SpecOp::FPLe => binary_expr!(ExprKind::FPLe, args, pos), + SpecOp::FPGe => binary_expr!(ExprKind::FPGe, args, pos), + SpecOp::FPAdd => binary_expr!(ExprKind::FPAdd, args, pos), + SpecOp::FPSub => binary_expr!(ExprKind::FPSub, args, pos), + SpecOp::FPMul => binary_expr!(ExprKind::FPMul, args, pos), + SpecOp::FPDiv => binary_expr!(ExprKind::FPDiv, args, pos), + SpecOp::FPMin => binary_expr!(ExprKind::FPMin, args, pos), + SpecOp::FPMax => binary_expr!(ExprKind::FPMax, args, pos), + SpecOp::FPNeg => unary_expr!(ExprKind::FPNeg, args, pos), + SpecOp::FPCeil => unary_expr!(ExprKind::FPCeil, args, pos), + SpecOp::FPFloor => unary_expr!(ExprKind::FPFloor, args, pos), + SpecOp::FPSqrt => unary_expr!(ExprKind::FPSqrt, args, pos), + SpecOp::FPTrunc => unary_expr!(ExprKind::FPTrunc, args, pos), + SpecOp::FPNearest => unary_expr!(ExprKind::FPNearest, args, pos), + SpecOp::FPIsZero => unary_expr!(ExprKind::FPIsZero, args, pos), + SpecOp::FPIsInfinite => unary_expr!(ExprKind::FPIsInfinite, args, pos), + SpecOp::FPIsNaN => unary_expr!(ExprKind::FPIsNaN, args, pos), + SpecOp::FPIsNegative => unary_expr!(ExprKind::FPIsNegative, args, pos), + SpecOp::FPIsPositive => unary_expr!(ExprKind::FPIsPositive, args, pos), + + // Conditionals + SpecOp::If => ternary_expr!(ExprKind::Conditional, args, pos), + SpecOp::Switch => { + assert!( + args.len() > 1, + "Unexpected number of args for switch operator {pos:?}", + ); + let on = expr_from_ast(&args[0]); + let arms: Vec<_> = args[1..] + .iter() + .map(|p| match p { + ast::SpecExpr::Pair { l, r, pos: _ } => { + (expr_from_ast(l), expr_from_ast(r)) + } + // TODO(mbm): error rather than panic for non-pair in switch, since it's not actually unreachable + _ => unreachable!("switch expression arguments must be pairs"), + }) + .collect(); + ExprKind::Switch(on, arms) + } + }, + ast::SpecExpr::Match { x, arms, pos: _ } => { + let x = expr_from_ast(x); + let arms = arms + .iter() + .map(|arm| Arm { + variant: arm.variant.clone(), + args: arm.args.clone(), + body: expr_from_ast(&arm.body), + }) + .collect(); + ExprKind::Match(x, arms) + } + ast::SpecExpr::Let { defs, body, pos: _ } => { + let defs = defs + .iter() + .map(|(ident, x)| (ident.clone(), expr_from_ast(x))) + .collect(); + let body = expr_from_ast(body); + ExprKind::Let(defs, body) + } + ast::SpecExpr::With { + decls, + body, + pos: _, + } => { + let decls = decls.clone(); + let body = expr_from_ast(body); + ExprKind::With(decls, body) + } + ast::SpecExpr::Pair { l, r, pos: _ } => { + // QUESTION(mbm): is there a cleaner way to handle switch statements without the pair type? + unreachable!( + "pairs must only occur in switch expressions, {:?} {:?}", + l, r + ) + } + ast::SpecExpr::Enum { + name, + variant, + args, + pos: _, + } => ExprKind::Constructor(Constructor::Enum { + name: name.clone(), + variant: variant.clone(), + args: exprs_from_ast(args), + }), + ast::SpecExpr::Struct { fields, pos: _ } => { + ExprKind::Constructor(Constructor::Struct { + fields: fields.iter().map(FieldInit::from_ast).collect(), + }) + } + ast::SpecExpr::Macro { + params, + body, + pos: _, + } => ExprKind::Macro(params.clone(), expr_from_ast(body)), + ast::SpecExpr::Expand { name, args, pos: _ } => { + ExprKind::Expand(name.clone(), exprs_from_ast(args)) + } + } + } +} + +fn spec_expr_to_usize(expr: &ast::SpecExpr) -> Option { + match expr { + &ast::SpecExpr::ConstInt { val, pos: _ } => { + // TODO(mbm): return error rather than unwrap + Some(val.try_into().expect("constant should be unsigned size")) + } + _ => None, + } +} + +#[derive(Debug, Clone)] +pub enum Constructor { + Enum { + // TODO(mbm): Enum identifiers should be mapped to TermId? + name: Ident, + variant: Ident, + args: Vec, + }, + Struct { + fields: Vec, + }, +} + +#[derive(Debug, Clone)] +pub struct Arm { + pub variant: Ident, + pub args: Vec, + pub body: Expr, +} + +#[derive(Debug, Clone)] +pub struct FieldInit { + pub name: Ident, + pub value: Expr, +} + +impl FieldInit { + fn from_ast(field: &ast::FieldInit) -> Self { + Self { + name: field.name.clone(), + value: expr_from_ast(&field.value), + } + } +} + +// QUESTION(mbm): should we make the result explicit in the spec syntax? +static RESULT: &str = "result"; + +#[derive(Clone)] +pub struct Spec { + pub args: Vec, + pub ret: Ident, + pub provides: Vec, + pub requires: Vec, + pub matches: Vec, + pub modifies: Vec, + pub pos: Pos, +} + +impl Spec { + fn new() -> Self { + Self { + args: Vec::new(), + ret: Self::result_ident(), + provides: Vec::new(), + requires: Vec::new(), + matches: Vec::new(), + modifies: Vec::new(), + pos: Pos::default(), + } + } + + fn from_ast(spec: &ast::Spec) -> Self { + Self { + args: spec.args.clone(), + ret: Self::result_ident(), + provides: exprs_from_ast(&spec.provides), + requires: exprs_from_ast(&spec.requires), + matches: exprs_from_ast(&spec.matches), + modifies: spec.modifies.clone(), + pos: spec.pos, + } + } + + fn result_ident() -> Ident { + Ident(RESULT.to_string(), Pos::default()) + } +} + +#[derive(Debug, Clone)] +pub struct State { + pub name: Ident, + pub ty: Compound, + pub default: Expr, +} + +#[derive(Debug, Clone)] +pub struct Signature { + pub args: Vec, + pub ret: Compound, +} + +impl Signature { + fn from_ast(sig: &ast::Signature) -> Self { + Self { + args: sig.args.iter().map(Compound::from_ast).collect(), + ret: Compound::from_ast(&sig.ret), + } + } +} + +impl std::fmt::Display for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "({args}) -> {ret}", + args = self + .args + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "), + ret = self.ret + ) + } +} + +pub struct Macro { + pub name: Ident, + pub params: Vec, + pub body: Expr, +} + +pub struct SpecEnv { + /// Specification for the given term. + pub term_spec: HashMap, + + /// State elements. + pub state: Vec, + + /// Terms that should be chained. + pub chain: HashSet, + + /// Tags applied to each term. + pub term_tags: HashMap>, + + // Type instantiations for the given term. + pub term_instantiations: HashMap>, + + /// Rules for which priority is significant. + pub priority: HashSet, + + /// Tags applied to each rule. + pub rule_tags: HashMap>, + + /// Model for the given type. + pub type_model: HashMap, + + // Track which types are derived (auto-generated) + pub derived_types: HashSet, + + /// Value for the given constant. + pub const_value: HashMap, + + /// Macro definitions. + pub macros: HashMap, +} + +impl SpecEnv { + + + pub fn from_ast(defs: &[Def], termenv: &TermEnv, tyenv: &TypeEnv) -> Result { + let mut env = Self { + term_spec: HashMap::new(), + state: Vec::new(), + chain: HashSet::new(), + term_tags: HashMap::new(), + term_instantiations: HashMap::new(), + priority: HashSet::new(), + rule_tags: HashMap::new(), + type_model: HashMap::new(), + derived_types: HashSet::new(), + const_value: HashMap::new(), + macros: HashMap::new(), + }; + + env.derive_type_models(tyenv)?; + env.collect_models(defs, tyenv); + env.derive_enum_variant_specs(termenv, tyenv)?; + env.collect_state(defs)?; + env.collect_instantiations(defs, termenv, tyenv); + env.collect_specs(defs, termenv, tyenv)?; + env.collect_attrs(defs, termenv, tyenv)?; + env.collect_macros(defs); + env.check_option_return_term_specs_uses_matches(termenv, tyenv)?; + env.check_for_chained_terms_with_spec(); + + Ok(env) + } + + // helper function for ExtEnum + /// Borrow the base enum for a TypeId if present. + fn get_enum_from_typeid(&self, tid: TypeId) -> Option<&crate::types::Enum> { + match self.type_model.get(&tid)? { + crate::types::Compound::Enum(e) => Some(e), + crate::types::Compound::ExtEnum { base, .. } => Some(base), + _ => None, + } + } + + /// Clone out an owned Enum for a TypeId (useful when constructing ExtEnum). + fn clone_enum_from_typeid(&self, tid: TypeId) -> Option { + self.get_enum_from_typeid(tid).cloned() + } + + fn collect_models(&mut self, defs: &[Def], tyenv: &TypeEnv) { + for def in defs { + if let ast::Def::Model(Model { name, val }) = def { + match val { + ast::ModelValue::TypeValue(model_type) => { + // only insert if this name hasn't been seen yet + let tid = tyenv.get_type_by_name(name).expect("type should exist"); + + // new model compound + let new_model = Compound::from_ast(model_type); + + if let Some(_existing) = self.type_model.get(&tid) { + // --- CASE 1: ext-enum --- + if let Compound::ExtEnum { .. } = new_model { + if self.derived_types.contains(&tid) { + // good: upgrade derived enum → ext-enum + self.type_model.insert(tid, new_model); + self.derived_types.remove(&tid); + continue; + } else { + panic!("cannot ext-enum a non-enum type: {}", name.0); + } + } + + // --- CASE 2: replacing a derived enum --- + if self.derived_types.contains(&tid) { + self.type_model.insert(tid, new_model); + self.derived_types.remove(&tid); + continue; + } + + // --- CASE 3: any other duplicate --- + panic!("duplicate type model: {}", name.0); + } else { + // first definition + self.type_model.insert(tid, new_model); + } + + + } + ast::ModelValue::ConstValue(val) => { + // TODO(mbm): error on missing constant name rather than panic + let sym = tyenv.intern(name).expect("constant name should be defined"); + // TODO(mbm): enforce that the expression is constant. + // TODO(mbm): ensure the type of the expression matches the type of the + self.const_value.insert(sym, expr_from_ast(val)); + } + ast::ModelValue::ExtEnumValue(fields) => { + // 1) Resolve the base type to a TypeId. + let base_tid = tyenv + .get_type_by_name(name) + .expect("ext-enum base type should exist"); + + // 2) Clone the existing base enum compound. + let base_enum = self + .clone_enum_from_typeid(base_tid) + .expect("expected base enum to be defined for type-ext-enum"); + + // 3) Lower the extra fields to veri types::Field. + let extra: Vec = fields + .iter() + .map(|mf| Field { + name: mf.name.clone(), + ty: Compound::from_ast(&mf.ty), + }) + .collect(); + + // 4) Build Compound::ExtEnum and overwrite the entry for this TypeId. + let compound = Compound::ExtEnum { base: base_enum, extra }; + self.type_model.insert(base_tid, compound); + } + } + } + } + } + + fn derive_type_models(&mut self, tyenv: &TypeEnv) -> Result<()> { + for ty in &tyenv.types { + // Has an explicit model already been specified? + if self.has_model(ty.id()) { + continue; + } + + // Derive a model from ISLE type, if possible. + if let Some(derived_type) = Compound::from_isle(ty, tyenv) { + // register derived type and mark derived + self.type_model.insert(ty.id(), derived_type); + self.derived_types.insert(ty.id()); + }; + } + Ok(()) + } + + fn derive_enum_variant_specs(&mut self, termenv: &TermEnv, tyenv: &TypeEnv) -> Result<()> { + for model in self.type_model.values() { + + // handle both Enum and ExtEnum + let enum_ref: Option<&Enum> = match model { + Compound::Enum(e) => Some(e), + Compound::ExtEnum { base, .. } => Some(base), + _ => None, + }; + + if let Some(e) = enum_ref { + for variant in &e.variants { + // Lookup the corresponding term. + let full_name = ast::Variant::full_name(&e.name, &variant.name); + let term_id = + termenv + .get_term_by_name(tyenv, &full_name) + .ok_or(format_err!( + "could not find variant term {name}", + name = full_name.0 + ))?; + + // if user wrote a spec already, do not autogenerate + if self.term_spec.contains_key(&term_id) { + continue; + } + + // build a fresh synthesize spec for the variant. + let pos = variant.name.1; + let args: Vec = variant.fields.iter().map(|f| f.name.clone()).collect(); + let constructor = Positioned::new( + pos, + ExprKind::Constructor(Constructor::Enum { + name: e.name.clone(), + variant: variant.name.clone(), + args: args.iter().cloned().map(var_from_ident).collect(), + }), + ); + + let mut spec = Spec::new(); + spec.args = args.clone(); + let ret = var_from_ident(spec.ret.clone()); + spec.provides + .push(Positioned::new(pos, ExprKind::Eq(ret, constructor))); + self.term_tags + .entry(term_id) + .or_default() + .insert("internal_derived_spec".to_string()); + } + } + } + Ok(()) + } + + // fn set_model_type(&mut self, name: &Ident, model_type: &ModelType, tyenv: &TypeEnv) { + // // TODO(mbm): error on missing type rather than panic + // let type_id = tyenv + // .get_type_by_name(name) + // .expect("type name should be defined"); + // // TODO(mbm): error on duplicate model + // let compound = Compound::from_ast(model_type); + + // if let Some(existing) = self.type_model.get(&type_id) { + // match (existing, &compound) { + // (Compound::Enum(_), Compound::ExtEnum { .. }) => { + // // replace Enum with ExtEnum + // self.type_model.insert(type_id, compound); + // return; + // } + // // if already enum and the new one is also enum -> error + // (Compound::Enum(_), Compound::Enum(_)) =>{ + // panic!("duplicate enum model: {}", name.0); + // } + // (Compound::ExtEnum{..}, Compound::ExtEnum{..}) =>{ + // panic!("duplicate extenum model: {}", name.0); + // } + // // otherwise: real conflict + // _ => { + // panic!("duplicate type model: {}", name.0); + // } + // } + // } else { + // self.type_model.insert(type_id, compound); + // } + // } + + fn collect_state(&mut self, defs: &[Def]) -> Result<()> { + // Collect states. + for def in defs { + if let ast::Def::State(ast::State { + name, + ty, + default, + pos: _, + }) = def + { + let ty = Compound::from_ast(ty); + let default = expr_from_ast(default); + self.state.push(State { + name: name.clone(), + ty, + default, + }); + } + } + + // Check for duplicates. + let mut names = HashSet::new(); + for state in &self.state { + let name = &state.name.0; + if names.contains(name) { + bail!("duplicate state {name}"); + } + names.insert(name); + } + + Ok(()) + } + + fn collect_instantiations(&mut self, defs: &[Def], termenv: &TermEnv, tyenv: &TypeEnv) { + // Collect form signatures first, as they may be referenced by instantiations. + let mut form_signature = HashMap::new(); + for def in defs { + if let ast::Def::Form(form) = def { + let signatures: Vec<_> = form.signatures.iter().map(Signature::from_ast).collect(); + form_signature.insert(form.name.0.clone(), signatures); + } + } + + // Collect instantiations. + for def in defs { + if let ast::Def::Instantiation(inst) = def { + let term_id = termenv.get_term_by_name(tyenv, &inst.term).unwrap(); + let sigs = match &inst.form { + Some(form) => form_signature[&form.0].clone(), + None => inst.signatures.iter().map(Signature::from_ast).collect(), + }; + self.term_instantiations.insert(term_id, sigs); + } + } + } + + fn collect_specs(&mut self, defs: &[Def], termenv: &TermEnv, tyenv: &TypeEnv) -> Result<()> { + for def in defs { + if let ast::Def::Spec(spec) = def { + let term_id = termenv + .get_term_by_name(tyenv, &spec.term) + .ok_or(format_err!( + "spec for unknown term {name}", + name = spec.term.0 + ))?; + match self.term_spec.entry(term_id) { + Entry::Occupied(_) => { + bail!("duplicate spec for term {name}", name = spec.term.0) + } + Entry::Vacant(e) => { + e.insert(Spec::from_ast(spec)); + } + } + } + } + Ok(()) + } + + fn collect_attrs(&mut self, defs: &[Def], termenv: &TermEnv, tyenv: &TypeEnv) -> Result<()> { + for def in defs { + if let ast::Def::Attr(attr) = def { + match &attr.target { + AttrTarget::Term(name) => { + let term_id = termenv.get_term_by_name(tyenv, name).ok_or(format_err!( + "attr term '{name}' should exist", + name = name.0 + ))?; + for kind in &attr.kinds { + match kind { + AttrKind::Chain => { + self.chain.insert(term_id); + } + AttrKind::Tag(tag) => { + self.term_tags + .entry(term_id) + .or_default() + .insert(tag.0.clone()); + } + AttrKind::Priority => { + bail!("priority attribute cannot be applied to terms"); + } + } + } + } + AttrTarget::Rule(name) => { + let rule_id = termenv + .get_rule_by_name(tyenv, name) + .ok_or(format_err!("attr rule '{}' does not exist", name.0))?; + for kind in &attr.kinds { + match kind { + AttrKind::Priority => { + self.priority.insert(rule_id); + } + AttrKind::Tag(tag) => { + self.rule_tags + .entry(rule_id) + .or_default() + .insert(tag.0.clone()); + } + AttrKind::Chain => { + bail!("chain attribute cannot be applied to rule"); + } + } + } + } + } + } + } + Ok(()) + } + + fn collect_macros(&mut self, defs: &[Def]) { + for def in defs { + if let ast::Def::SpecMacro(spec_macro) = def { + let body = expr_from_ast(&spec_macro.body); + self.macros.insert( + spec_macro.name.0.clone(), + Macro { + name: spec_macro.name.clone(), + params: spec_macro.params.clone(), + body, + }, + ); + } + } + } + + fn check_option_return_term_specs_uses_matches( + &self, + termenv: &TermEnv, + tyenv: &TypeEnv, + ) -> Result<()> { + for (term_id, spec) in &self.term_spec { + let term = &termenv.terms[term_id.index()]; + if !Self::term_returns_option(term, tyenv) { + continue; + } + if !spec.requires.is_empty() { + bail!( + "term '{name}' requires should be match", + name = tyenv.syms[term.name.index()], + ); + } + } + Ok(()) + } + + fn term_returns_option(term: &Term, tyenv: &TypeEnv) -> bool { + // Constructor + if term.has_constructor() { + return term.is_partial(); + } + + // External extractor + if let Some(sig) = term.extractor_sig(&tyenv) { + return sig.ret_kind == ReturnKind::Option; + } + + // Extractor + if term.has_extractor() { + return true; + } + + false + } + + fn check_for_chained_terms_with_spec(&self) { + for term_id in &self.chain { + // TODO(mbm): error rather than panic + assert!( + !self.term_spec.contains_key(term_id), + "chained term should not have spec" + ); + } + } + + /// Resolve any named types in the given compound type. + pub fn resolve_type(&self, ty: &Compound, tyenv: &TypeEnv) -> Result { + ty.resolve(&mut |name| { + let type_id = tyenv + .get_type_by_name(name) + .ok_or(format_err!("unknown type {}", name.0))?; + let ty = self + .type_model + .get(&type_id) + .ok_or(format_err!("unspecified model for type {}", name.0))?; + Ok(ty.clone()) + }) + } + + /// Resolve any named types in the given term signature. + pub fn resolve_signature(&self, sig: &Signature, tyenv: &TypeEnv) -> Result { + Ok(Signature { + args: sig + .args + .iter() + .map(|arg| self.resolve_type(arg, tyenv)) + .collect::>()?, + ret: self.resolve_type(&sig.ret, tyenv)?, + }) + } + + /// Lookup instantiations for the given term, with any named types resolved. + pub fn resolve_term_instantiations( + &self, + term_id: &TermId, + tyenv: &TypeEnv, + ) -> Result> { + let Some(sigs) = self.term_instantiations.get(term_id) else { + return Ok(Vec::new()); + }; + + sigs.iter() + .map(|sig| self.resolve_signature(sig, tyenv)) + .collect::>() + } + + /// Report whether the given term has a specification. + pub fn has_spec(&self, term_id: TermId) -> bool { + self.term_spec.contains_key(&term_id) + } + + pub fn has_model(&self, type_id: TypeId) -> bool { + self.type_model.contains_key(&type_id) + } +} diff --git a/cranelift/isle/veri/veri/src/testing.rs b/cranelift/isle/veri/veri/src/testing.rs new file mode 100644 index 000000000000..308a5da1ff0b --- /dev/null +++ b/cranelift/isle/veri/veri/src/testing.rs @@ -0,0 +1,43 @@ +use std::{cmp::Ordering, fmt::Debug}; + +pub fn assert_strictly_increasing(elements: &[T]) +where + T: PartialOrd + Debug, +{ + elements.windows(2).for_each(|p| assert!(p[0] < p[1])); +} + +pub fn assert_partial_order_properties(elements: &[T]) +where + T: PartialOrd + Debug, +{ + // Equality + for a in elements { + for b in elements { + assert_eq!( + a == b, + a.partial_cmp(b) == Some(Ordering::Equal), + "equality property failed: a={a:?} b={b:?}" + ); + } + } + + // Transitivity + for a in elements { + for b in elements { + for c in elements { + assert!( + !(a < b && b < c && !(a < c)), + "transitivity property failed: a={a:?} b={b:?} c={c:?}" + ); + } + } + } + + // Duality + for a in elements { + for b in elements { + assert_eq!(a < b, b > a, "duality property failed: a={a:?} b={b:?}"); + } + } +} diff --git a/cranelift/isle/veri/veri/src/trie.rs b/cranelift/isle/veri/veri/src/trie.rs new file mode 100644 index 000000000000..f0127418993e --- /dev/null +++ b/cranelift/isle/veri/veri/src/trie.rs @@ -0,0 +1,153 @@ +use std::sync::Arc; + +use crate::program::Program; +use cranelift_isle::{ + error::{Errors, ErrorsBuilder}, + files::Files, + sema::{ExternalSig, ReturnKind, TermEnv, TermId, Type, TypeEnv, TypeId}, + trie_again::{self, Binding, BindingId, RuleSet}, +}; + +pub fn build_trie(termenv: &TermEnv, files: Arc) -> Result, Errors> { + let (terms, errors) = trie_again::build(termenv); + if errors.is_empty() { + Ok(terms) + } else { + Err(ErrorsBuilder::new() + .errors(errors) + .files(files.clone()) + .build()) + } +} + +#[derive(Clone, Debug)] +pub enum BindingType { + Base(TypeId), + Option(Box), + Tuple(Vec), +} + +impl BindingType { + pub fn display(&self, tyenv: &TypeEnv) -> String { + match self { + BindingType::Base(type_id) => { + let ty = &tyenv.types[type_id.index()]; + ty.name(tyenv).to_string() + } + BindingType::Option(inner) => format!("Option({})", inner.display(tyenv)), + BindingType::Tuple(inners) => format!( + "({inners})", + inners = inners + .iter() + .map(|inner| inner.display(tyenv)) + .collect::>() + .join(", ") + ), + } + } +} + +/// Determine the type of a given binding. +pub fn binding_type( + binding: &Binding, + term_id: TermId, + prog: &Program, + // TODO(mbm): is there a less ugly way to do binding lookup here? + lookup_binding: impl Fn(BindingId) -> Binding, +) -> BindingType { + match binding { + Binding::ConstInt { ty, .. } | Binding::MakeVariant { ty, .. } => BindingType::Base(*ty), + + Binding::ConstPrim { val } => BindingType::Base(prog.tyenv.const_types[val]), + + Binding::Argument { index } => { + let term = &prog.termenv.terms[term_id.index()]; + BindingType::Base(term.arg_tys[index.index()]) + } + + Binding::Extractor { term, .. } => { + // Determine the extractor signature. + let term = &prog.termenv.terms[term.index()]; + let sig = term + .extractor_sig(&prog.tyenv) + .expect("term should have extractor signature"); + external_sig_return_type(&sig) + } + + Binding::Constructor { term, .. } => { + // Determine the constructor signature. + let term = &prog.termenv.terms[term.index()]; + let sig = term + .constructor_sig(&prog.tyenv) + .expect("term should have constructor signature"); + external_sig_return_type(&sig) + } + + Binding::MakeSome { inner } => { + let inner_binding = lookup_binding(*inner); + let inner_ty = binding_type(&inner_binding, term_id, prog, lookup_binding); + BindingType::Option(Box::new(inner_ty)) + } + + Binding::MatchSome { source } => { + let source_binding = lookup_binding(*source); + let source_ty = binding_type(&source_binding, term_id, prog, lookup_binding); + match source_ty { + BindingType::Option(ty) => *ty, + _ => unreachable!("source of match some should be an option"), + } + } + + Binding::MatchVariant { + source, + variant, + field, + } => { + // Lookup type ID for the underlying enum. + let source_binding = lookup_binding(*source); + let source_ty = binding_type(&source_binding, term_id, prog, lookup_binding); + let source_type_id = match source_ty { + BindingType::Base(type_id) => type_id, + _ => unreachable!("source of match variant should be a base type"), + }; + + // Lookup variant. + let enum_ty = &prog.tyenv.types[source_type_id.index()]; + let variant = match enum_ty { + Type::Enum { variants, .. } => &variants[variant.index()], + _ => unreachable!("source match variant should be an enum"), + }; + + // Lookup field type. + let field = &variant.fields[field.index()]; + BindingType::Base(field.ty) + } + + Binding::MatchTuple { source, field } => { + let source_binding = lookup_binding(*source); + let source_ty = binding_type(&source_binding, term_id, prog, lookup_binding); + match source_ty { + BindingType::Tuple(tys) => tys[field.index()].clone(), + _ => unreachable!("source type should be a tuple"), + } + } + + Binding::Iterator { .. } => unimplemented!("iterator bindings not supported"), + } +} + +fn external_sig_return_type(sig: &ExternalSig) -> BindingType { + // Multiple return types are represented as a tuple. + let ty = if sig.ret_tys.len() == 1 { + BindingType::Base(sig.ret_tys[0]) + } else { + BindingType::Tuple(sig.ret_tys.iter().copied().map(BindingType::Base).collect()) + }; + + // Fallible terms return option type. + match sig.ret_kind { + ReturnKind::Option => BindingType::Option(Box::new(ty)), + ReturnKind::Plain => ty, + ReturnKind::Iterator => unimplemented!("extractor iterator return"), + } +} diff --git a/cranelift/isle/veri/veri/src/type_inference.rs b/cranelift/isle/veri/veri/src/type_inference.rs new file mode 100644 index 000000000000..062ea33afd62 --- /dev/null +++ b/cranelift/isle/veri/veri/src/type_inference.rs @@ -0,0 +1,1231 @@ +use std::{ + cmp::Ordering, + collections::{hash_map::Entry, HashMap}, + iter::zip, + vec, +}; + +use anyhow::{bail, format_err, Result}; +use cranelift_isle::{files::Files, sema::TermId}; + +use crate::{ + spec::Signature, + types::{Compound, Const, Type, Width}, + veri::{Call, Conditions, Expr, ExprId, Qualifier, Symbolic}, +}; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TypeValue { + Type(Type), + Value(Const), +} + +impl TypeValue { + pub fn ty(&self) -> Type { + match self { + TypeValue::Type(ty) => ty.clone(), + TypeValue::Value(c) => c.ty(), + } + } + + fn as_value(&self) -> Option<&Const> { + match self { + TypeValue::Value(c) => Some(c), + _ => None, + } + } + + pub fn refines_type(&self, ty: &Type) -> bool { + self >= &Self::Type(ty.clone()) + } + + pub fn merge(left: &Self, right: &Self) -> Option { + match left.partial_cmp(right) { + Some(Ordering::Greater) => Some(left.clone()), + Some(Ordering::Less | Ordering::Equal) => Some(right.clone()), + None => None, + } + } +} + +impl PartialOrd for TypeValue { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (TypeValue::Type(l), TypeValue::Type(r)) => l.partial_cmp(r), + (TypeValue::Type(ty), TypeValue::Value(v)) if ty <= &v.ty() => Some(Ordering::Less), + (TypeValue::Value(v), TypeValue::Type(ty)) if &v.ty() >= ty => Some(Ordering::Greater), + (TypeValue::Value(l), TypeValue::Value(r)) if l == r => Some(Ordering::Equal), + _ => None, + } + } +} + +impl std::fmt::Display for TypeValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TypeValue::Type(ty) => ty.fmt(f), + TypeValue::Value(c) => c.fmt(f), + } + } +} + +/// Boolean expression or its negation. +#[derive(Debug, Clone)] +pub enum Literal { + Var(ExprId), + Not(ExprId), +} + +impl std::fmt::Display for Literal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Literal::Var(x) => write!(f, "{}", x.index()), + Literal::Not(x) => write!(f, "\u{00AC}{}", x.index()), + } + } +} + +#[derive(Debug, Clone)] +pub enum Constraint { + /// Expression x has the given type. + Type { x: ExprId, ty: Type }, + /// Expressions have the same type. + SameType { x: ExprId, y: ExprId }, + /// Expressions have the same type and value. + Identical { x: ExprId, y: ExprId }, + /// Expression x is a bitvector with width given by the integer expression w. + WidthOf { x: ExprId, w: ExprId }, + /// Bitvector x is the concatenation bitvectors l and r. + Concat { x: ExprId, l: ExprId, r: ExprId }, + /// Expression x has known constant value v. + Value { x: ExprId, c: Const }, + /// Constraint conditioned on a boolean. + Implies { c: ExprId, then: Box }, + /// Clause is a disjunction that must hold. + Clause { literals: Vec }, +} + +impl std::fmt::Display for Constraint { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Constraint::Type { x, ty } => write!(f, "type({}) = {ty}", x.index()), + Constraint::SameType { x, y } => { + write!(f, "type({}) == type({})", x.index(), y.index()) + } + Constraint::Identical { x, y } => write!(f, "{} == {}", x.index(), y.index()), + Constraint::WidthOf { x, w } => write!(f, "{} = width_of({})", w.index(), x.index()), + Constraint::Concat { x, l, r } => { + write!(f, "{} = {}:{}", x.index(), l.index(), r.index()) + } + Constraint::Value { x, c } => write!(f, "{} = value({c})", x.index()), + Constraint::Implies { c, then } => write!(f, "{} => {then}", c.index()), + Constraint::Clause { literals } => write!( + f, + "clause({})", + literals + .iter() + .map(ToString::to_string) + .collect::>() + .join(" \u{2228} ") + ), + } + } +} + +#[derive(Clone)] +pub enum Choice { + TermInstantiation(TermId, Signature), +} + +impl std::fmt::Display for Choice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Choice::TermInstantiation(term_id, sig) => { + write!(f, "term_instantiation({}, {sig})", term_id.index()) + } + } + } +} + +#[derive(Clone)] +pub struct Arm { + choice: Choice, + constraints: Vec, +} + +#[derive(Default, Clone)] +pub struct Branch { + arms: Vec, +} + +#[derive(Default)] +pub struct System { + choices: Vec, + constraints: Vec, + branches: Vec, +} + +impl System { + fn fork(&self) -> Vec { + let mut branches = self.branches.clone(); + let branch = branches.pop().expect("should have at least one branch"); + + let mut children = Vec::new(); + for arm in &branch.arms { + let mut constraints = self.constraints.clone(); + constraints.extend(arm.constraints.iter().cloned()); + + let mut choices = self.choices.clone(); + // Only record the choice if there are multiple branches. + if branch.arms.len() > 1 { + choices.push(arm.choice.clone()); + } + + children.push(System { + constraints, + choices, + branches: branches.clone(), + }) + } + + children + } + + pub fn pretty_print(&self) { + println!("system {{"); + + // Choices + println!("\tchoices = ["); + for choice in &self.choices { + println!("\t\t{choice}"); + } + println!("\t]"); + + // Constraints + println!("\tconstraints = ["); + for constraint in &self.constraints { + println!("\t\t{constraint}"); + } + println!("\t]"); + + // Branches + for branch in &self.branches { + println!("\tbranch {{"); + for arm in &branch.arms { + println!("\t\t{choice} => [", choice = arm.choice); + for constraint in &arm.constraints { + println!("\t\t\t{constraint}"); + } + println!("\t\t]"); + } + println!("\t}}"); + } + + println!("}}"); + } +} + +pub fn type_constraint_system(conditions: &Conditions) -> System { + let builder = SystemBuilder::new(conditions); + builder.build() +} + +struct SystemBuilder<'a> { + conditions: &'a Conditions, + + system: System, + arm: Option, +} + +impl<'a> SystemBuilder<'a> { + fn new(conditions: &'a Conditions) -> Self { + Self { + conditions, + system: System::default(), + arm: None, + } + } + + fn build(mut self) -> System { + // Expression constraints. + for (i, expr) in self.conditions.exprs.iter().enumerate() { + self.veri_expr(ExprId(i), expr); + } + + // Assumptions. + for a in &self.conditions.assumptions { + self.boolean_value(*a, true); + } + + // Assertions. + for a in &self.conditions.assertions { + self.boolean(*a); + } + + // Calls. + for call in &self.conditions.calls { + self.call(call); + } + + // Qualifiers. + for qualifier in &self.conditions.qualifiers { + self.qualifier(qualifier); + } + + self.system + } + + fn veri_expr(&mut self, x: ExprId, expr: &Expr) { + match expr { + Expr::Const(c) => { + self.value(x, c.clone()); + } + Expr::Variable(v) => { + let ty = self.conditions.variables[v.index()].ty.clone(); + self.ty(x, ty); + } + Expr::Not(y) => { + self.boolean(x); + self.boolean(*y); + + // ((NOT X) OR (NOT Y)) + self.clause(vec![Literal::Not(x), Literal::Not(*y)]); + // (X OR Y) + self.clause(vec![Literal::Var(x), Literal::Var(*y)]); + } + Expr::And(y, z) => { + // TODO(mbm): clause implies boolean + self.boolean(x); + self.boolean(*y); + self.boolean(*z); + + // ((NOT X) OR Y) + self.clause(vec![Literal::Not(x), Literal::Var(*y)]); + // ((NOT X) OR Z) + self.clause(vec![Literal::Not(x), Literal::Var(*z)]); + // (X OR (NOT Y) OR (NOT Z)) + self.clause(vec![Literal::Var(x), Literal::Not(*y), Literal::Not(*z)]); + } + Expr::Or(y, z) => { + self.boolean(x); + self.boolean(*y); + self.boolean(*z); + + // ((NOT X) OR Y OR Z) + self.clause(vec![Literal::Not(x), Literal::Var(*y), Literal::Var(*z)]); + // (X OR (NOT Y)) + self.clause(vec![Literal::Var(x), Literal::Not(*y)]); + // (X OR (NOT Z)) + self.clause(vec![Literal::Var(x), Literal::Not(*z)]); + } + Expr::Imp(y, z) => { + self.boolean(x); + self.boolean(*y); + self.boolean(*z); + + // ((NOT X) OR (NOT Y) OR Z) + self.clause(vec![Literal::Not(x), Literal::Not(*y), Literal::Var(*z)]); + // (X OR Y) + self.clause(vec![Literal::Var(x), Literal::Var(*y)]); + // (X OR (NOT Z)) + self.clause(vec![Literal::Var(x), Literal::Not(*y)]); + } + Expr::Eq(y, z) => { + self.boolean(x); + self.same_type(*y, *z); + self.constraint(Constraint::Implies { + c: x, + then: Box::new(Constraint::Identical { x: *y, y: *z }), + }); + } + Expr::Lt(y, z) | Expr::Lte(y, z) => { + self.boolean(x); + self.integer(*y); + self.integer(*z); + } + Expr::BVUgt(y, z) + | Expr::BVUge(y, z) + | Expr::BVUlt(y, z) + | Expr::BVUle(y, z) + | Expr::BVSgt(y, z) + | Expr::BVSge(y, z) + | Expr::BVSlt(y, z) + | Expr::BVSle(y, z) + | Expr::FPEq(y, z) + | Expr::FPNe(y, z) + | Expr::FPLt(y, z) + | Expr::FPGt(y, z) + | Expr::FPLe(y, z) + | Expr::FPGe(y, z) + | Expr::BVSaddo(y, z) => { + self.boolean(x); + self.bit_vector(*y); + self.bit_vector(*z); + + self.same_type(*y, *z); + } + Expr::BVNot(y) | Expr::BVNeg(y) => { + self.bit_vector(x); + self.bit_vector(*y); + + self.same_type(x, *y); + } + Expr::Cls(y) | Expr::Clz(y) | Expr::Rev(y) | Expr::Popcnt(y) => { + self.bit_vector(x); + self.bit_vector(*y); + + self.same_type(x, *y); + } + Expr::Add(y, z) | Expr::Sub(y, z) | Expr::Mul(y, z) => { + self.integer(x); + self.integer(*y); + self.integer(*z); + } + Expr::BVAdd(y, z) + | Expr::BVSub(y, z) + | Expr::BVMul(y, z) + | Expr::BVSDiv(y, z) + | Expr::BVUDiv(y, z) + | Expr::BVSRem(y, z) + | Expr::BVURem(y, z) + | Expr::BVAnd(y, z) + | Expr::BVOr(y, z) + | Expr::BVXor(y, z) + | Expr::BVShl(y, z) + | Expr::BVLShr(y, z) + | Expr::BVAShr(y, z) + | Expr::BVRotl(y, z) + | Expr::BVRotr(y, z) + | Expr::FPAdd(y, z) + | Expr::FPSub(y, z) + | Expr::FPMul(y, z) + | Expr::FPDiv(y, z) + | Expr::FPMin(y, z) + | Expr::FPMax(y, z) => { + self.bit_vector(x); + self.bit_vector(*y); + self.bit_vector(*z); + + self.same_type(x, *y); + self.same_type(x, *z); + } + Expr::FPNeg(y) + | Expr::FPSqrt(y) + | Expr::FPCeil(y) + | Expr::FPFloor(y) + | Expr::FPNearest(y) + | Expr::FPTrunc(y) => { + self.bit_vector(x); + self.bit_vector(*y); + + self.same_type(x, *y); + } + // QUESTION(mbm): should we assert in type inference that FP ops are 32 or 64-bit? + Expr::FPIsZero(y) + | Expr::FPIsInfinite(y) + | Expr::FPIsNaN(y) + | Expr::FPIsNegative(y) + | Expr::FPIsPositive(y) => { + self.boolean(x); + self.bit_vector(*y); + } + Expr::Conditional(c, t, e) => { + self.boolean(*c); + self.same_type(x, *t); + self.same_type(x, *e); + } + Expr::BVZeroExt(w, y) | Expr::BVSignExt(w, y) | Expr::BVConvTo(w, y) => { + self.bit_vector(x); + self.integer(*w); + self.bit_vector(*y); + self.width_of(x, *w); + } + Expr::BVExtract(h, l, y) => { + let width = 1 + h + .checked_sub(*l) + .expect("high bit should not be less than low bit"); + self.bit_vector_of_width(x, width); + self.bit_vector(*y); + } + Expr::BVConcat(y, z) => { + self.bit_vector(x); + self.bit_vector(*y); + self.bit_vector(*z); + self.concat(x, *y, *z); + } + Expr::Int2BV(w, y) => { + self.bit_vector(x); + self.integer(*w); + self.integer(*y); + self.width_of(x, *w); + } + Expr::BV2Nat(y) => { + self.integer(x); + self.bit_vector(*y); + } + Expr::ToFP(w, y) | Expr::ToFPUnsigned(w, y) => { + self.integer(*w); + self.bit_vector(*y); + self.bit_vector(x); + self.width_of(x, *w); + self.width_of(*y, *w); + } + Expr::FPToUBV(w, y) | Expr::FPToSBV(w, y) => { + self.integer(*w); + self.bit_vector(*y); + self.bit_vector(x); + self.width_of(x, *w); + } + Expr::ToFPFromFP(w, y) => { + self.integer(*w); + self.bit_vector(*y); + self.bit_vector(x); + self.width_of(x, *w); + } + Expr::WidthOf(y) => { + self.integer(x); + self.bit_vector(*y); + self.width_of(*y, x); + } + Expr::FPPositiveInfinity(w) + | Expr::FPNegativeInfinity(w) + | Expr::FPPositiveZero(w) + | Expr::FPNegativeZero(w) + | Expr::FPNaN(w) => { + self.bit_vector(x); + self.integer(*w); + self.width_of(x, *w); + } + } + } + + fn call(&mut self, call: &Call) { + if call.signatures.is_empty() { + return; + } + + // Branch for the choice of term signature. + // + // We do this even for the case of a single signature, since it will + // preserve metadata about where the type assignment came from. + self.branch(); + + for sig in &call.signatures { + // Branch arm for + self.push_arm(Choice::TermInstantiation(call.term, sig.clone())); + + // Arguments. + assert_eq!(call.args.len(), sig.args.len()); + for (a, ty) in zip(&call.args, &sig.args) { + self.symbolic(a, ty.clone()); + } + + // Return. + self.symbolic(&call.ret, sig.ret.clone()); + + // Pop branch arm. + self.pop(); + } + } + + fn qualifier(&mut self, qualifier: &Qualifier) { + self.symbolic(&qualifier.value, qualifier.ty.clone()); + } + + fn symbolic(&mut self, v: &Symbolic, ty: Compound) { + match (v, ty) { + (Symbolic::Scalar(x), Compound::Primitive(ty)) => self.ty(*x, ty), + (Symbolic::Struct(fields), Compound::Struct(field_tys)) => { + assert_eq!(fields.len(), field_tys.len()); + for (field, field_ty) in zip(fields, field_tys) { + assert_eq!(field.name, field_ty.name.0); + self.symbolic(&field.value, field_ty.ty); + } + } + (Symbolic::Enum(e), Compound::Enum(enum_ty)) => { + assert_eq!(e.ty, enum_ty.id); + // Discriminant is an integer. + self.integer(e.discriminant); + // Variant types. + assert_eq!(e.variants.len(), enum_ty.variants.len()); + for (variant, variant_ty) in zip(&e.variants, &enum_ty.variants) { + assert_eq!(variant.id, variant_ty.id); + self.symbolic(&variant.value, variant_ty.ty()); + } + } + // QUESTION(mbm): should Option and Tuple be in a different enum so they don't appear in type inference? + (Symbolic::Option(_), _) => unimplemented!("option types unsupported"), + (Symbolic::Tuple(_), _) => unimplemented!("tuple types unsupported"), + (v, ty) => unreachable!("type mismatch: {v} of type {ty}"), + } + } + + fn bit_vector_of_width(&mut self, x: ExprId, width: usize) { + self.ty(x, Type::BitVector(Width::Bits(width))); + } + + fn bit_vector(&mut self, x: ExprId) { + self.ty(x, Type::BitVector(Width::Unknown)); + } + + fn integer(&mut self, x: ExprId) { + self.ty(x, Type::Int); + } + + fn boolean(&mut self, x: ExprId) { + self.ty(x, Type::Bool); + } + + fn ty(&mut self, x: ExprId, ty: Type) { + self.constraint(Constraint::Type { x, ty }); + } + + fn same_type(&mut self, x: ExprId, y: ExprId) { + self.constraint(Constraint::SameType { x, y }); + } + + fn width_of(&mut self, x: ExprId, w: ExprId) { + self.constraint(Constraint::WidthOf { x, w }); + } + + fn concat(&mut self, x: ExprId, l: ExprId, r: ExprId) { + self.constraint(Constraint::Concat { x, l, r }); + } + + fn boolean_value(&mut self, x: ExprId, b: bool) { + self.value(x, Const::Bool(b)); + } + + fn value(&mut self, x: ExprId, c: Const) { + self.constraint(Constraint::Value { x, c }); + } + + fn clause(&mut self, literals: Vec) { + self.constraint(Constraint::Clause { literals }) + } + + fn constraint(&mut self, constraint: Constraint) { + let current = match self.arm.as_mut() { + Some(arm) => &mut arm.constraints, + None => &mut self.system.constraints, + }; + current.push(constraint) + } + + fn branch(&mut self) { + self.system.branches.push(Branch::default()); + } + + fn push_arm(&mut self, choice: Choice) { + assert!(self.arm.is_none()); + self.arm = Some(Arm { + choice, + constraints: Vec::new(), + }); + } + + fn pop(&mut self) { + let arm = self.arm.take().expect("must have arm"); + self.system + .branches + .last_mut() + .expect("should have branch") + .arms + .push(arm); + } +} + +#[derive(Default, Clone)] +pub struct Assignment { + pub expr_type_value: HashMap, +} + +impl Assignment { + pub fn new() -> Self { + Self { + expr_type_value: HashMap::new(), + } + } + + pub fn is_concrete(&self) -> bool { + self.expr_type_value + .values() + .all(|tv| tv.ty().is_concrete()) + } + + pub fn satisfies_constraints(&self, constraints: &[Constraint]) -> Result<()> { + constraints + .iter() + .try_for_each(|c| self.satisfies_constraint(c)) + } + + pub fn satisfies_constraint(&self, constraint: &Constraint) -> Result<()> { + match *constraint { + Constraint::Type { x, ref ty } => self.expect_expr_type_refinement(x, ty), + Constraint::SameType { x, y } => self.expect_same_type(x, y), + Constraint::Identical { x, y } => self.expect_identical(x, y), + Constraint::WidthOf { x, w } => self.expect_width_of(x, w), + Constraint::Concat { x, l, r } => self.expect_concat(x, l, r), + Constraint::Value { x, ref c } => self.expect_value(x, c), + Constraint::Implies { c, ref then } => self.expect_implies(c, then), + Constraint::Clause { ref literals } => self.expect_clause(literals), + } + } + + pub fn assignment(&self, x: ExprId) -> Option<&TypeValue> { + self.expr_type_value.get(&x) + } + + pub fn try_assignment(&self, x: ExprId) -> Result<&TypeValue> { + self.assignment(x).ok_or(format_err!( + "expression {x} missing assignment", + x = x.index() + )) + } + + pub fn value(&self, x: ExprId) -> Option<&Const> { + self.assignment(x)?.as_value() + } + + pub fn try_value(&self, x: ExprId) -> Result<&Const> { + self.value(x).ok_or(format_err!( + "expression {x} should be a known value", + x = x.index() + )) + } + + pub fn bool_value(&self, x: ExprId) -> Option { + self.value(x)?.as_bool() + } + + pub fn int_value(&self, x: ExprId) -> Option { + self.value(x)?.as_int() + } + + pub fn try_int_value(&self, x: ExprId) -> Result { + self.int_value(x).ok_or(format_err!( + "expression {x} should be a known integer value", + x = x.index() + )) + } + + pub fn literal(&self, lit: &Literal) -> Option { + match *lit { + Literal::Var(x) => self.bool_value(x), + Literal::Not(x) => Some(!self.bool_value(x)?), + } + } + + fn expect_expr_type_refinement(&self, x: ExprId, base: &Type) -> Result<()> { + let tv = self.try_assignment(x)?; + if !tv.refines_type(base) { + bail!("expected type {tv} to be refinement of {base}") + } + Ok(()) + } + + fn expect_same_type(&self, x: ExprId, y: ExprId) -> Result<()> { + let tx = self.try_assignment(x)?.ty(); + let ty = self.try_assignment(y)?.ty(); + if tx != ty { + bail!( + "expressions {x} and {y} should have same type: got {tx} and {ty}", + x = x.index(), + y = y.index() + ) + } + Ok(()) + } + + fn expect_identical(&self, x: ExprId, y: ExprId) -> Result<()> { + let tvx = self.try_assignment(x)?; + let tvy = self.try_assignment(y)?; + if tvx != tvy { + bail!( + "expressions {x} and {y} should be identical: got {tvx} and {tvy}", + x = x.index(), + y = y.index() + ) + } + Ok(()) + } + + pub fn bit_vector_width(&self, x: ExprId) -> Option { + self.assignment(x)?.ty().as_bit_vector_width()?.as_bits() + } + + pub fn try_bit_vector_width(&self, x: ExprId) -> Result { + self.bit_vector_width(x).ok_or(format_err!( + "expression {x} should be a bit-vector of known width", + x = x.index() + )) + } + + fn expect_width_of(&self, x: ExprId, w: ExprId) -> Result<()> { + // Expression x should be a concrete bitvector. + let width = self.try_bit_vector_width(x)?; + + // Expression w should be an integer equal to the width. + self.expect_value(w, &Const::Int(width.try_into().unwrap()))?; + + Ok(()) + } + + fn expect_concat(&self, x: ExprId, l: ExprId, r: ExprId) -> Result<()> { + // All inputs should be bitvectors of known width. + let x_width = self.try_bit_vector_width(x)?; + let l_width = self.try_bit_vector_width(l)?; + let r_width = self.try_bit_vector_width(r)?; + + // Verify x width is the sum of input widths. + let concat_width = l_width + .checked_add(r_width) + .expect("concat width should not overflow"); + if x_width != concat_width { + bail!( + "expression {x} should be the concatenation of {l} and {r}", + x = x.index(), + l = l.index(), + r = r.index() + ); + } + + Ok(()) + } + + fn expect_value(&self, x: ExprId, expect: &Const) -> Result<()> { + let got = self.try_value(x)?; + if got != expect { + bail!("expected value {expect}; got {got}"); + } + Ok(()) + } + + fn expect_implies(&self, c: ExprId, then: &Constraint) -> Result<()> { + if self.bool_value(c) == Some(true) { + self.satisfies_constraint(then) + } else { + Ok(()) + } + } + + fn expect_clause(&self, literals: &[Literal]) -> Result<()> { + for literal in literals { + match self.literal(literal) { + Some(true) | None => { + return Ok(()); + } + Some(false) => { + continue; + } + } + } + bail!("false clause"); + } + + pub fn pretty_print(&self, conditions: &Conditions) { + for (i, expr) in conditions.exprs.iter().enumerate() { + print!("{i}:\t"); + match self.expr_type_value.get(&ExprId(i)) { + None => print!("false\t-"), + Some(tv) => print!("{}\t{tv}", tv.ty().is_concrete()), + } + println!("\t{expr}"); + } + } +} + +pub struct Conflict { + pub x: ExprId, + pub reason: String, +} + +impl Conflict { + fn new(x: ExprId, reason: String) -> Self { + Self { x, reason } + } + + pub fn diagnostic(&self, conditions: &Conditions, files: &Files) -> String { + if let Some(pos) = conditions.pos.get(&self.x) { + format!( + "{position}: {reason}", + position = pos.pretty_print_line(files), + reason = self.reason + ) + } else { + self.reason.clone() + } + } +} + +pub enum Status { + Solved, + Inapplicable(Conflict), + Underconstrained, + TypeError(Conflict), +} + +impl std::fmt::Display for Status { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Status::Solved => write!(f, "solved"), + Status::Inapplicable(..) => write!(f, "inapplicable"), + Status::Underconstrained => write!(f, "underconstrained"), + Status::TypeError(..) => write!(f, "type error"), + } + } +} + +pub struct Solution { + pub status: Status, + pub choices: Vec, + pub assignment: Assignment, +} + +#[derive(Clone)] +pub struct Solver { + assignment: Assignment, +} + +impl Solver { + pub fn new() -> Self { + Self { + assignment: Assignment::new(), + } + } + + pub fn solve(mut self, system: &System) -> Vec { + // Deduce assignments from constraints. + let result = self.propagate(&system.constraints); + if let Err(status) = result { + return vec![Solution { + status, + choices: system.choices.clone(), + assignment: self.assignment, + }]; + } + + // Done? + if system.branches.is_empty() { + let status = if self.assignment.is_concrete() { + Status::Solved + } else { + Status::Underconstrained + }; + return vec![Solution { + status, + choices: system.choices.clone(), + assignment: self.assignment, + }]; + }; + + // Fork. + let mut solutions = Vec::new(); + for child in system.fork() { + let sub = self.clone(); + solutions.extend(sub.solve(&child)); + } + + solutions + } + + fn propagate(&mut self, constraints: &[Constraint]) -> Result<(), Status> { + // Iterate until no changes. + while self.iterate(constraints)? {} + Ok(()) + } + + fn iterate(&mut self, constraints: &[Constraint]) -> Result { + let mut change = false; + for constraint in constraints { + // TODO(mbm): remove satisfied constraints from list + change |= self.constraint(constraint)?; + } + Ok(change) + } + + fn constraint(&mut self, constraint: &Constraint) -> Result { + log::trace!("process type constraint: {constraint}"); + match constraint { + Constraint::Type { x, ty } => self.set_type(*x, ty.clone()), + Constraint::SameType { x, y } => self.same_type(*x, *y), + Constraint::Identical { x, y } => self.identical(*x, *y), + Constraint::WidthOf { x, w } => self.width_of(*x, *w), + Constraint::Concat { x, l, r } => self.concat(*x, *l, *r), + Constraint::Value { x, c } => self.set_value(*x, c.clone()), + Constraint::Implies { c, then } => self.implies(*c, then), + Constraint::Clause { literals } => self.clause(literals), + } + } + + fn set_type_value(&mut self, x: ExprId, tv: TypeValue) -> Result { + log::trace!("set type value: {x:?} = {tv:?}"); + + // If we don't have an assignment for the expression, record it. + if let Entry::Vacant(e) = self.assignment.expr_type_value.entry(x) { + e.insert(tv); + return Ok(true); + } + + // If we do, merge this type value with the existing one. + let existing = &self.assignment.expr_type_value[&x]; + let merged = TypeValue::merge(existing, &tv).ok_or_else(|| { + if !existing.ty().is_compatible_with(&tv.ty()) { + Status::TypeError(Conflict::new( + x, + format!("concrete type error between types:\n\t{existing}\n\t{tv}"), + )) + } else { + Status::Inapplicable(Conflict::new( + x, + format!("inapplicable set type value: {existing:?} = {tv:?}"), + )) + } + })?; + if merged != *existing { + self.assignment.expr_type_value.insert(x, merged); + return Ok(true); + } + + // No change. + Ok(false) + } + + fn set_type(&mut self, x: ExprId, ty: Type) -> Result { + self.set_type_value(x, TypeValue::Type(ty)) + } + + fn set_bit_vector_width(&mut self, x: ExprId, bits: usize) -> Result { + self.set_type(x, Type::BitVector(Width::Bits(bits))) + } + + fn same_type(&mut self, x: ExprId, y: ExprId) -> Result { + // TODO(mbm): union find + // TODO(mbm): simplify by initializing all expression types to unknown + match ( + self.assignment.expr_type_value.get(&x).cloned(), + self.assignment.expr_type_value.get(&y).cloned(), + ) { + (None, None) => Ok(false), + (Some(tvx), None) => self.set_type(y, tvx.ty()), + (None, Some(tvy)) => self.set_type(x, tvy.ty()), + (Some(tvx), Some(tvy)) => Ok(self.set_type(x, tvy.ty())? | self.set_type(y, tvx.ty())?), + } + } + + fn identical(&mut self, x: ExprId, y: ExprId) -> Result { + match ( + self.assignment.expr_type_value.get(&x).cloned(), + self.assignment.expr_type_value.get(&y).cloned(), + ) { + (None, None) => Ok(false), + (Some(tvx), None) => self.set_type_value(y, tvx), + (None, Some(tvy)) => self.set_type_value(x, tvy), + (Some(tvx), Some(tvy)) => { + Ok(self.set_type_value(x, tvy)? | self.set_type_value(y, tvx)?) + } + } + } + + fn width_of(&mut self, x: ExprId, w: ExprId) -> Result { + match ( + self.assignment.expr_type_value.get(&x), + self.assignment.expr_type_value.get(&w), + ) { + ( + Some( + &TypeValue::Type(Type::BitVector(Width::Bits(width))) + | &TypeValue::Value(Const::BitVector(width, _)), + ), + _, + ) => self.set_int_value(w, width.try_into().unwrap()), + (_, Some(&TypeValue::Value(Const::Int(v)))) => { + self.set_bit_vector_width(x, v.try_into().unwrap()) + } + _ => Ok(false), + } + } + + fn concat(&mut self, x: ExprId, l: ExprId, r: ExprId) -> Result { + match ( + self.assignment.bit_vector_width(x), + self.assignment.bit_vector_width(l), + self.assignment.bit_vector_width(r), + ) { + // Two known: we can infer the third. + (None, Some(lw), Some(rw)) => { + // Width equation: |x| = |l| + |r| + self.set_bit_vector_width(x, lw + rw) + } + (Some(xw), None, Some(rw)) => { + // Width equation: |l| = |x| - |r| + self.set_bit_vector_width( + l, + xw.checked_sub(rw).ok_or_else(|| { + Status::Inapplicable(Conflict::new( + l, + format!("inapplicable concat xw - rw: {l:?} = {r:?}"), + )) + })?, + ) + } + (Some(xw), Some(lw), None) => { + // Width equation: |r| = |x| - |l| + self.set_bit_vector_width( + r, + xw.checked_sub(lw).ok_or_else(|| { + Status::Inapplicable(Conflict::new( + r, + format!("inapplicable concat xw - lw: {l:?} = {r:?}"), + )) + })?, + ) + } + + // Zero or one known: cannot deduce anything. + (None, None, None) + | (None, None, Some(_)) + | (None, Some(_), None) + | (Some(_), None, None) => Ok(false), + + // All known: verify correctness. + (Some(xw), Some(lw), Some(rw)) => { + if xw != lw + rw { + Err(Status::Inapplicable(Conflict::new( + x, + format!("inapplicable concat known: {l:?} = {r:?}"), + ))) + } else { + Ok(false) + } + } + } + } + + fn implies(&mut self, c: ExprId, then: &Constraint) -> Result { + if self.assignment.bool_value(c) == Some(true) { + self.constraint(then) + } else { + Ok(false) + } + } + + fn clause(&mut self, literals: &[Literal]) -> Result { + // Check if we can propogate the value of a single unknown literal. + let mut unknown = None; + for literal in literals { + match (self.assignment.literal(literal), unknown) { + // One disjunction is known true. Can't deduce anything else. + (Some(true), _) => { + return Ok(false); + } + // Known false: also deduce nothing. + (Some(false), _) => { + continue; + } + // First unknown literal. + (None, None) => { + unknown = Some(literal); + } + // More than one unknown literal: deduce nothing. + (None, Some(_)) => { + return Ok(false); + } + } + } + + // Assign true. + match unknown { + Some(lit) => self.set_literal(lit, true), + None => Ok(false), + } + } + + fn set_literal(&mut self, lit: &Literal, b: bool) -> Result { + match *lit { + Literal::Var(x) => self.set_bool_value(x, b), + Literal::Not(x) => self.set_bool_value(x, !b), + } + } + + fn set_bool_value(&mut self, x: ExprId, b: bool) -> Result { + self.set_value(x, Const::Bool(b)) + } + + fn set_int_value(&mut self, x: ExprId, v: i128) -> Result { + self.set_value(x, Const::Int(v)) + } + + fn set_value(&mut self, x: ExprId, c: Const) -> Result { + self.set_type_value(x, TypeValue::Value(c)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::testing::{assert_partial_order_properties, assert_strictly_increasing}; + + #[test] + fn test_type_value_partial_order_bit_vector() { + assert_strictly_increasing(&[ + TypeValue::Type(Type::Unknown), + TypeValue::Type(Type::BitVector(Width::Unknown)), + TypeValue::Type(Type::BitVector(Width::Bits(64))), + TypeValue::Value(Const::BitVector(64, 42u8.into())), + ]); + } + + #[test] + fn test_type_value_partial_order_int() { + assert_strictly_increasing(&[ + TypeValue::Type(Type::Unknown), + TypeValue::Type(Type::Int), + TypeValue::Value(Const::Int(42)), + ]); + } + + #[test] + fn test_type_value_partial_order_bool() { + assert_strictly_increasing(&[ + TypeValue::Type(Type::Unknown), + TypeValue::Type(Type::Bool), + TypeValue::Value(Const::Bool(true)), + ]); + } + + #[test] + fn test_type_value_partial_order_unspecified() { + assert_strictly_increasing(&[ + TypeValue::Type(Type::Unspecified), + TypeValue::Value(Const::Unspecified), + ]); + } + + #[test] + fn test_type_value_partial_order_properties() { + assert_partial_order_properties(&[ + // Unknown + TypeValue::Type(Type::Unknown), + // BitVectors + TypeValue::Type(Type::BitVector(Width::Unknown)), + TypeValue::Type(Type::BitVector(Width::Bits(32))), + TypeValue::Value(Const::BitVector(32, 42u8.into())), + TypeValue::Value(Const::BitVector(32, 43u8.into())), + TypeValue::Type(Type::BitVector(Width::Bits(64))), + TypeValue::Value(Const::BitVector(64, 42u8.into())), + TypeValue::Value(Const::BitVector(64, 43u8.into())), + // Int + TypeValue::Type(Type::Int), + TypeValue::Value(Const::Int(42)), + TypeValue::Value(Const::Int(43)), + // Bool + TypeValue::Type(Type::Bool), + TypeValue::Value(Const::Bool(false)), + TypeValue::Value(Const::Bool(true)), + // Unspecified + TypeValue::Type(Type::Unspecified), + TypeValue::Value(Const::Unspecified), + ]); + } +} diff --git a/cranelift/isle/veri/veri/src/types.rs b/cranelift/isle/veri/veri/src/types.rs new file mode 100644 index 000000000000..c1b89d7c0dad --- /dev/null +++ b/cranelift/isle/veri/veri/src/types.rs @@ -0,0 +1,488 @@ +use std::cmp::Ordering; + +use anyhow::Result; +use cranelift_isle::{ + ast::{Ident, ModelType}, + lexer::Pos, + sema::{self, Sym, TypeEnv, TypeId, VariantId}, +}; +use num_bigint::BigUint; + +/// Width of a bit vector. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum Width { + Unknown, + Bits(usize), +} + +impl Width { + pub fn as_bits(&self) -> Option { + match self { + Width::Unknown => None, + Width::Bits(bits) => Some(*bits), + } + } +} + +impl PartialOrd for Width { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Width::Unknown, Width::Unknown) => Some(Ordering::Equal), + (Width::Unknown, Width::Bits(_)) => Some(Ordering::Less), + (Width::Bits(_), Width::Unknown) => Some(Ordering::Greater), + (Width::Bits(l), Width::Bits(r)) if l == r => Some(Ordering::Equal), + (Width::Bits(_), Width::Bits(_)) => None, + } + } +} + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum Type { + Unspecified, + Unknown, + BitVector(Width), + Int, + Bool, + Unit, +} + +impl Type { + pub fn is_concrete(&self) -> bool { + match self { + Type::Unspecified => true, + Type::Unknown | Type::BitVector(Width::Unknown) => false, + Type::BitVector(Width::Bits(_)) | Type::Int | Type::Bool | Type::Unit => true, + } + } + + pub fn as_bit_vector_width(&self) -> Option<&Width> { + match self { + Type::BitVector(w) => Some(w), + _ => None, + } + } + + pub fn is_compatible_with(&self, other: &Type) -> bool { + match (self, other) { + (Type::Unknown, _) + | (_, Type::Unknown) + | (Type::Unspecified, Type::Unspecified) + | (Type::Unit, Type::Unit) + | (Type::Bool, Type::Bool) + | (Type::Int, Type::Int) + | (Type::BitVector(_), Type::BitVector(_)) => true, + _ => false, + } + } +} + +impl std::fmt::Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Type::Unspecified => write!(f, "\u{2a33}"), + Type::Unknown => write!(f, "unk"), + Type::BitVector(Width::Bits(w)) => write!(f, "bv {w}"), + Type::BitVector(Width::Unknown) => write!(f, "bv _"), + Type::Int => write!(f, "int"), + Type::Bool => write!(f, "bool"), + Type::Unit => write!(f, "unit"), + } + } +} + +impl PartialOrd for Type { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + // Unspecified is equal to itself, but otherwise incomparible. + (Type::Unspecified, Type::Unspecified) => Some(Ordering::Equal), + (Type::Unspecified, _) | (_, Type::Unspecified) => None, + + (Type::Unknown, Type::Unknown) => Some(Ordering::Equal), + (Type::Unknown, _) => Some(Ordering::Less), + (_, Type::Unknown) => Some(Ordering::Greater), + (Type::BitVector(l), Type::BitVector(r)) => l.partial_cmp(r), + (Type::Int, Type::Int) => Some(Ordering::Equal), + (Type::Bool, Type::Bool) => Some(Ordering::Equal), + (Type::Unit, Type::Unit) => Some(Ordering::Equal), + (_, _) => None, + } + } +} + +#[derive(Debug, Clone)] +pub enum Compound { + Primitive(Type), + Struct(Vec), + Enum(Enum), + ExtEnum{base: Enum, extra: Vec}, // new compound type: ExtEnum + // TODO(mbm): intern name identifier + Named(Ident), +} + +#[derive(Debug, Clone)] +pub struct Field { + // TODO(mbm): intern name identifier + pub name: Ident, + pub ty: Compound, +} + +impl Field { + fn from_isle(field: &sema::Field, tyenv: &TypeEnv) -> Self { + let ty = &tyenv.types[field.ty.index()]; + Self { + name: Ident(tyenv.syms[field.name.index()].clone(), Pos::default()), + ty: Compound::named_from_isle(ty, tyenv), + } + } + + /// Resolve any named types. + pub fn resolve(&self, lookup: &mut F) -> Result + where + F: FnMut(&Ident) -> Result, + { + Ok(Field { + name: self.name.clone(), + ty: self.ty.resolve(lookup)?, + }) + } +} + +#[derive(Debug, Clone)] +pub struct Variant { + pub name: Ident, + pub id: VariantId, + pub fields: Vec, +} + +impl Variant { + fn from_isle(variant: &sema::Variant, tyenv: &TypeEnv) -> Self { + Self { + name: Ident(tyenv.syms[variant.name.index()].clone(), variant.pos), + id: variant.id, + fields: variant + .fields + .iter() + .map(|f| Field::from_isle(f, tyenv)) + .collect(), + } + } + + pub fn is_unit(&self) -> bool { + self.fields.is_empty() + } + + pub fn ty(&self) -> Compound { + Compound::Struct(self.fields.clone()) + } + + /// Resolve any named types. + pub fn resolve(&self, lookup: &mut F) -> Result + where + F: FnMut(&Ident) -> Result, + { + Ok(Variant { + name: self.name.clone(), + id: self.id, + fields: self + .fields + .iter() + .map(|f| f.resolve(lookup)) + .collect::>()?, + }) + } +} + +impl std::fmt::Display for Variant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_unit() { + write!(f, "{name}", name = self.name.0) + } else { + write!(f, "{name} {ty}", name = self.name.0, ty = self.ty()) + } + } +} + +#[derive(Debug, Clone)] +pub struct Enum { + pub name: Ident, + pub id: TypeId, + pub variants: Vec, +} + +impl Enum { + pub fn from_isle( + name: Sym, + id: TypeId, + variants: &[sema::Variant], + pos: Pos, + tyenv: &TypeEnv, + ) -> Self { + Self { + name: Ident(tyenv.syms[name.index()].clone(), pos), + id, + variants: variants + .iter() + .map(|v| Variant::from_isle(v, tyenv)) + .collect(), + } + } + + /// Resolve any named types. + pub fn resolve(&self, lookup: &mut F) -> Result + where + F: FnMut(&Ident) -> Result, + { + Ok(Self { + name: self.name.clone(), + id: self.id, + variants: self + .variants + .iter() + .map(|v| v.resolve(lookup)) + .collect::>()?, + }) + } +} + +impl Compound { + pub fn from_ast(model: &ModelType) -> Self { + match model { + ModelType::Unspecified => Self::Primitive(Type::Unspecified), + ModelType::Auto => Self::Primitive(Type::Unknown), + ModelType::Int => Self::Primitive(Type::Int), + ModelType::Bool => Self::Primitive(Type::Bool), + ModelType::Unit => Self::Primitive(Type::Unit), + ModelType::BitVec(None) => Self::Primitive(Type::BitVector(Width::Unknown)), + ModelType::BitVec(Some(bits)) => Self::Primitive(Type::BitVector(Width::Bits(*bits))), + ModelType::Struct(fields) => Self::Struct( + fields + .iter() + .map(|m| Field { + name: m.name.clone(), + ty: Self::from_ast(&m.ty), + }) + .collect(), + ), + ModelType::Named(name) => Self::Named(name.clone()), + ModelType::ExtEnum(variants) => { + // Build `types::Variant`s + let lowered_variants: Vec = variants + .iter() + .enumerate() + .map(|(i, mv)| { + let fields = mv.fields + .iter() + .map(|mf| Field { + name: mf.name.clone(), + ty: Self::from_ast(&mf.ty), + }) + .collect(); + + Variant { + name: mv.name.clone(), + id: VariantId(i), + fields, + } + }) + .collect(); + + // Wrap in our Compound::ExtEnum + Self::ExtEnum { + base: Enum { + id: TypeId(0), // placeholder, depends on type assignment + name: Ident("anon_extenum".to_string(), Pos::default()), // minimal Ident + variants: lowered_variants, + }, + extra: vec![], // can fill later if ExtEnum carries extras + } + } + } + } + + /// Derive a type corresponding to the given ISLE type, if possible. For + /// ISLE internal enumerations, this will build the corresponding VeriISLE + /// enum representation. + pub fn from_isle(ty: &sema::Type, tyenv: &TypeEnv) -> Option { + match ty { + sema::Type::Enum { + name, + id, + variants, + pos, + .. + } if !variants.is_empty() => Some(Self::Enum(Enum::from_isle( + *name, *id, variants, *pos, tyenv, + ))), + _ => None, + } + } + + /// Build a named reference to the given ISLE type. + pub fn named_from_isle(ty: &sema::Type, tyenv: &TypeEnv) -> Self { + Self::Named(Ident(ty.name(tyenv).to_string(), ty.pos())) + } + + pub fn as_primitive(&self) -> Option<&Type> { + match self { + Compound::Primitive(ty) => Some(ty), + _ => None, + } + } + + pub fn as_enum(&self) -> Option<&Enum> { + match self { + Compound::Enum(e) => Some(e), + Compound::ExtEnum { base, .. } => Some(base), + _ => None, + } + } + + /// Resolve any named types. + pub fn resolve(&self, lookup: &mut F) -> Result + where + F: FnMut(&Ident) -> Result, + { + match self { + Compound::Primitive(_) => Ok(self.clone()), + Compound::Struct(fields) => Ok(Compound::Struct( + fields + .iter() + .map(|f| f.resolve(lookup)) + .collect::>()?, + )), + Compound::Enum(e) => Ok(Compound::Enum(e.resolve(lookup)?)), + Compound::ExtEnum { base, extra } => { + let base = base.resolve(lookup)?; + let extra = extra.iter().map(|f| f.resolve(lookup)).collect::>()?; + Ok(Compound::ExtEnum { base, extra }) + } + Compound::Named(name) => { + // TODO(mbm): named type model cycle detection + let ty = lookup(name)?; + ty.resolve(lookup) + } + } + } +} + +impl std::fmt::Display for Compound { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Compound::Primitive(ty) => ty.fmt(f), + Compound::Struct(fields) => write!( + f, + "{{{fields}}}", + fields = fields + .iter() + .map(|f| format!("{}: {}", f.name.0, f.ty)) + .collect::>() + .join(", ") + ), + Compound::Enum(e) => { + write!(f, "enum({name})", name = e.name.0,) + } + Compound::ExtEnum { base, extra } => { + // format: "extenum(EnumName { ...extra fields... })" + write!( + f, + "extenum({} + [{}])", + base.name.0, + extra.iter() + .map(|f| format!("{}: {}", f.name.0, f.ty)) + .collect::>() + .join(", ") + ) + } + Compound::Named(name) => write!(f, "{}", name.0), + } + } +} + +// QUESTION(mbm): can this be deduped with the corresponding spec constant type? +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum Const { + Bool(bool), + Int(i128), + BitVector(usize, BigUint), + Unspecified, +} + +impl Const { + pub fn ty(&self) -> Type { + match self { + Const::Bool(_) => Type::Bool, + Const::Int(_) => Type::Int, + Const::BitVector(w, _) => Type::BitVector(Width::Bits(*w)), + Const::Unspecified => Type::Unspecified, + } + } + + pub fn as_bool(&self) -> Option { + match self { + Const::Bool(b) => Some(*b), + _ => None, + } + } + + pub fn as_int(&self) -> Option { + match self { + Const::Int(v) => Some(*v), + _ => None, + } + } +} + +impl std::fmt::Display for Const { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Const::Bool(b) => write!(f, "{b}"), + Const::Int(v) => write!(f, "{v}"), + Const::BitVector(bits, v) => { + if bits % 4 == 0 { + write!(f, "#x{v:0>nibbles$x}", nibbles = bits / 4) + } else { + write!(f, "#b{v:0>bits$b}") + } + } + Const::Unspecified => write!(f, "\u{2a33}"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::testing::assert_partial_order_properties; + + #[test] + fn test_width_partial_order_less_than() { + assert!(Width::Unknown < Width::Bits(64)); + } + + #[test] + fn test_width_partial_order_properties() { + assert_partial_order_properties(&[Width::Unknown, Width::Bits(32), Width::Bits(64)]); + } + + #[test] + fn test_type_partial_order_less_than() { + assert!(Type::Unknown < Type::BitVector(Width::Unknown)); + assert!(Type::BitVector(Width::Unknown) < Type::BitVector(Width::Bits(64))); + assert!(Type::Unknown < Type::Int); + assert!(Type::Unknown < Type::Bool); + } + + #[test] + fn test_type_partial_order_properties() { + assert_partial_order_properties(&[ + Type::Unspecified, + Type::Unknown, + Type::BitVector(Width::Unknown), + Type::BitVector(Width::Bits(32)), + Type::BitVector(Width::Bits(64)), + Type::Int, + Type::Bool, + Type::Unit, + ]); + } +} diff --git a/cranelift/isle/veri/veri/src/veri.rs b/cranelift/isle/veri/veri/src/veri.rs new file mode 100644 index 000000000000..9f05c4add706 --- /dev/null +++ b/cranelift/isle/veri/veri/src/veri.rs @@ -0,0 +1,2515 @@ +use crate::{ + expand::{Constrain, Expansion}, + program::Program, + spec::{self, Arm, Constructor, Signature, State}, + trie::{binding_type, BindingType}, + types::{Compound, Const, Type, Variant, Width}, +}; +use anyhow::{bail, format_err, Context, Error, Result}; +use cranelift_isle::{ + ast::Ident, + lexer::Pos, + sema::{Sym, TermId, TypeId, VariantId}, + trie_again::{Binding, BindingId, Constraint, TupleIndex}, +}; +use std::{ + collections::{hash_map::Entry, HashMap, HashSet}, + iter::zip, +}; + +declare_id!( + /// The id of an expression within verification Conditions. + #[must_use] + ExprId +); + +declare_id!( + /// The id of a variable within verification Conditions. + VariableId +); + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum Expr { + // Terminals. + Const(Const), + Variable(VariableId), + + // Boolean. + Not(ExprId), + And(ExprId, ExprId), + Or(ExprId, ExprId), + Imp(ExprId, ExprId), + Eq(ExprId, ExprId), + Lt(ExprId, ExprId), + Lte(ExprId, ExprId), + + BVUgt(ExprId, ExprId), + BVUge(ExprId, ExprId), + BVUlt(ExprId, ExprId), + BVUle(ExprId, ExprId), + + BVSgt(ExprId, ExprId), + BVSge(ExprId, ExprId), + BVSlt(ExprId, ExprId), + BVSle(ExprId, ExprId), + + BVSaddo(ExprId, ExprId), + + // Unary. + BVNot(ExprId), + BVNeg(ExprId), + Cls(ExprId), + Clz(ExprId), + Rev(ExprId), + Popcnt(ExprId), + + // Binary. + Add(ExprId, ExprId), + Sub(ExprId, ExprId), + Mul(ExprId, ExprId), + BVAdd(ExprId, ExprId), + BVSub(ExprId, ExprId), + BVMul(ExprId, ExprId), + BVSDiv(ExprId, ExprId), + BVUDiv(ExprId, ExprId), + BVSRem(ExprId, ExprId), + BVURem(ExprId, ExprId), + BVAnd(ExprId, ExprId), + BVOr(ExprId, ExprId), + BVXor(ExprId, ExprId), + BVShl(ExprId, ExprId), + BVLShr(ExprId, ExprId), + BVAShr(ExprId, ExprId), + BVRotl(ExprId, ExprId), + BVRotr(ExprId, ExprId), + + // ITE + Conditional(ExprId, ExprId, ExprId), + + // Bitwidth conversion. + BVZeroExt(ExprId, ExprId), + BVSignExt(ExprId, ExprId), + BVConvTo(ExprId, ExprId), + + // Extract specified bit range. + BVExtract(usize, usize, ExprId), + + // Concatenate bitvectors. + BVConcat(ExprId, ExprId), + + // Integer conversion. + Int2BV(ExprId, ExprId), + BV2Nat(ExprId), + + // Bitwidth. + WidthOf(ExprId), + + // Floating point conversion. + ToFP(ExprId, ExprId), + ToFPUnsigned(ExprId, ExprId), + ToFPFromFP(ExprId, ExprId), + FPToUBV(ExprId, ExprId), + FPToSBV(ExprId, ExprId), + + // Floating point. + FPPositiveInfinity(ExprId), + FPNegativeInfinity(ExprId), + FPPositiveZero(ExprId), + FPNegativeZero(ExprId), + FPNaN(ExprId), + FPEq(ExprId, ExprId), + FPNe(ExprId, ExprId), + FPLt(ExprId, ExprId), + FPGt(ExprId, ExprId), + FPLe(ExprId, ExprId), + FPGe(ExprId, ExprId), + FPAdd(ExprId, ExprId), + FPSub(ExprId, ExprId), + FPMul(ExprId, ExprId), + FPDiv(ExprId, ExprId), + FPMin(ExprId, ExprId), + FPMax(ExprId, ExprId), + FPNeg(ExprId), + FPCeil(ExprId), + FPFloor(ExprId), + FPSqrt(ExprId), + FPTrunc(ExprId), + FPNearest(ExprId), + FPIsZero(ExprId), + FPIsInfinite(ExprId), + FPIsNaN(ExprId), + FPIsNegative(ExprId), + FPIsPositive(ExprId), +} + +impl Expr { + pub fn is_variable(&self) -> bool { + matches!(self, Self::Variable(_)) + } + + pub fn pure(&self) -> bool { + match self { + Expr::BVConvTo(..) => false, + _ => true, + } + } + + pub fn sources(&self) -> Vec { + match self { + Expr::Const(_) | Expr::Variable(_) => Vec::new(), + // Unary + Expr::Not(x) + | Expr::BVNot(x) + | Expr::BVNeg(x) + | Expr::BVExtract(_, _, x) + | Expr::BV2Nat(x) + | Expr::Cls(x) + | Expr::Clz(x) + | Expr::Rev(x) + | Expr::Popcnt(x) + | Expr::WidthOf(x) + | Expr::FPPositiveInfinity(x) + | Expr::FPNegativeInfinity(x) + | Expr::FPPositiveZero(x) + | Expr::FPNegativeZero(x) + | Expr::FPNaN(x) + | Expr::FPNeg(x) + | Expr::FPCeil(x) + | Expr::FPFloor(x) + | Expr::FPSqrt(x) + | Expr::FPTrunc(x) + | Expr::FPNearest(x) + | Expr::FPIsZero(x) + | Expr::FPIsInfinite(x) + | Expr::FPIsNaN(x) + | Expr::FPIsNegative(x) + | Expr::FPIsPositive(x) => vec![*x], + + // Binary + Expr::And(x, y) + | Expr::Or(x, y) + | Expr::Imp(x, y) + | Expr::Eq(x, y) + | Expr::Lt(x, y) + | Expr::Lte(x, y) + | Expr::Add(x, y) + | Expr::Sub(x, y) + | Expr::Mul(x, y) + | Expr::BVUgt(x, y) + | Expr::BVUge(x, y) + | Expr::BVUlt(x, y) + | Expr::BVUle(x, y) + | Expr::BVSgt(x, y) + | Expr::BVSge(x, y) + | Expr::BVSlt(x, y) + | Expr::BVSle(x, y) + | Expr::BVSaddo(x, y) + | Expr::BVAdd(x, y) + | Expr::BVSub(x, y) + | Expr::BVMul(x, y) + | Expr::BVSDiv(x, y) + | Expr::BVUDiv(x, y) + | Expr::BVSRem(x, y) + | Expr::BVURem(x, y) + | Expr::BVAnd(x, y) + | Expr::BVOr(x, y) + | Expr::BVXor(x, y) + | Expr::BVShl(x, y) + | Expr::BVLShr(x, y) + | Expr::BVAShr(x, y) + | Expr::BVRotl(x, y) + | Expr::BVRotr(x, y) + | Expr::BVZeroExt(x, y) + | Expr::BVSignExt(x, y) + | Expr::BVConvTo(x, y) + | Expr::Int2BV(x, y) + | Expr::ToFP(x, y) + | Expr::ToFPUnsigned(x, y) + | Expr::ToFPFromFP(x, y) + | Expr::FPToUBV(x, y) + | Expr::FPToSBV(x, y) + | Expr::BVConcat(x, y) + | Expr::FPEq(x, y) + | Expr::FPNe(x, y) + | Expr::FPLt(x, y) + | Expr::FPGt(x, y) + | Expr::FPLe(x, y) + | Expr::FPGe(x, y) + | Expr::FPAdd(x, y) + | Expr::FPSub(x, y) + | Expr::FPMul(x, y) + | Expr::FPDiv(x, y) + | Expr::FPMin(x, y) + | Expr::FPMax(x, y) => vec![*x, *y], + + // Ternary + Expr::Conditional(c, t, e) => vec![*c, *t, *e], + } + } +} + +impl std::fmt::Display for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Expr::Const(c) => write!(f, "const({c})"), + Expr::Variable(v) => write!(f, "var({})", v.index()), + Expr::Not(x) => write!(f, "!{}", x.index()), + Expr::And(x, y) => write!(f, "{} && {}", x.index(), y.index()), + Expr::Or(x, y) => write!(f, "{} || {}", x.index(), y.index()), + Expr::Imp(x, y) => write!(f, "{} => {}", x.index(), y.index()), + Expr::Eq(x, y) => write!(f, "{} == {}", x.index(), y.index()), + Expr::Lt(x, y) => write!(f, "{} < {}", x.index(), y.index()), + Expr::Lte(x, y) => write!(f, "{} <= {}", x.index(), y.index()), + Expr::Add(x, y) => write!(f, "{} + {}", x.index(), y.index()), + Expr::Sub(x, y) => write!(f, "{} - {}", x.index(), y.index()), + Expr::Mul(x, y) => write!(f, "{} * {}", x.index(), y.index()), + Expr::BVUgt(x, y) => write!(f, "bvugt({}, {})", x.index(), y.index()), + Expr::BVUge(x, y) => write!(f, "bvuge({}, {})", x.index(), y.index()), + Expr::BVUlt(x, y) => write!(f, "bvult({}, {})", x.index(), y.index()), + Expr::BVUle(x, y) => write!(f, "bvule({}, {})", x.index(), y.index()), + Expr::BVSgt(x, y) => write!(f, "bvsgt({}, {})", x.index(), y.index()), + Expr::BVSge(x, y) => write!(f, "bvsge({}, {})", x.index(), y.index()), + Expr::BVSlt(x, y) => write!(f, "bvslt({}, {})", x.index(), y.index()), + Expr::BVSle(x, y) => write!(f, "bvsle({}, {})", x.index(), y.index()), + Expr::BVSaddo(x, y) => write!(f, "bvsaddo({}, {})", x.index(), y.index()), + Expr::BVNot(x) => write!(f, "bvnot({})", x.index()), + Expr::BVNeg(x) => write!(f, "bvneg({})", x.index()), + Expr::Cls(x) => write!(f, "cls({})", x.index()), + Expr::Clz(x) => write!(f, "clz({})", x.index()), + Expr::Rev(x) => write!(f, "rev({})", x.index()), + Expr::Popcnt(x) => write!(f, "popcnt({})", x.index()), + Expr::BVAdd(x, y) => write!(f, "bvadd({}, {})", x.index(), y.index()), + Expr::BVSub(x, y) => write!(f, "bvsub({}, {})", x.index(), y.index()), + Expr::BVMul(x, y) => write!(f, "bvmul({}, {})", x.index(), y.index()), + Expr::BVSDiv(x, y) => write!(f, "bvsdiv({}, {})", x.index(), y.index()), + Expr::BVUDiv(x, y) => write!(f, "bvudiv({}, {})", x.index(), y.index()), + Expr::BVSRem(x, y) => write!(f, "bvsrem({}, {})", x.index(), y.index()), + Expr::BVURem(x, y) => write!(f, "bvurem({}, {})", x.index(), y.index()), + Expr::BVAnd(x, y) => write!(f, "bvand({}, {})", x.index(), y.index()), + Expr::BVOr(x, y) => write!(f, "bvor({}, {})", x.index(), y.index()), + Expr::BVXor(x, y) => write!(f, "bvxor({}, {})", x.index(), y.index()), + Expr::BVShl(x, y) => write!(f, "bvshl({}, {})", x.index(), y.index()), + Expr::BVLShr(x, y) => write!(f, "bvlshr({}, {})", x.index(), y.index()), + Expr::BVAShr(x, y) => write!(f, "bvashr({}, {})", x.index(), y.index()), + Expr::BVRotl(x, y) => write!(f, "bvrotl({}, {})", x.index(), y.index()), + Expr::BVRotr(x, y) => write!(f, "bvrotr({}, {})", x.index(), y.index()), + Expr::Conditional(c, t, e) => { + write!(f, "{} ? {} : {}", c.index(), t.index(), e.index()) + } + Expr::BVZeroExt(w, x) => write!(f, "bv_zero_ext({}, {})", w.index(), x.index()), + Expr::BVSignExt(w, x) => write!(f, "bv_zero_ext({}, {})", w.index(), x.index()), + Expr::BVConvTo(w, x) => write!(f, "bv_conv_to({}, {})", w.index(), x.index()), + Expr::BVExtract(h, l, x) => write!(f, "bv_extract({h}, {l}, {})", x.index()), + Expr::BVConcat(x, y) => write!(f, "bv_concat({}, {})", x.index(), y.index()), + Expr::Int2BV(w, x) => write!(f, "int2bv({}, {})", w.index(), x.index()), + Expr::ToFP(w, x) => write!(f, "to_fp({}, {})", w.index(), x.index()), + Expr::ToFPUnsigned(w, x) => write!(f, "to_fp_unsigned({}, {})", w.index(), x.index()), + Expr::ToFPFromFP(w, x) => write!(f, "to_fp_from_fp({}, {})", w.index(), x.index()), + Expr::FPToUBV(w, x) => write!(f, "fp.to_ubv({}, {})", w.index(), x.index()), + Expr::FPToSBV(w, x) => write!(f, "fp.to_sbv({}, {})", w.index(), x.index()), + Expr::BV2Nat(x) => write!(f, "bv2nat({})", x.index()), + Expr::WidthOf(x) => write!(f, "width_of({})", x.index()), + Expr::FPPositiveInfinity(x) => write!(f, "fp.+oo({})", x.index()), + Expr::FPNegativeInfinity(x) => write!(f, "fp.-oo({})", x.index()), + Expr::FPPositiveZero(x) => write!(f, "fp.+zero({})", x.index()), + Expr::FPNegativeZero(x) => write!(f, "fp.-zero({})", x.index()), + Expr::FPNaN(x) => write!(f, "fp.NaN({})", x.index()), + Expr::FPEq(x, y) => write!(f, "fp.eq({}, {})", x.index(), y.index()), + Expr::FPNe(x, y) => write!(f, "fp.ne({}, {})", x.index(), y.index()), + Expr::FPLt(x, y) => write!(f, "fp.lt({}, {})", x.index(), y.index()), + Expr::FPGt(x, y) => write!(f, "fp.gt({}, {})", x.index(), y.index()), + Expr::FPLe(x, y) => write!(f, "fp.le({}, {})", x.index(), y.index()), + Expr::FPGe(x, y) => write!(f, "fp.ge({}, {})", x.index(), y.index()), + Expr::FPAdd(x, y) => write!(f, "fp.add({}, {})", x.index(), y.index()), + Expr::FPSub(x, y) => write!(f, "fp.sub({}, {})", x.index(), y.index()), + Expr::FPMul(x, y) => write!(f, "fp.mul({}, {})", x.index(), y.index()), + Expr::FPDiv(x, y) => write!(f, "fp.div({}, {})", x.index(), y.index()), + Expr::FPMin(x, y) => write!(f, "fp.min({}, {})", x.index(), y.index()), + Expr::FPMax(x, y) => write!(f, "fp.max({}, {})", x.index(), y.index()), + Expr::FPNeg(x) => write!(f, "fp.neg({})", x.index()), + Expr::FPCeil(x) => write!(f, "fp.ceil({})", x.index()), + Expr::FPFloor(x) => write!(f, "fp.floor({})", x.index()), + Expr::FPSqrt(x) => write!(f, "fp.sqrt({})", x.index()), + Expr::FPTrunc(x) => write!(f, "fp.trunc({})", x.index()), + Expr::FPNearest(x) => write!(f, "fp.nearest({})", x.index()), + Expr::FPIsZero(x) => write!(f, "fp.isZero({})", x.index()), + Expr::FPIsInfinite(x) => write!(f, "fp.isInfinite({})", x.index()), + Expr::FPIsNaN(x) => write!(f, "fp.isNaN({})", x.index()), + Expr::FPIsNegative(x) => write!(f, "fp.isNegative({})", x.index()), + Expr::FPIsPositive(x) => write!(f, "fp.isPositive({})", x.index()), + } + } +} + +// QUESTION(mbm): can we merge `Model` and `Assignment` from type inference? +pub type Model = HashMap; + +// QUESTION(mbm): does the distinction between expressions and variables make sense? +#[derive(Debug)] +pub struct Variable { + pub ty: Type, + pub name: String, +} + +impl Variable { + fn component_name(prefix: &str, field: &str) -> String { + format!("{prefix}_{field}") + } +} + +#[derive(Debug, Clone)] +pub struct SymbolicOption { + some: ExprId, + inner: Box, +} + +#[derive(Debug, Clone)] +pub struct SymbolicField { + pub name: String, + pub value: Symbolic, +} + +impl SymbolicField { + fn eval(&self, model: &Model) -> Result { + Ok(FieldValue { + name: self.name.clone(), + value: self.value.eval(model)?, + }) + } +} + +#[derive(Debug, Clone)] +pub struct SymbolicEnum { + pub ty: TypeId, + pub discriminant: ExprId, + pub variants: Vec, +} + +impl SymbolicEnum { + fn try_variant_by_name(&self, name: &str) -> Result<&SymbolicVariant> { + self.variants + .iter() + .find(|v| v.name == name) + .ok_or(format_err!("no variant with name {name}")) + } + + fn validate(&self) -> Result<()> { + // Expect the variants to have distinct discriminants in the range [0, num_variants). + for (expect, variant) in self.variants.iter().enumerate() { + if variant.discriminant != expect { + bail!( + "variant '{name}' has unexpected discriminant", + name = variant.name + ); + } + } + Ok(()) + } +} + +#[derive(Debug, Clone)] +pub struct SymbolicVariant { + pub name: String, + pub id: VariantId, + pub discriminant: usize, + pub value: Symbolic, +} + +impl SymbolicVariant { + fn try_field_by_name(&self, name: &str) -> Result<&SymbolicField> { + self.fields()? + .iter() + .find(|f| f.name == name) + .ok_or(format_err!("no field with name {name}")) + } + + fn field_values(&self) -> Result> { + Ok(self.fields()?.iter().map(|f| f.value.clone()).collect()) + } + + fn fields(&self) -> Result<&Vec> { + self.value + .as_struct() + .ok_or(format_err!("variant value is not a struct")) + } +} + +impl std::fmt::Display for SymbolicVariant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{name} {value}", name = self.name, value = self.value) + } +} + +/// Inline spec expression macro. +/// +/// Note that at this stage the spec expressions are preserved as +/// [`spec::Expr`]. Generation of [`Expr`] objects from them is deferred until +/// macro expansion. +#[derive(Debug, Clone)] +pub struct Macro { + pub params: Vec, + pub body: spec::Expr, +} + +#[derive(Debug, Clone)] +pub enum Symbolic { + Scalar(ExprId), + Struct(Vec), + Enum(SymbolicEnum), + ExtEnum(SymbolicEnum, Vec), // ext enum with extra fields + Option(SymbolicOption), + Tuple(Vec), + Macro(Macro), +} + +impl Symbolic { + fn as_scalar(&self) -> Option { + match self { + Self::Scalar(x) => Some(*x), + _ => None, + } + } + + fn as_struct(&self) -> Option<&Vec> { + match self { + Self::Struct(fields) => Some(fields), + // Self::ExtEnum(_, fields) => Some (fields), + _ => None, + } + } + + fn as_enum(&self) -> Option<&SymbolicEnum> { + match self { + Self::Enum(e) => Some(e), + Self::ExtEnum(e, _) => Some(e), + _ => None, + } + } + + fn as_option(&self) -> Option<&SymbolicOption> { + match self { + Self::Option(opt) => Some(opt), + _ => None, + } + } + + fn as_tuple(&self) -> Option<&Vec> { + match self { + Self::Tuple(fields) => Some(fields), + _ => None, + } + } + + fn elements(&self) -> &[Symbolic] { + match self { + Self::Tuple(fields) => &fields[..], + v => std::slice::from_ref(v), + } + } + + fn eval(&self, model: &Model) -> Result { + match self { + Symbolic::Scalar(x) => Ok(Value::Const( + model + .get(x) + .ok_or(format_err!("undefined expression in model"))? + .clone(), + )), + Symbolic::Struct(fields) => Ok(Value::Struct( + fields + .iter() + .map(|f| f.eval(model)) + .collect::>()?, + )), + Symbolic::Enum(e) => { + // Determine the enum variant by looking up the discriminant. + let discriminant: usize = model + .get(&e.discriminant) + .ok_or(format_err!("undefined discriminant in model"))? + .as_int() + .ok_or(format_err!( + "model value for discriminant is not an integer" + ))? + .try_into() + .unwrap(); + let variant = e + .variants + .iter() + .find(|v| v.discriminant == discriminant) + .ok_or(format_err!("no variant with discriminant {discriminant}"))?; + Ok(Value::Enum(Box::new(VariantValue { + name: variant.name.clone(), + value: variant.value.eval(model)?, + }))) + } + Symbolic::ExtEnum(e, extra) => { + // 1. Same as Enum: resolve discriminant and variant + let discriminant: usize = model + .get(&e.discriminant) + .ok_or(format_err!("undefined discriminant in model"))? + .as_int() + .ok_or(format_err!( + "model value for discriminant is not an integer" + ))? + .try_into() + .unwrap(); + let variant = e + .variants + .iter() + .find(|v| v.discriminant == discriminant) + .ok_or(format_err!("no variant with discriminant {discriminant}"))?; + + let base_value = Value::Enum(Box::new(VariantValue { + name: variant.name.clone(), + value: variant.value.eval(model)?, + })); + + // 2. Evaluate extra fields like a struct + let extra_values = extra + .iter() + .map(|f| f.eval(model)) + .collect::>>()?; + + // 3. Wrap into a new Value variant (you’ll need a `Value::ExtEnum` to store this) + Ok(Value::ExtEnum { + base: Box::new(base_value), + extra: extra_values, + }) + } + + Symbolic::Option(opt) => match model.get(&opt.some) { + Some(Const::Bool(true)) => { + Ok(Value::Option(Some(Box::new(opt.inner.eval(model)?)))) + } + Some(Const::Bool(false)) => Ok(Value::Option(None)), + Some(_) => bail!("model value for option some is not boolean"), + None => bail!("undefined expression in model"), + }, + Symbolic::Tuple(elements) => Ok(Value::Tuple( + elements + .iter() + .map(|s| s.eval(model)) + .collect::>()?, + )), + Symbolic::Macro(_) => bail!("cannot evaluate macro"), + } + } + + // Build a new value by applying the given map function to all constituent + // scalars in this symbolic value. + fn scalar_map(&self, f: &mut F) -> Symbolic + where + F: FnMut(ExprId) -> ExprId, + { + match self { + Symbolic::Scalar(x) => Symbolic::Scalar(f(*x)), + Symbolic::Struct(fields) => Symbolic::Struct( + fields + .iter() + .map(|field| SymbolicField { + name: field.name.clone(), + value: field.value.scalar_map(f), + }) + .collect(), + ), + Symbolic::Enum(e) => Symbolic::Enum(SymbolicEnum { + ty: e.ty, + discriminant: f(e.discriminant), + variants: e + .variants + .iter() + .map(|v| SymbolicVariant { + id: v.id, + name: v.name.clone(), + discriminant: v.discriminant, + value: v.value.scalar_map(f), + }) + .collect(), + }), + v => todo!("scalar map: {v:?}"), + } + } + + fn merge(a: &Symbolic, b: &Symbolic, merge: &mut F) -> Result + where + F: FnMut(ExprId, ExprId) -> ExprId, + { + if std::mem::discriminant(a) != std::mem::discriminant(b) { + bail!("conditional arms have incompatible types"); + } + match (a, b) { + (Symbolic::Scalar(a), Symbolic::Scalar(b)) => Ok(merge(*a, *b).into()), + (Symbolic::Struct(a_fields), Symbolic::Struct(b_fields)) => { + assert_eq!(a_fields.len(), b_fields.len()); + Ok(Symbolic::Struct( + zip(a_fields, b_fields) + .map(|(a, b)| { + assert_eq!(a.name, b.name); + Ok(SymbolicField { + name: a.name.clone(), + value: Symbolic::merge(&a.value, &b.value, merge)?, + }) + }) + .collect::>()?, + )) + } + (Symbolic::Enum(a), Symbolic::Enum(b)) => { + assert_eq!(a.ty, b.ty); + let ty = a.ty; + let discriminant = merge(a.discriminant, b.discriminant); + assert_eq!(a.variants.len(), b.variants.len()); + let variants = zip(&a.variants, &b.variants) + .map(|(a, b)| { + assert_eq!(a.name, b.name); + assert_eq!(a.id, b.id); + assert_eq!(a.discriminant, b.discriminant); + Ok(SymbolicVariant { + name: a.name.clone(), + id: a.id, + discriminant: a.discriminant, + value: Symbolic::merge(&a.value, &b.value, merge)?, + }) + }) + .collect::>()?; + Ok(Symbolic::Enum(SymbolicEnum { + ty, + discriminant, + variants, + })) + } + case => todo!("symbolic merge types: {case:?}"), + } + } +} + +impl From for Symbolic { + fn from(x: ExprId) -> Self { + Symbolic::Scalar(x) + } +} + +impl std::fmt::Display for Symbolic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Symbolic::Scalar(x) => write!(f, "{}", x.index()), + Symbolic::Struct(fields) => write!( + f, + "{{{fields}}}", + fields = fields + .iter() + .map(|f| format!("{}: {}", f.name, f.value)) + .collect::>() + .join(", ") + ), + Symbolic::Enum(e) => write!( + f, + "{{{discriminant}, {variants}}}", + discriminant = e.discriminant.index(), + variants = e + .variants + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ), + Symbolic::ExtEnum(base_enum, extra_fields) => { + write!( + f, + "extenum({discriminant}, {variants}, extras: {{{extras}}})", + discriminant = base_enum.discriminant.index(), + variants = base_enum + .variants + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", "), + extras = extra_fields + .iter() + .map(|f| format!("{}: {}", f.name, f.value)) + .collect::>() + .join(", ") + ) + } + Symbolic::Option(SymbolicOption { some, inner }) => { + write!(f, "Option{{some: {}, inner: {inner}}}", some.index()) + } + Symbolic::Tuple(vs) => write!( + f, + "({vs})", + vs = vs + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ), + Symbolic::Macro(_) => write!(f, "macro"), + } + } +} + +#[derive(Clone, Debug)] +pub enum Value { + Const(Const), + Struct(Vec), + Enum(Box), + ExtEnum { + base: Box, // base enum value + extra: Vec, // evaluated extra fields + }, + Option(Option>), + Tuple(Vec), +} + +#[derive(Debug, Clone)] +pub struct FieldValue { + name: String, + value: Value, +} + +#[derive(Debug, Clone)] +pub struct VariantValue { + name: String, + value: Value, +} + +impl std::fmt::Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Value::Const(c) => c.fmt(f), + Value::Struct(fields) => write!( + f, + "{{{fields}}}", + fields = fields + .iter() + .map(|f| format!("{}: {}", f.name, f.value)) + .collect::>() + .join(", ") + ), + Value::Enum(v) => write!(f, "{name} {value}", name = v.name, value = v.value), + Value::ExtEnum { base, extra } => { + write!( + f, + "extenum({base}, extras: {{{extras}}})", + base = base, + extras = extra + .iter() + .map(|f| format!("{}: {}", f.name, f.value)) + .collect::>() + .join(", ") + ) + } + Value::Option(Some(v)) => write!(f, "Some({v})"), + Value::Option(None) => write!(f, "None"), + Value::Tuple(elements) => write!( + f, + "({})", + elements + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ), + } + } +} + +// QUESTION(mbm): is `Call` the right name? consider `Term`, `TermInstance`, ...? +#[derive(Debug)] +pub struct Call { + pub term: TermId, + pub args: Vec, + pub ret: Symbolic, + pub signatures: Vec, +} + +// Type qualifier, for example derived from an `(as ...)` expression. +#[derive(Debug)] +pub struct Qualifier { + pub value: Symbolic, + pub ty: Compound, +} + +/// Verification conditions for an expansion. +#[derive(Debug, Default)] +pub struct Conditions { + pub exprs: Vec, + pub assumptions: Vec, + pub assertions: Vec, + pub variables: Vec, + pub state: Variables, + pub calls: Vec, + pub qualifiers: Vec, + pub pos: HashMap, +} + +impl Conditions { + pub fn from_expansion(expansion: &Expansion, prog: &Program) -> Result { + let builder = ConditionsBuilder::new(expansion, prog); + builder.build() + } + + pub fn pretty_print(&self, prog: &Program) { + println!("conditions {{"); + + // Expressions + println!("\texprs = ["); + for (i, expr) in self.exprs.iter().enumerate() { + println!("\t\t{i}:\t{expr}"); + } + println!("\t]"); + + // Assumptions + println!("\tassumptions = ["); + for expr_id in &self.assumptions { + println!("\t\t{}", expr_id.index()); + } + println!("\t]"); + + // Assertions + println!("\tassertions = ["); + for expr_id in &self.assertions { + println!("\t\t{}", expr_id.index()); + } + println!("\t]"); + + // Variables + println!("\tvariables = ["); + for (i, v) in self.variables.iter().enumerate() { + println!("\t\t{i}:\t{ty}\t{name}", ty = v.ty, name = v.name); + } + println!("\t]"); + + // Calls + // TODO(mbm): prettier pretty printing code + println!("\tcalls = ["); + for call in &self.calls { + println!("\t\tcall {{"); + println!("\t\t\tterm = {}", prog.term_name(call.term)); + if !call.args.is_empty() { + println!("\t\t\targs = ["); + for arg in &call.args { + println!("\t\t\t\t{}", arg); + } + println!("\t\t\t]"); + } + println!("\t\t\tret = {}", call.ret); + if !call.signatures.is_empty() { + println!("\t\t\tsignatures = ["); + for sig in &call.signatures { + println!("\t\t\t\tsignature {{"); + if !sig.args.is_empty() { + println!("\t\t\t\t\targs = ["); + for arg in &sig.args { + println!("\t\t\t\t\t\t{arg}"); + } + println!("\t\t\t\t\t]"); + } + println!("\t\t\t\t\tret = {}", sig.ret); + println!("\t\t\t\t}}"); + } + println!("\t\t\t]"); + } + println!("\t\t}}"); + } + println!("\t]"); + + println!("}}"); + } + + pub fn validate(&self) -> Result<()> { + // Ensure there are no dangling expressions. + let reachable = self.reachable(); + for x in (0..self.exprs.len()).map(ExprId) { + if self.exprs[x.index()].is_variable() { + continue; + } + if !reachable.contains(&x) { + bail!("expression {x} is unreachable", x = x.index()); + } + } + + Ok(()) + } + + fn reachable(&self) -> HashSet { + let mut reach = HashSet::new(); + + let mut stack: Vec = Vec::new(); + stack.extend(&self.assumptions); + stack.extend(&self.assertions); + + while let Some(x) = stack.pop() { + if reach.contains(&x) { + continue; + } + + reach.insert(x); + let expr = &self.exprs[x.index()]; + stack.extend(expr.sources()); + } + + reach + } + + pub fn print_model(&self, model: &Model, prog: &Program) -> Result<()> { + // State + for (name, value) in &self.state.0 { + println!("state: {name} = {}", value.eval(model)?); + } + + // Calls + for call in &self.calls { + // Skip unit enum variant terms, which may occur frequently and are + // rarely informative. + let term = prog.term(call.term); + if term.is_enum_variant() && call.args.is_empty() { + continue; + } + + println!( + "{term_name}({args}) -> {ret}", + term_name = prog.term_name(call.term), + args = call + .args + .iter() + .map(|a| Ok(a.eval(model)?.to_string())) + .collect::>>()? + .join(", "), + ret = call.ret.eval(model)? + ); + } + + Ok(()) + } + + pub fn error_at_expr(&self, prog: &Program, x: ExprId, msg: impl Into) -> Error { + if let Some(pos) = self.pos.get(&x) { + prog.error_at_pos(*pos, msg).into() + } else { + Error::msg(msg.into()) + } + } +} + +enum TermKind { + Constructor, + Extractor, +} + +#[derive(Copy, Clone)] +enum Invocation { + Caller, + Callee, +} + +#[derive(Copy, Clone)] +enum Domain { + Total, + Partial(ExprId), +} + +impl Domain { + fn from_return_value(value: &Symbolic) -> (Self, Symbolic) { + match value { + Symbolic::Option(opt) => (Self::Partial(opt.some), (*opt.inner).clone()), + v => (Self::Total, v.clone()), + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct Variables(HashMap); + +impl Variables { + fn new() -> Self { + Self(HashMap::new()) + } + + fn get(&self, name: &String) -> Option<&Symbolic> { + self.0.get(name) + } + + fn expect(&self, name: &String) -> Result<&Symbolic> { + self.get(name) + .ok_or(format_err!("undefined variable {name}")) + } + + fn set(&mut self, name: String, value: Symbolic) -> Result<()> { + match self.0.entry(name) { + Entry::Occupied(e) => { + bail!("redefinition of variable {name}", name = e.key()); + } + Entry::Vacant(e) => { + e.insert(value); + Ok(()) + } + } + } +} + +struct ConditionsBuilder<'a> { + expansion: &'a Expansion, + prog: &'a Program, + + state_modification_conds: HashMap>, + binding_value: HashMap, + expr_map: HashMap, + conditions: Conditions, + position_stack: Vec, +} + +impl<'a> ConditionsBuilder<'a> { + fn new(expansion: &'a Expansion, prog: &'a Program) -> Self { + Self { + expansion, + prog, + state_modification_conds: HashMap::new(), + binding_value: HashMap::new(), + expr_map: HashMap::new(), + conditions: Conditions::default(), + position_stack: Vec::new(), + } + } + + fn build(mut self) -> Result { + // State initialization. + for state in &self.prog.specenv.state { + self.init_state(state)?; + } + + // Bindings. + for (i, binding) in self.expansion.bindings.iter().enumerate() { + if let Some(binding) = binding { + self.add_binding(i.try_into().unwrap(), binding)?; + } + } + + // Callee contract for the term under expansion. + self.constructor( + self.expansion.result, + self.expansion.term, + &self.expansion.parameters, + Invocation::Callee, + )?; + + // Constraints. + for constrain in &self.expansion.constraints { + let holds = self.constrain(constrain)?; + self.conditions.assumptions.push(holds); + } + + // Equals. + for (a, b) in self.expansion.equalities() { + let eq = self.bindings_equal(a, b)?; + self.conditions.assumptions.push(eq); + } + + // State defaults. + for state in &self.prog.specenv.state { + self.state_default(state)?; + } + + // Validate + self.conditions.validate()?; + + Ok(self.conditions) + } + + fn init_state(&mut self, state: &State) -> Result<()> { + let name = &state.name.0; + let value = self.alloc_value(&state.ty, name.clone())?; + self.conditions.state.set(name.clone(), value)?; + Ok(()) + } + + fn state_default(&mut self, state: &State) -> anyhow::Result<()> { + // Evaluate the default spec expression in a scope that only defines + // the state variable itself. + let mut vars = Variables::new(); + let name = &state.name.0; + vars.set(name.clone(), self.conditions.state.expect(name)?.clone())?; + let mut default = self.spec_expr(&state.default, &vars)?; + + // Other specs may have declared conditions under which they modify the + // state. The default only applies when none of them are true. + if let Some(conds) = self.state_modification_conds.get(name) { + let modified = self.any(conds.clone()); + let not_modified = self.dedup_expr(Expr::Not(modified)); + default = self.scalar(Expr::Imp(not_modified, self.as_scalar(default)?)); + } + + // The expression should define an assumption about the state variable, + // so should be a scalar boolean. + self.conditions.assumptions.push(self.as_scalar(default)?); + + Ok(()) + } + + fn add_binding(&mut self, id: BindingId, binding: &Binding) -> Result<()> { + // Exit if already added. + if self.binding_value.contains_key(&id) { + return Ok(()); + } + + // Allocate a value. + let binding_type = self.binding_type(binding); + let name = format!("b{}", id.index()); + let value = self.alloc_binding(&binding_type, name)?; + self.binding_value.insert(id, value); + + // Ensure dependencies have been added. + for source in binding.sources() { + let source_binding = self + .expansion + .binding(*source) + .expect("source binding should be defined"); + self.add_binding(*source, source_binding)?; + } + + // Generate conditions depending on binding type. + match binding { + Binding::ConstInt { val, ty } => self.const_int(id, *val, *ty), + + Binding::ConstPrim { val } => self.const_prim(id, *val), + + // Argument binding has no associated constraints. + Binding::Argument { .. } => Ok(()), + + Binding::Extractor { term, parameter } => self.extractor(id, *term, *parameter), + + Binding::Constructor { + term, parameters, .. + } => self.constructor(id, *term, parameters, Invocation::Caller), + + Binding::Iterator { .. } => unimplemented!("iterator bindings"), + + Binding::MakeVariant { + ty, + variant, + fields, + } => self.make_variant(id, *ty, *variant, fields), + + Binding::MatchVariant { + source, + variant, + field, + } => self.match_variant(id, *source, *variant, *field), + + Binding::MakeSome { inner } => self.make_some(id, *inner), + + Binding::MatchSome { source } => self.match_some(id, *source), + + Binding::MatchTuple { source, field } => self.match_tuple(id, *source, *field), + } + } + + fn const_int(&mut self, id: BindingId, val: i128, ty: TypeId) -> Result<()> { + let eq = self.equals_const_int(id, val, ty)?; + self.conditions.assumptions.push(eq); + Ok(()) + } + + fn equals_const_int(&mut self, id: BindingId, val: i128, ty: TypeId) -> Result { + // Determine modeled type. + let ty_name = self.prog.type_name(ty); + let ty = self + .prog + .specenv + .type_model + .get(&ty) + .ok_or(self.error(format!("no model for type {ty_name}")))? + .as_primitive() + .ok_or(self.error("constant must have basic type"))?; + + // Construct value of the determined type. + let value = self.spec_typed_value(val, ty)?.into(); + + // Destination binding equals constant value. + let eq = self.values_equal(self.binding_value[&id].clone(), value)?; + Ok(eq) + } + + fn const_prim(&mut self, id: BindingId, val: Sym) -> Result<()> { + let eq = self.equals_const_prim(id, val)?; + self.conditions.assumptions.push(eq); + Ok(()) + } + + fn equals_const_prim(&mut self, id: BindingId, val: Sym) -> Result { + // Lookup value. + let spec_value = self + .prog + .specenv + .const_value + .get(&val) + .ok_or(self.error(format!( + "value of constant {const_name} is unspecified", + const_name = self.prog.tyenv.syms[val.index()] + )))?; + let value = self.spec_expr_no_vars(spec_value)?; + + // Destination binding equals constant value. + let eq = self.values_equal(self.binding_value[&id].clone(), value)?; + Ok(eq) + } + + fn extractor(&mut self, id: BindingId, term: TermId, parameter: BindingId) -> Result<()> { + // Arguments are the actually the return values of an + // extractor, possibly wrapped in an Option<..> type. + let (domain, ret) = Domain::from_return_value(&self.binding_value[&id]); + let args = ret.elements(); + + // Result maps to the parameter of an extractor. + let result = self.binding_value[¶meter].clone(); + + // Call extractor. + self.call( + term, + TermKind::Extractor, + args, + result, + Invocation::Caller, + domain, + ) + .with_context(|| { + format!( + "expanding extractor '{name}'", + name = self.prog.term_name(term) + ) + }) + } + + fn constructor( + &mut self, + id: BindingId, + term: TermId, + parameters: &[BindingId], + invocation: Invocation, + ) -> Result<()> { + // Arguments. + let mut args = Vec::new(); + for parameter_binding_id in parameters { + let x = self + .binding_value + .get(parameter_binding_id) + .expect("parameter binding should be defined") + .clone(); + args.push(x); + } + + // Return value. + let (domain, result) = Domain::from_return_value(&self.binding_value[&id]); + + // Call constructor. + self.call( + term, + TermKind::Constructor, + &args, + result, + invocation, + domain, + ) + .with_context(|| { + format!( + "expanding constructor '{name}'", + name = self.prog.term_name(term) + ) + }) + } + + fn call( + &mut self, + term: TermId, + kind: TermKind, + args: &[Symbolic], + ret: Symbolic, + invocation: Invocation, + domain: Domain, + ) -> Result<()> { + // Lookup spec. + let term_name = self.prog.term_name(term); + let term_spec = self + .prog + .specenv + .term_spec + .get(&term) + .ok_or(self.error(format!("no spec for term {term_name}",)))?; + + // We are provided the arguments and return value as they appear + // syntactically in the term declaration and specification. However, + // whether these are the actual inputs and outputs of the corresponding + // function depends on the term kind. + if term_spec.args.len() != args.len() { + bail!("incorrect number of arguments for term {term_name}"); + } + let arguments: Vec<_> = zip(&term_spec.args, args).collect(); + let result = (&term_spec.ret, &ret); + let (inputs, outputs) = match kind { + TermKind::Constructor => (arguments.as_slice(), std::slice::from_ref(&result)), + TermKind::Extractor => (std::slice::from_ref(&result), arguments.as_slice()), + }; + + // Scope for spec expression evaluation. State variables are always available. + let mut vars = self.conditions.state.clone(); + + // State modification conditions. + for modifies in &term_spec.modifies { + let cond = if let Some(cond_name) = &modifies.cond { + // Allocate new boolean for the modification condition. + let cond = self.alloc_variable( + Type::Bool, + format!("{name}_modification_cond", name = modifies.state.0), + ); + // Bring into spec scope. + vars.set(cond_name.0.clone(), cond.into())?; + cond + } else { + // TODO(mbm): warn when state is both conditionally and unconditionaly modified. + self.boolean(true) + }; + + // Record condition to determine when the default spec applies. + self.state_modification_conds + .entry(modifies.state.0.clone()) + .or_default() + .push(cond); + } + + // Inputs are available to the requires and matches clauses. + for (name, input) in inputs { + vars.set(name.0.clone(), (*input).clone())?; + } + + // Requires. + let mut requires: Vec = Vec::new(); + for require in &term_spec.requires { + let require = self.spec_expr(require, &vars)?; + requires.push(self.as_scalar(require)?); + } + + // Matches. + let mut matches: Vec = Vec::new(); + for m in &term_spec.matches { + let m = self.spec_expr(m, &vars)?; + matches.push(self.as_scalar(m)?); + } + + // Outputs: only in scope for provides. + for (name, output) in outputs { + vars.set(name.0.clone(), (*output).clone())?; + } + + // Provides. + let mut provides: Vec = Vec::new(); + for provide in &term_spec.provides { + let provide = self.spec_expr(provide, &vars)?; + provides.push(self.as_scalar(provide)?); + } + + // Partial function. + // REVIEW(mbm): pin down semantics for partial function specifications. + if let Domain::Partial(p) = domain { + // Matches describe when the function applies. + let all_matches = self.all(matches); + let eq = self.exprs_equal(p, all_matches); + self.conditions.assumptions.push(eq); + + // Provides are conditioned on the match. + let all_provides = self.all(provides); + let provide = self.dedup_expr(Expr::Imp(all_matches, all_provides)); + provides = vec![provide]; + } else if !matches.is_empty() { + bail!("spec matches on non-partial function"); + } + + // Assert/assume depending on caller or callee. + match invocation { + Invocation::Caller => { + self.conditions.assertions.extend(requires); + self.conditions.assumptions.extend(provides); + } + Invocation::Callee => { + self.conditions.assumptions.extend(requires); + self.conditions.assertions.extend(provides); + } + } + + // Record callsite. + self.record_term_instantiation(term, args.to_vec(), ret)?; + + Ok(()) + } + + fn record_term_instantiation( + &mut self, + term: TermId, + args: Vec, + ret: Symbolic, + ) -> Result<()> { + let signatures = self + .prog + .specenv + .resolve_term_instantiations(&term, &self.prog.tyenv)?; + self.conditions.calls.push(Call { + term, + args, + ret, + signatures, + }); + Ok(()) + } + + fn make_variant( + &mut self, + id: BindingId, + ty: TypeId, + variant: VariantId, + fields: &[BindingId], + ) -> Result<()> { + // Lookup term corresponding to variant. + let variant_term_id = self.prog.get_variant_term(ty, variant); + + // Invoke as a constructor. + self.constructor(id, variant_term_id, fields, Invocation::Caller)?; + + Ok(()) + } + + fn match_variant( + &mut self, + id: BindingId, + source: BindingId, + variant: VariantId, + field: TupleIndex, + ) -> Result<()> { + // Source binding should be an enum. + let e = self.binding_value[&source] + .as_enum() + .ok_or(self.error("target of variant constraint should be an enum"))? + .clone(); + + // Lookup enum type via corresponding constriant, + let tys: Vec<_> = self + .expansion + .constraints + .iter() + .flat_map(|c| match c { + Constrain::Match(id, Constraint::Variant { ty, variant: v, .. }) + if *id == source && *v == variant => + { + Some(ty) + } + _ => None, + }) + .collect(); + if tys.len() != 1 { + bail!("expected exactly one variant constraint for match variant binding"); + } + let ty = tys[0]; + + // Lookup variant and field. + let variant_type = self.prog.tyenv.get_variant(*ty, variant); + let variant_name = self.prog.tyenv.syms[variant_type.name.index()].as_str(); + + let field_sym = variant_type.fields[field.index()].name; + let field_name = &self.prog.tyenv.syms[field_sym.index()]; + + // Destination binding. + let v = self.binding_value[&id].clone(); + + // Assumption: if the variant matches then the destination binding + // equals the projected field. + let variant = e.try_variant_by_name(variant_name)?; + let field = variant.try_field_by_name(field_name)?; + + let discriminator = self.discriminator(&e, variant); + let eq = self.values_equal(v, field.value.clone())?; + let constraint = self.dedup_expr(Expr::Imp(discriminator, eq)); + self.conditions.assumptions.push(constraint); + + Ok(()) + } + + fn make_some(&mut self, id: BindingId, inner: BindingId) -> Result<()> { + // Destination binding should be an option. + let opt = self.binding_value[&id] + .as_option() + .expect("destination of make_some binding should be an option") + .clone(); + + // Inner binding. + let inner = self.binding_value[&inner].clone(); + + // Assumption: option is Some. + self.conditions.assumptions.push(opt.some); + + // Assumption: option value is equal to this binding. + let eq = self.values_equal(inner, (*opt.inner).clone())?; + self.conditions.assumptions.push(eq); + + Ok(()) + } + + fn match_some(&mut self, id: BindingId, source: BindingId) -> Result<()> { + // Source should be an option. + let opt = self.binding_value[&source] + .as_option() + .expect("source of match_some binding should be an option") + .clone(); + + // Destination binding. + let v = self.binding_value[&id].clone(); + + // Assumption: if the option is some, then the inner value + // equals this binding. + let eq = self.values_equal(v, (*opt.inner).clone())?; + let constraint = self.dedup_expr(Expr::Imp(opt.some, eq)); + self.conditions.assumptions.push(constraint); + + Ok(()) + } + + fn match_tuple(&mut self, id: BindingId, source: BindingId, field: TupleIndex) -> Result<()> { + // Source should be a tuple. Access its fields. + let fields = self.binding_value[&source] + .as_tuple() + .expect("source of match_tuple binding should be a tuple") + .clone(); + + // Destination binding. + let v = self.binding_value[&id].clone(); + + // Assumption: indexed field should equal this binding. + let eq = self.values_equal(v, fields[field.index()].clone())?; + self.conditions.assumptions.push(eq); + + Ok(()) + } + + fn constrain(&mut self, constrain: &Constrain) -> Result { + match constrain { + Constrain::Match(binding_id, constraint) => self.constraint(*binding_id, constraint), + Constrain::NotAll(constrains) => { + let cs = constrains + .iter() + .map(|c| self.constrain(c)) + .collect::>()?; + let all = self.all(cs); + let not_all = self.dedup_expr(Expr::Not(all)); + Ok(not_all) + } + } + } + + fn constraint(&mut self, binding_id: BindingId, constraint: &Constraint) -> Result { + match constraint { + Constraint::Some => self.constraint_some(binding_id), + Constraint::ConstPrim { val } => self.equals_const_prim(binding_id, *val), + Constraint::ConstInt { val, ty } => self.equals_const_int(binding_id, *val, *ty), + Constraint::Variant { + ty, + variant, + fields: _, + } => self.constraint_variant(binding_id, *ty, *variant), + } + } + + fn constraint_some(&mut self, binding_id: BindingId) -> Result { + // Constrained binding should be an option. + let opt = self.binding_value[&binding_id] + .as_option() + .expect("target of some constraint should be an option") + .clone(); + + // Constraint: option is Some. + Ok(opt.some) + } + + fn constraint_variant( + &mut self, + binding_id: BindingId, + ty: TypeId, + variant: VariantId, + ) -> Result { + // Constrained binding should be an enum. + let e = self.binding_value[&binding_id] + .as_enum() + .ok_or(self.error("target of variant constraint should be an enum"))? + .clone(); + + // TODO(mbm): check the enum type is correct? + + // Lookup variant. + let variant_type = self.prog.tyenv.get_variant(ty, variant); + let variant_name = self.prog.tyenv.syms[variant_type.name.index()].as_str(); + + // Assumption: discriminant equals variant. + let variant = e.try_variant_by_name(variant_name)?; + let discriminator = self.discriminator(&e, variant); + Ok(discriminator) + } + + fn spec_expr(&mut self, expr: &spec::Expr, vars: &Variables) -> Result { + self.position_stack.push(expr.pos); + let result = self.spec_expr_kind(&expr.x, vars); + self.position_stack.pop(); + result + } + + fn spec_expr_kind(&mut self, expr: &spec::ExprKind, vars: &Variables) -> Result { + macro_rules! unary_expr { + ($expr:path, $x:ident) => {{ + let $x = self.spec_expr($x, vars)?; + Ok(self.scalar($expr(self.as_scalar($x)?))) + }}; + } + + macro_rules! binary_expr { + ($expr:path, $x:ident, $y:ident) => {{ + let $x = self.spec_expr($x, vars)?; + let $y = self.spec_expr($y, vars)?; + Ok(self.scalar($expr(self.as_scalar($x)?, self.as_scalar($y)?))) + }}; + } + + macro_rules! variadic_expr { + ($expr:path, $xs:ident) => {{ + let exprs: Vec = $xs + .iter() + .map(|x| { + let x = self.spec_expr(x, vars)?; + self.as_scalar(x) + }) + .collect::>>()?; + Ok(Symbolic::Scalar( + exprs + .into_iter() + .rev() + .reduce(|acc, e| self.dedup_expr($expr(e, acc))) + .ok_or(self.error("empty variadic expression"))?, + )) + }}; + } + + match expr { + spec::ExprKind::Var(v) => { + let v = vars.expect(&v.0)?; + Ok(v.clone()) + } + + spec::ExprKind::Const(c) => Ok(self.constant(c.clone()).into()), + + spec::ExprKind::Constructor(constructor) => self.construct(constructor, vars), + + spec::ExprKind::Field(name, x) => { + let x = self.spec_expr(x, vars)?; + self.spec_field(name, x) + } + + spec::ExprKind::Discriminator(variant, x) => { + let x = self.spec_expr(x, vars)?; + self.spec_discriminator(variant, x) + } + + spec::ExprKind::Not(x) => unary_expr!(Expr::Not, x), + spec::ExprKind::And(xs) => variadic_expr!(Expr::And, xs), + spec::ExprKind::Or(xs) => variadic_expr!(Expr::Or, xs), + spec::ExprKind::Imp(x, y) => binary_expr!(Expr::Imp, x, y), + + spec::ExprKind::Eq(x, y) => { + let x = self.spec_expr(x, vars)?; + let y = self.spec_expr(y, vars)?; + Ok(self.values_equal(x, y)?.into()) + } + + spec::ExprKind::Lt(x, y) => binary_expr!(Expr::Lt, x, y), + spec::ExprKind::Lte(x, y) => binary_expr!(Expr::Lte, x, y), + spec::ExprKind::Gt(x, y) => binary_expr!(Expr::Lt, y, x), + spec::ExprKind::Gte(x, y) => binary_expr!(Expr::Lte, y, x), + spec::ExprKind::BVUlt(x, y) => binary_expr!(Expr::BVUlt, x, y), + spec::ExprKind::BVUle(x, y) => binary_expr!(Expr::BVUle, x, y), + spec::ExprKind::BVSge(x, y) => binary_expr!(Expr::BVSge, x, y), + spec::ExprKind::BVSlt(x, y) => binary_expr!(Expr::BVSlt, x, y), + spec::ExprKind::BVSle(x, y) => binary_expr!(Expr::BVSle, x, y), + spec::ExprKind::BVSgt(x, y) => binary_expr!(Expr::BVSgt, x, y), + spec::ExprKind::BVUgt(x, y) => binary_expr!(Expr::BVUgt, x, y), + spec::ExprKind::BVUge(x, y) => binary_expr!(Expr::BVUge, x, y), + spec::ExprKind::BVSaddo(x, y) => binary_expr!(Expr::BVSaddo, x, y), + spec::ExprKind::BVNot(x) => unary_expr!(Expr::BVNot, x), + spec::ExprKind::BVNeg(x) => unary_expr!(Expr::BVNeg, x), + spec::ExprKind::Cls(x) => unary_expr!(Expr::Cls, x), + spec::ExprKind::Clz(x) => unary_expr!(Expr::Clz, x), + spec::ExprKind::Rev(x) => unary_expr!(Expr::Rev, x), + spec::ExprKind::Popcnt(x) => unary_expr!(Expr::Popcnt, x), + spec::ExprKind::Add(x, y) => binary_expr!(Expr::Add, x, y), + spec::ExprKind::Sub(x, y) => binary_expr!(Expr::Sub, x, y), + spec::ExprKind::Mul(x, y) => binary_expr!(Expr::Mul, x, y), + spec::ExprKind::BVAdd(x, y) => binary_expr!(Expr::BVAdd, x, y), + spec::ExprKind::BVSub(x, y) => binary_expr!(Expr::BVSub, x, y), + spec::ExprKind::BVMul(x, y) => binary_expr!(Expr::BVMul, x, y), + spec::ExprKind::BVSDiv(x, y) => binary_expr!(Expr::BVSDiv, x, y), + spec::ExprKind::BVAnd(x, y) => binary_expr!(Expr::BVAnd, x, y), + spec::ExprKind::BVOr(x, y) => binary_expr!(Expr::BVOr, x, y), + spec::ExprKind::BVXor(x, y) => binary_expr!(Expr::BVXor, x, y), + spec::ExprKind::BVShl(x, y) => binary_expr!(Expr::BVShl, x, y), + spec::ExprKind::BVLShr(x, y) => binary_expr!(Expr::BVLShr, x, y), + spec::ExprKind::BVAShr(x, y) => binary_expr!(Expr::BVAShr, x, y), + spec::ExprKind::BVUDiv(x, y) => binary_expr!(Expr::BVUDiv, x, y), + spec::ExprKind::BVURem(x, y) => binary_expr!(Expr::BVURem, x, y), + spec::ExprKind::BVSRem(x, y) => binary_expr!(Expr::BVSRem, x, y), + spec::ExprKind::BVRotl(x, y) => binary_expr!(Expr::BVRotl, x, y), + spec::ExprKind::BVRotr(x, y) => binary_expr!(Expr::BVRotr, x, y), + + spec::ExprKind::Conditional(c, t, e) => { + let c = self.spec_expr(c, vars)?; + let t = self.spec_expr(t, vars)?; + let e = self.spec_expr(e, vars)?; + self.conditional(self.as_scalar(c)?, t, e) + } + + spec::ExprKind::Switch(on, arms) => self.spec_switch(on, arms, vars), + + spec::ExprKind::Match(on, arms) => self.spec_match(on, arms, vars), + + spec::ExprKind::Let(defs, body) => self.spec_let(defs, body, vars), + + spec::ExprKind::With(decls, body) => self.spec_with(decls, body, vars), + + spec::ExprKind::Expand(ident, args) => self.spec_expand(ident, args, vars), + + spec::ExprKind::BVZeroExt(w, x) => binary_expr!(Expr::BVZeroExt, w, x), + spec::ExprKind::BVSignExt(w, x) => binary_expr!(Expr::BVSignExt, w, x), + spec::ExprKind::BVConvTo(w, x) => binary_expr!(Expr::BVConvTo, w, x), + + spec::ExprKind::BVExtract(h, l, x) => { + let x = self.spec_expr(x, vars)?; + Ok(self.scalar(Expr::BVExtract(*h, *l, self.as_scalar(x)?))) + } + + spec::ExprKind::BVConcat(xs) => variadic_expr!(Expr::BVConcat, xs), + spec::ExprKind::BVReplicate(x, n) => { + let x = self.spec_expr(x, vars)?; + let r = self.replicate(self.as_scalar(x)?, *n)?; + Ok(Symbolic::Scalar(r)) + } + spec::ExprKind::Int2BV(w, x) => binary_expr!(Expr::Int2BV, w, x), + spec::ExprKind::BV2Nat(x) => unary_expr!(Expr::BV2Nat, x), + spec::ExprKind::ToFP(w, x) => binary_expr!(Expr::ToFP, w, x), + spec::ExprKind::ToFPUnsigned(w, x) => binary_expr!(Expr::ToFPUnsigned, w, x), + spec::ExprKind::ToFPFromFP(w, x) => binary_expr!(Expr::ToFPFromFP, w, x), + spec::ExprKind::FPToUBV(w, x) => binary_expr!(Expr::FPToUBV, w, x), + spec::ExprKind::FPToSBV(w, x) => binary_expr!(Expr::FPToSBV, w, x), + spec::ExprKind::WidthOf(x) => unary_expr!(Expr::WidthOf, x), + + spec::ExprKind::As(x, ty) => { + let x = self.spec_expr(x, vars)?; + self.conditions.qualifiers.push(Qualifier { + value: x.clone(), + ty: ty.clone(), + }); + Ok(x) + } + + spec::ExprKind::FPPositiveInfinity(x) => unary_expr!(Expr::FPPositiveInfinity, x), + spec::ExprKind::FPNegativeInfinity(x) => unary_expr!(Expr::FPNegativeInfinity, x), + spec::ExprKind::FPPositiveZero(x) => unary_expr!(Expr::FPPositiveZero, x), + spec::ExprKind::FPNegativeZero(x) => unary_expr!(Expr::FPNegativeZero, x), + spec::ExprKind::FPNaN(x) => unary_expr!(Expr::FPNaN, x), + spec::ExprKind::FPEq(x, y) => binary_expr!(Expr::FPEq, x, y), + spec::ExprKind::FPNe(x, y) => binary_expr!(Expr::FPNe, x, y), + spec::ExprKind::FPLt(x, y) => binary_expr!(Expr::FPLt, x, y), + spec::ExprKind::FPGt(x, y) => binary_expr!(Expr::FPGt, x, y), + spec::ExprKind::FPLe(x, y) => binary_expr!(Expr::FPLe, x, y), + spec::ExprKind::FPGe(x, y) => binary_expr!(Expr::FPGe, x, y), + spec::ExprKind::FPAdd(x, y) => binary_expr!(Expr::FPAdd, x, y), + spec::ExprKind::FPSub(x, y) => binary_expr!(Expr::FPSub, x, y), + spec::ExprKind::FPMul(x, y) => binary_expr!(Expr::FPMul, x, y), + spec::ExprKind::FPDiv(x, y) => binary_expr!(Expr::FPDiv, x, y), + spec::ExprKind::FPMin(x, y) => binary_expr!(Expr::FPMin, x, y), + spec::ExprKind::FPMax(x, y) => binary_expr!(Expr::FPMax, x, y), + spec::ExprKind::FPNeg(x) => unary_expr!(Expr::FPNeg, x), + spec::ExprKind::FPCeil(x) => unary_expr!(Expr::FPCeil, x), + spec::ExprKind::FPFloor(x) => unary_expr!(Expr::FPFloor, x), + spec::ExprKind::FPSqrt(x) => unary_expr!(Expr::FPSqrt, x), + spec::ExprKind::FPTrunc(x) => unary_expr!(Expr::FPTrunc, x), + spec::ExprKind::FPNearest(x) => unary_expr!(Expr::FPNearest, x), + spec::ExprKind::FPIsZero(x) => unary_expr!(Expr::FPIsZero, x), + spec::ExprKind::FPIsInfinite(x) => unary_expr!(Expr::FPIsInfinite, x), + spec::ExprKind::FPIsNaN(x) => unary_expr!(Expr::FPIsNaN, x), + spec::ExprKind::FPIsNegative(x) => unary_expr!(Expr::FPIsNegative, x), + spec::ExprKind::FPIsPositive(x) => unary_expr!(Expr::FPIsPositive, x), + + spec::ExprKind::Macro(params, body) => Ok(Symbolic::Macro(Macro { + params: params.clone(), + body: body.clone(), + })), + } + } + + fn spec_expr_no_vars(&mut self, expr: &spec::Expr) -> Result { + let no_vars = Variables::new(); + self.spec_expr(expr, &no_vars) + } + + fn spec_typed_value(&mut self, val: i128, ty: &Type) -> Result { + match ty { + Type::Bool => Ok(self.boolean(match val { + 0 => false, + 1 => true, + _ => bail!("boolean value must be zero or one"), + })), + Type::Int => Ok(self.constant(Const::Int(val))), + Type::BitVector(Width::Bits(w)) => { + Ok(self.constant(Const::BitVector(*w, val.try_into()?))) + } + _ => bail!("cannot construct constant of type {ty}"), + } + } + + fn construct(&mut self, constructor: &Constructor, vars: &Variables) -> Result { + match constructor { + Constructor::Enum { + name, + variant, + args, + } => { + // Lookup ISLE type by name. + let type_id = self + .prog + .tyenv + .get_type_by_name(name) + .ok_or(self.error(format!("unknown enum type {name}", name = name.0)))?; + + // Determine type model. + let model = self.prog.specenv.type_model.get(&type_id).ok_or( + self.error(format!("unspecified model for type {name}", name = name.0)), + )?; + + // Should be an enum. + let e = model.as_enum().ok_or( + self.error(format!("{name} expected to have enum type", name = name.0)), + )?; + + // Lookup variant. + let variant = + e.variants.iter().find(|v| v.name.0 == variant.0).ok_or( + self.error(format!("unknown variant {variant}", variant = variant.0)), + )?; + + // Discriminant: constant value since we are constructing a known variant. + let discriminant = self.constant(Const::Int(variant.id.index().try_into()?)); + + // Variants: undefined except for the variant under construction. + let variants = e + .variants + .iter() + .map(|v| { + // For all except the variant under construction, allocate an undefined variant. + if v.id != variant.id { + // QUESTION(mbm): use undef variant or IfThen and fresh bits in solver? + return self.alloc_variant(v, "undef".to_string()); + } + + // Construct a variant provided arguments. + assert_eq!(args.len(), v.fields.len()); + let fields = zip(&v.fields, args) + .map(|(f, a)| { + Ok(SymbolicField { + name: f.name.0.clone(), + value: self.spec_expr(a, vars)?, + }) + }) + .collect::>()?; + Ok(SymbolicVariant { + name: v.name.0.clone(), + id: v.id, + discriminant: v.id.index(), + value: Symbolic::Struct(fields), + }) + }) + .collect::>()?; + + Ok(self.new_enum(type_id, discriminant, variants)?) + } + Constructor::Struct { fields } => Ok(Symbolic::Struct( + fields + .iter() + .map(|f| { + Ok(SymbolicField { + name: f.name.0.clone(), + value: self.spec_expr(&f.value, vars)?, + }) + }) + .collect::>()?, + )), + } + } + + fn spec_field(&mut self, name: &Ident, v: Symbolic) -> Result { + log::trace!("access field {} from {}", name.0, v); + + // 1. If ExtEnum: check extra fields FIRST + if let Symbolic::ExtEnum(_base_enum, extra_fields) = &v { + if let Some(f) = extra_fields.iter().find(|f| f.name == name.0) { + return Ok(f.value.clone()); + } + + // 2. If not found, fall through to base enum variant payload + if let Some(e) = v.as_enum() { + for variant in &e.variants { + if let Ok(fields) = variant.fields() { + if let Some(f) = fields.iter().find(|f| f.name == name.0) { + return Ok(f.value.clone()); + } + } + } + } + + // 3. Otherwise: truly nonexistent + return Err(self.error(format!( + "attempt to access nonexistent field: {}", + name.0 + ))); + } + + // Normal struct case + if let Some(fields) = v.as_struct() { + if let Some(f) = fields.iter().find(|f| f.name == name.0) { + return Ok(f.value.clone()); + } else { + return Err(self.error(format!( + "attempt to access nonexistent struct field: {}", + name.0 + ))); + } + } + + Err(self.error(format!( + "field access from non-struct value: {}", + name.0 + ))) + } + + + fn spec_discriminator(&mut self, name: &Ident, v: Symbolic) -> Result { + let e = v + .as_enum() + .ok_or(self.error("discriminator for non-enum value"))?; + let variant = e.try_variant_by_name(&name.0)?; + let discriminator = self.discriminator(e, variant); + Ok(discriminator.into()) + } + + fn discriminator(&mut self, e: &SymbolicEnum, variant: &SymbolicVariant) -> ExprId { + let discriminant = self.constant(Const::Int(variant.discriminant.try_into().unwrap())); + self.exprs_equal(e.discriminant, discriminant) + } + + fn spec_switch( + &mut self, + on: &spec::Expr, + arms: &[(spec::Expr, spec::Expr)], + vars: &Variables, + ) -> Result { + // Generate branch arms. + let on = self.spec_expr(on, vars)?; + let cases = arms + .iter() + .map(|(value, then)| { + let value = self.spec_expr(value, vars)?; + let cond = self.values_equal(on.clone(), value)?; + Ok((cond, self.spec_expr(then, vars)?)) + }) + .collect::>>()?; + + // Build an expression splitting over cases. + self.cases(&cases) + } + + fn spec_match(&mut self, on: &spec::Expr, arms: &[Arm], vars: &Variables) -> Result { + // Generate the enum value to match on. + let on = self.spec_expr(on, vars)?; + let e = on.as_enum().ok_or(self.error("match on non-enum value"))?; + + // Generate cases. + let mut cases = Vec::new(); + for arm in arms { + // Lookup the variant. + let variant = e.try_variant_by_name(&arm.variant.0)?; + + // Arm condition is that the discriminant matches the variant. + let cond = self.discriminator(e, variant); + + // Arm value is the result of the body expression, evaluated with + // the variants fields brought into scope. + let Some(fields) = variant.value.as_struct() else { + bail!("variant {name} must have struct value", name = variant.name); + }; + if arm.args.len() != fields.len() { + bail!( + "incorrect number of arguments for variant {name}", + name = variant.name + ); + } + let mut arm_vars = vars.clone(); + for (arg, field) in zip(&arm.args, fields) { + arm_vars.set(arg.0.clone(), field.value.clone())?; + } + let body = self.spec_expr(&arm.body, &arm_vars)?; + + // Add case for this match arm. + cases.push((cond, body)); + } + + // Build an expression splitting over cases. + self.cases(&cases) + } + + fn cases(&mut self, cases: &[(ExprId, Symbolic)]) -> Result { + // Build an undefined fallback value. + let Some((_, value)) = cases.last() else { + bail!("must have at least one case"); + }; + let fallback = value.scalar_map(&mut |_| self.undef_variable()); + + // Represent as nested conditionals. + cases + .iter() + .rev() + .cloned() + .try_fold(fallback, |acc, (cond, then)| { + self.conditional(cond, then, acc) + }) + } + + fn spec_let( + &mut self, + defs: &[(Ident, spec::Expr)], + body: &spec::Expr, + vars: &Variables, + ) -> Result { + // Evaluate let defs. + let mut let_vars = vars.clone(); + for (name, expr) in defs { + let expr = self.spec_expr(expr, &let_vars)?; + let_vars.set(name.0.clone(), expr)?; + } + + // Evaluate body in let-binding scope. + self.spec_expr(body, &let_vars) + } + + fn spec_with( + &mut self, + decls: &[Ident], + body: &spec::Expr, + vars: &Variables, + ) -> Result { + // Declare new variables. + let mut with_vars = vars.clone(); + for name in decls { + // QUESTION(mbm): allow with scopes to optionally specify types? + let expr = Symbolic::Scalar(self.alloc_variable(Type::Unknown, name.0.clone())); + with_vars.set(name.0.clone(), expr)?; + } + + // Evaluate body in new scope. + self.spec_expr(body, &with_vars) + } + + fn spec_expand( + &mut self, + name: &Ident, + args: &[spec::Expr], + vars: &Variables, + ) -> Result { + // Lookup macro. + // + // Could be an inline macro in a local variable, or a macro defined at global scope. + let (params, body) = if let Some(v) = vars.get(&name.0) { + let Symbolic::Macro(m) = v else { + bail!("variable {name} is not a macro", name = name.0); + }; + (&m.params, &m.body) + } else { + let defn = self + .prog + .specenv + .macros + .get(&name.0) + .ok_or(self.error(format!("unknown macro {name}", name = name.0)))?; + (&defn.params, &defn.body) + }; + + // Build macro expansion scope. + // QUESTION(mbm): should macros be able to access global state? + let mut macro_vars = Variables::new(); + if params.len() != args.len() { + bail!( + "incorrect number of arguments for macro {name}", + name = name.0 + ); + } + for (param, arg) in zip(params, args) { + let arg = self.spec_expr(arg, vars)?; + macro_vars.set(param.0.clone(), arg)?; + } + + // Evaluate macro body. + self.spec_expr(&body, ¯o_vars) + } + + fn replicate(&mut self, x: ExprId, n: usize) -> Result { + match n { + 0 => bail!("cannot replicate zero times"), + 1 => Ok(x), + _ => { + let h = n / 2; + let l = self.replicate(x, h)?; + let r = self.replicate(x, n - h)?; + Ok(self.dedup_expr(Expr::BVConcat(l, r))) + } + } + } + + fn conditional(&mut self, c: ExprId, t: Symbolic, e: Symbolic) -> Result { + Symbolic::merge(&t, &e, &mut |t, e| { + self.dedup_expr(Expr::Conditional(c, t, e)) + }) + } + + fn bindings_equal(&mut self, a: BindingId, b: BindingId) -> Result { + // TODO(mbm): can this be done without clones? + let a = self.binding_value[&a].clone(); + let b = self.binding_value[&b].clone(); + self.values_equal(a, b) + } + + fn values_equal(&mut self, a: Symbolic, b: Symbolic) -> Result { + if std::mem::discriminant(&a) != std::mem::discriminant(&b) { + return Err(self.error("equality on different symbolic types")); + } + match (a, b) { + (Symbolic::Scalar(u), Symbolic::Scalar(v)) => Ok(self.exprs_equal(u, v)), + + (Symbolic::Struct(us), Symbolic::Struct(vs)) => { + // Field-wise equality. + // TODO(mbm): can we expect that structs are the same length? + assert_eq!(us.len(), vs.len(), "field length mismatch"); + let fields_eq = zip(us, vs) + .map(|(fu, fv)| { + assert_eq!(fu.name, fv.name, "field name mismatch"); + self.values_equal(fu.value, fv.value) + }) + .collect::>()?; + + // All fields must be equal. + Ok(self.all(fields_eq)) + } + + (Symbolic::Enum(u), Symbolic::Enum(v)) => { + // Discriminant equality. + let discriminants_eq = self.exprs_equal(u.discriminant, v.discriminant); + let mut equalities = vec![discriminants_eq]; + + // Variant equality conditions. + assert_eq!(u.variants.len(), v.variants.len(), "variant count mismatch"); + let variants_eq = zip(&u.variants, &v.variants) + .map(|(uv, vv)| { + assert_eq!(uv.name, vv.name, "variant name mismatch"); + let ud = self.discriminator(&u, uv); + let eq = self.values_equal(uv.value.clone(), vv.value.clone())?; + Ok(self.dedup_expr(Expr::Imp(ud, eq))) + }) + .collect::>>()?; + equalities.extend(variants_eq); + + // Combine discriminant and variant conditions. + Ok(self.all(equalities)) + } + + (Symbolic::Enum(u), Symbolic::ExtEnum(v, _extra)) + | (Symbolic::ExtEnum(v, _extra), Symbolic::Enum(u)) => { + // Discriminant equality + let discriminants_eq = self.exprs_equal(u.discriminant, v.discriminant); + let mut equalities = vec![discriminants_eq]; + + // Compare only the base variant payloads + assert_eq!(u.variants.len(), v.variants.len()); + let variants_eq = zip(&u.variants, &v.variants) + .map(|(uv, vv)| { + assert_eq!(uv.name, vv.name); + let ud = self.discriminator(&u, uv); + let eq = self.values_equal(uv.value.clone(), vv.value.clone())?; + Ok(self.dedup_expr(Expr::Imp(ud, eq))) + }) + .collect::>>()?; + + equalities.extend(variants_eq); + Ok(self.all(equalities)) + } + + + (Symbolic::Tuple(us), Symbolic::Tuple(vs)) => { + // Field-wise equality. + // TODO(mbm): can we expect that tuples are the same length? + assert_eq!(us.len(), vs.len(), "tuple length mismatch"); + let fields_eq = zip(us, vs) + .map(|(u, v)| self.values_equal(u, v)) + .collect::>()?; + + // All fields must be equal. + Ok(self.all(fields_eq)) + } + + ref c => todo!("values equal: {c:?}"), + } + } + + fn exprs_equal(&mut self, lhs: ExprId, rhs: ExprId) -> ExprId { + self.dedup_expr(Expr::Eq(lhs, rhs)) + } + + fn all(&mut self, exprs: Vec) -> ExprId { + exprs + .into_iter() + .reduce(|acc, e| self.dedup_expr(Expr::And(acc, e))) + .unwrap_or_else(|| self.boolean(true)) + } + + fn any(&mut self, exprs: Vec) -> ExprId { + exprs + .into_iter() + .reduce(|acc, e| self.dedup_expr(Expr::Or(acc, e))) + .unwrap_or_else(|| self.boolean(false)) + } + + fn boolean(&mut self, value: bool) -> ExprId { + self.constant(Const::Bool(value)) + } + + fn constant(&mut self, c: Const) -> ExprId { + self.dedup_expr(Expr::Const(c)) + } + + /// Determine the type of the given binding in the context of the + /// [Expansion] we are constructing verification conditions for. + fn binding_type(&self, binding: &Binding) -> BindingType { + binding_type( + binding, + self.expansion.term, + self.prog, + |binding_id: BindingId| self.expansion.bindings[binding_id.index()].clone().unwrap(), + ) + } + + fn alloc_binding(&mut self, binding_type: &BindingType, name: String) -> Result { + match binding_type { + BindingType::Base(type_id) => self.alloc_model(*type_id, name), + BindingType::Option(inner_type) => { + let some = self.alloc_variable(Type::Bool, Variable::component_name(&name, "some")); + let inner = Box::new( + self.alloc_binding(inner_type, Variable::component_name(&name, "inner"))?, + ); + Ok(Symbolic::Option(SymbolicOption { some, inner })) + } + BindingType::Tuple(inners) => { + let inners = inners + .iter() + .enumerate() + .map(|(i, inner_type)| { + self.alloc_binding( + inner_type, + Variable::component_name(&name, &i.to_string()), + ) + }) + .collect::>()?; + Ok(Symbolic::Tuple(inners)) + } + } + } + + fn alloc_value(&mut self, ty: &Compound, name: String) -> Result { + match ty { + Compound::Primitive(ty) => Ok(Symbolic::Scalar(self.alloc_variable(ty.clone(), name))), + Compound::Struct(fields) => Ok(Symbolic::Struct( + fields + .iter() + .map(|f| { + Ok(SymbolicField { + name: f.name.0.clone(), + value: self + .alloc_value(&f.ty, Variable::component_name(&name, &f.name.0))?, + }) + }) + .collect::>()?, + )), + Compound::Enum(e) => { + let discriminant = + self.alloc_variable(Type::Int, Variable::component_name(&name, "discriminant")); + let variants = e + .variants + .iter() + .map(|v| self.alloc_variant(v, name.clone())) + .collect::>()?; + Ok(self.new_enum(e.id, discriminant, variants)?) + } + Compound::ExtEnum { base, extra } => { + // Build the SymbolicEnum exactly like the `Enum` arm does. + + // 1) discriminator variable for the enum + let discriminant = self.alloc_variable( + Type::Int, + Variable::component_name(&name, "discriminant"), + ); + + // 2) per-variant payloads + let variants = base + .variants + .iter() + .map(|v| self.alloc_variant(v, name.clone())) + .collect::>()?; + + // 3) assemble a SymbolicEnum (same fields your `Enum` arm uses) + let sym_enum = SymbolicEnum { + ty: base.id, + discriminant, + variants, + }; + + // Now allocate the extra struct-like fields. + let extra_syms = extra + .iter() + .map(|f| { + Ok(SymbolicField { + name: f.name.0.clone(), + value: self.alloc_value( + &f.ty, + Variable::component_name(&name, &f.name.0), + )?, + }) + }) + .collect::>>()?; + + // `Symbolic::ExtEnum` is a *tuple* variant: (SymbolicEnum, Vec) + Ok(Symbolic::ExtEnum(sym_enum, extra_syms)) + } + + + Compound::Named(_) => { + let ty = self.prog.specenv.resolve_type(ty, &self.prog.tyenv)?; + self.alloc_value(&ty, name) + } + } + } + + fn new_enum( + &mut self, + ty: TypeId, + discriminant: ExprId, + variants: Vec, + ) -> Result { + // Construct symbolic enum and ensure it's valid. + let e = SymbolicEnum { + ty, + discriminant, + variants, + }; + e.validate()?; + + // Assume discriminant invariant: positive integer less than number of + // variants. + let zero = self.constant(Const::Int(0)); + let num_variants = self.constant(Const::Int(e.variants.len().try_into()?)); + let discriminant_positive = self.dedup_expr(Expr::Lte(zero, discriminant)); + let discriminant_less_than_num_variants = + self.dedup_expr(Expr::Lt(discriminant, num_variants)); + let discriminant_in_range = self.dedup_expr(Expr::And( + discriminant_positive, + discriminant_less_than_num_variants, + )); + self.conditions.assumptions.push(discriminant_in_range); + + // Variant term instantiations. + let ret = Symbolic::Enum(e.clone()); + for variant in &e.variants { + let term = self.prog.get_variant_term(e.ty, variant.id); + let args = variant.field_values()?; + self.record_term_instantiation(term, args, ret.clone())?; + } + + Ok(ret) + } + + fn alloc_variant(&mut self, variant: &Variant, name: String) -> Result { + let name = Variable::component_name(&name, &variant.name.0); + Ok(SymbolicVariant { + name: variant.name.0.clone(), + id: variant.id, + discriminant: variant.id.index(), + value: self.alloc_value(&variant.ty(), name)?, + }) + } + + fn alloc_model(&mut self, type_id: TypeId, name: String) -> Result { + let type_name = self.prog.type_name(type_id); + let ty = self + .prog + .specenv + .type_model + .get(&type_id) + .ok_or(self.error(format!("unspecified model for type {type_name}")))?; + self.alloc_value(ty, name) + } + + fn undef_variable(&mut self) -> ExprId { + self.alloc_variable(Type::Unknown, "undef".to_string()) + } + + fn alloc_variable(&mut self, ty: Type, name: String) -> ExprId { + let v = VariableId(self.conditions.variables.len()); + self.conditions.variables.push(Variable { ty, name }); + self.dedup_expr(Expr::Variable(v)) + } + + fn scalar(&mut self, expr: Expr) -> Symbolic { + Symbolic::Scalar(self.dedup_expr(expr)) + } + + fn as_scalar(&self, v: Symbolic) -> Result { + v.as_scalar().ok_or(self.error("expected scalar value")) + } + + fn dedup_expr(&mut self, expr: Expr) -> ExprId { + // Dedupe, if pure. + let maybe_id = if expr.pure() { + self.expr_map.get(&expr) + } else { + None + }; + + // Otherwise, allocate new one. + let id = if let Some(id) = maybe_id { + *id + } else { + let id = ExprId(self.conditions.exprs.len()); + self.conditions.exprs.push(expr.clone()); + self.expr_map.insert(expr, id); + id + }; + + if let Some(pos) = self.position_stack.last() { + self.conditions.pos.insert(id, *pos); + } + + id + } + + fn error(&self, msg: impl Into) -> Error { + if let Some(pos) = self.position_stack.last() { + self.prog.error_at_pos(*pos, msg).into() + } else { + Error::msg(msg.into()) + } + } +} diff --git a/cranelift/isle/veri/veri/tests/filetests.rs b/cranelift/isle/veri/veri/tests/filetests.rs new file mode 100644 index 000000000000..91bf9973df08 --- /dev/null +++ b/cranelift/isle/veri/veri/tests/filetests.rs @@ -0,0 +1,27 @@ +use std::path::PathBuf; + +use cranelift_isle_veri::runner::Runner; +use cranelift_isle_veri_test_macros::file_tests; +use tempfile::tempdir; + +#[file_tests(path = "filetests/pass", ext = "isle")] +fn pass(test_file: &str) { + let inputs = vec![PathBuf::from(test_file)]; + let mut runner = Runner::from_files(&inputs, "test").expect("should be able to create runner"); + let temp_dir = tempdir().expect("should be able to create temporary log directory"); + runner.set_log_dir(temp_dir.path().join("log")); + runner.include_first_rule_named(); + runner.set_root_term("test"); + runner.run().expect("verification should pass"); +} + +#[file_tests(path = "filetests/broken", ext = "isle")] +fn broken(test_file: &str) { + let inputs = vec![PathBuf::from(test_file)]; + let mut runner = Runner::from_files(&inputs, "test").expect("should be able to create runner"); + let temp_dir = tempdir().expect("should be able to create temporary log directory"); + runner.set_log_dir(temp_dir.path().join("log")); + runner.include_first_rule_named(); + runner.set_root_term("test"); + runner.run().expect_err("verification should fail"); +} diff --git a/cranelift/jit/Cargo.toml b/cranelift/jit/Cargo.toml index 0d367c7a32cd..28ea982991cc 100644 --- a/cranelift/jit/Cargo.toml +++ b/cranelift/jit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-jit" -version = "0.101.0" +version = "0.112.0" authors = ["The Cranelift Project Developers"] description = "A JIT library backed by Cranelift" repository = "https://github.com/bytecodealliance/wasmtime" @@ -8,6 +8,10 @@ documentation = "https://docs.rs/cranelift-jit" license = "Apache-2.0 WITH LLVM-exception" readme = "README.md" edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [dependencies] cranelift-module = { workspace = true } @@ -17,7 +21,7 @@ cranelift-entity = { workspace = true } cranelift-control = { workspace = true } anyhow = { workspace = true } region = "2.2.0" -libc = { version = "0.2.42" } +libc = { workspace = true } target-lexicon = { workspace = true } memmap2 = { version = "0.2.1", optional = true } log = { workspace = true } @@ -36,6 +40,6 @@ selinux-fix = ['memmap2'] default = [] [dev-dependencies] -cranelift = { workspace = true } +cranelift = { path = "../umbrella" } cranelift-frontend = { workspace = true } cranelift-entity = { workspace = true } diff --git a/cranelift/jit/examples/jit-minimal.rs b/cranelift/jit/examples/jit-minimal.rs index 3ebf7536f9c3..f806929bc153 100644 --- a/cranelift/jit/examples/jit-minimal.rs +++ b/cranelift/jit/examples/jit-minimal.rs @@ -1,6 +1,5 @@ use codegen::ir::UserFuncName; use cranelift::prelude::*; -use cranelift_codegen::settings::{self, Configurable}; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{default_libcall_names, Linkage, Module}; use std::mem; @@ -11,7 +10,7 @@ fn main() { // FIXME set back to true once the x64 backend supports it. flag_builder.set("is_pic", "false").unwrap(); let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { - panic!("host machine is not supported: {}", msg); + panic!("host machine is not supported: {msg}"); }); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) @@ -68,7 +67,7 @@ fn main() { let value = { let results = bcx.inst_results(call); assert_eq!(results.len(), 1); - results[0].clone() + results[0] }; bcx.ins().return_(&[value]); bcx.seal_all_blocks(); diff --git a/cranelift/jit/src/backend.rs b/cranelift/jit/src/backend.rs index 438c60d49e27..8f95a30b5a7a 100644 --- a/cranelift/jit/src/backend.rs +++ b/cranelift/jit/src/backend.rs @@ -4,7 +4,7 @@ use crate::{compiled_blob::CompiledBlob, memory::BranchProtection, memory::Memor use cranelift_codegen::binemit::Reloc; use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::settings::Configurable; -use cranelift_codegen::{self, ir, settings, FinalizedMachReloc}; +use cranelift_codegen::{ir, settings, FinalizedMachReloc}; use cranelift_control::ControlPlane; use cranelift_entity::SecondaryMap; use cranelift_module::{ @@ -14,7 +14,6 @@ use cranelift_module::{ use log::info; use std::cell::RefCell; use std::collections::HashMap; -use std::convert::{TryFrom, TryInto}; use std::ffi::CString; use std::io::Write; use std::ptr; @@ -28,8 +27,8 @@ const READONLY_DATA_ALIGNMENT: u64 = 0x1; /// A builder for `JITModule`. pub struct JITBuilder { isa: OwnedTargetIsa, - symbols: HashMap, - lookup_symbols: Vec Option<*const u8>>>, + symbols: HashMap>, + lookup_symbols: Vec Option<*const u8> + Send>>, libcall_names: Box String + Send + Sync>, hotswap_enabled: bool, } @@ -68,7 +67,7 @@ impl JITBuilder { flag_builder.set("use_colocated_libcalls", "false").unwrap(); flag_builder.set("is_pic", "true").unwrap(); let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { - panic!("host machine is not supported: {}", msg); + panic!("host machine is not supported: {msg}"); }); let isa = isa_builder.finish(settings::Flags::new(flag_builder))?; Ok(Self::with_isa(isa, libcall_names)) @@ -117,7 +116,7 @@ impl JITBuilder { where K: Into, { - self.symbols.insert(name.into(), ptr); + self.symbols.insert(name.into(), SendWrapper(ptr)); self } @@ -130,7 +129,7 @@ impl JITBuilder { K: Into, { for (name, ptr) in symbols { - self.symbols.insert(name.into(), ptr); + self.symbols.insert(name.into(), SendWrapper(ptr)); } self } @@ -141,7 +140,7 @@ impl JITBuilder { /// symbol table. Symbol lookup fn's are called in reverse of the order in which they were added. pub fn symbol_lookup_fn( &mut self, - symbol_lookup_fn: Box Option<*const u8>>, + symbol_lookup_fn: Box Option<*const u8> + Send>, ) -> &mut Self { self.lookup_symbols.push(symbol_lookup_fn); self @@ -166,6 +165,15 @@ struct GotUpdate { ptr: *const u8, } +unsafe impl Send for GotUpdate {} + +/// A wrapper that impls Send for the contents. +/// +/// SAFETY: This must not be used for any types where it would be UB for them to be Send +#[derive(Copy, Clone)] +struct SendWrapper(T); +unsafe impl Send for SendWrapper {} + /// A `JITModule` implements `Module` and emits code and data into memory where it can be /// directly called and accessed. /// @@ -173,16 +181,16 @@ struct GotUpdate { pub struct JITModule { isa: OwnedTargetIsa, hotswap_enabled: bool, - symbols: RefCell>, - lookup_symbols: Vec Option<*const u8>>>, - libcall_names: Box String>, + symbols: RefCell>>, + lookup_symbols: Vec Option<*const u8> + Send>>, + libcall_names: Box String + Send + Sync>, memory: MemoryHandle, declarations: ModuleDeclarations, - function_got_entries: SecondaryMap>>>, - function_plt_entries: SecondaryMap>>, - data_object_got_entries: SecondaryMap>>>, - libcall_got_entries: HashMap>>, - libcall_plt_entries: HashMap>, + function_got_entries: SecondaryMap>>>>, + function_plt_entries: SecondaryMap>>>, + data_object_got_entries: SecondaryMap>>>>, + libcall_got_entries: HashMap>>>, + libcall_plt_entries: HashMap>>, compiled_functions: SecondaryMap>, compiled_data_objects: SecondaryMap>, functions_to_finalize: Vec, @@ -204,7 +212,7 @@ impl JITModule { /// /// # Safety /// - /// Because this function invalidates any pointers retrived from the + /// Because this function invalidates any pointers retrieved from the /// corresponding module, it should only be used when none of the functions /// from that module are currently executing and none of the `fn` pointers /// are called afterwards. @@ -216,7 +224,7 @@ impl JITModule { fn lookup_symbol(&self, name: &str) -> Option<*const u8> { match self.symbols.borrow_mut().entry(name.to_owned()) { - std::collections::hash_map::Entry::Occupied(occ) => Some(*occ.get()), + std::collections::hash_map::Entry::Occupied(occ) => Some(occ.get().0), std::collections::hash_map::Entry::Vacant(vac) => { let ptr = self .lookup_symbols @@ -224,7 +232,7 @@ impl JITModule { .rev() // Try last lookup function first .find_map(|lookup| lookup(name)); if let Some(ptr) = ptr { - vac.insert(ptr); + vac.insert(SendWrapper(ptr)); } ptr } @@ -267,7 +275,7 @@ impl JITModule { fn new_func_plt_entry(&mut self, id: FuncId, val: *const u8) { let got_entry = self.new_got_entry(val); - self.function_got_entries[id] = Some(got_entry); + self.function_got_entries[id] = Some(SendWrapper(got_entry)); let plt_entry = self.new_plt_entry(got_entry); self.record_function_for_perf( plt_entry.as_ptr().cast(), @@ -277,12 +285,12 @@ impl JITModule { self.declarations.get_function_decl(id).linkage_name(id) ), ); - self.function_plt_entries[id] = Some(plt_entry); + self.function_plt_entries[id] = Some(SendWrapper(plt_entry)); } fn new_data_got_entry(&mut self, id: DataId, val: *const u8) { let got_entry = self.new_got_entry(val); - self.data_object_got_entries[id] = Some(got_entry); + self.data_object_got_entries[id] = Some(SendWrapper(got_entry)); } unsafe fn write_plt_entry_bytes(plt_ptr: *mut [u8; 16], got_ptr: NonNull>) { @@ -334,13 +342,13 @@ impl JITModule { } else if linkage == Linkage::Preemptible { 0 as *const u8 } else { - panic!("can't resolve symbol {}", name); + panic!("can't resolve symbol {name}"); } } ModuleRelocTarget::LibCall(ref libcall) => { let sym = (self.libcall_names)(*libcall); self.lookup_symbol(&sym) - .unwrap_or_else(|| panic!("can't resolve libcall {}", sym)) + .unwrap_or_else(|| panic!("can't resolve libcall {sym}")) } _ => panic!("invalid name"), } @@ -351,7 +359,7 @@ impl JITModule { /// Panics if there's no entry in the table for the given function. pub fn read_got_entry(&self, func_id: FuncId) -> *const u8 { let got_entry = self.function_got_entries[func_id].unwrap(); - unsafe { got_entry.as_ref() }.load(Ordering::SeqCst) + unsafe { got_entry.0.as_ref() }.load(Ordering::SeqCst) } fn get_got_address(&self, name: &ModuleRelocTarget) -> NonNull> { @@ -359,16 +367,18 @@ impl JITModule { ModuleRelocTarget::User { .. } => { if ModuleDeclarations::is_function(name) { let func_id = FuncId::from_name(name); - self.function_got_entries[func_id].unwrap() + self.function_got_entries[func_id].unwrap().0 } else { let data_id = DataId::from_name(name); - self.data_object_got_entries[data_id].unwrap() + self.data_object_got_entries[data_id].unwrap().0 } } - ModuleRelocTarget::LibCall(ref libcall) => *self - .libcall_got_entries - .get(libcall) - .unwrap_or_else(|| panic!("can't resolve libcall {}", libcall)), + ModuleRelocTarget::LibCall(ref libcall) => { + self.libcall_got_entries + .get(libcall) + .unwrap_or_else(|| panic!("can't resolve libcall {libcall}")) + .0 + } _ => panic!("invalid name"), } } @@ -380,6 +390,7 @@ impl JITModule { let func_id = FuncId::from_name(name); self.function_plt_entries[func_id] .unwrap() + .0 .as_ptr() .cast::() } else { @@ -389,7 +400,8 @@ impl JITModule { ModuleRelocTarget::LibCall(ref libcall) => self .libcall_plt_entries .get(libcall) - .unwrap_or_else(|| panic!("can't resolve libcall {}", libcall)) + .unwrap_or_else(|| panic!("can't resolve libcall {libcall}")) + .0 .as_ptr() .cast::(), _ => panic!("invalid name"), @@ -544,9 +556,13 @@ impl JITModule { continue; }; let got_entry = module.new_got_entry(addr); - module.libcall_got_entries.insert(libcall, got_entry); + module + .libcall_got_entries + .insert(libcall, SendWrapper(got_entry)); let plt_entry = module.new_plt_entry(got_entry); - module.libcall_plt_entries.insert(libcall, plt_entry); + module + .libcall_plt_entries + .insert(libcall, SendWrapper(plt_entry)); } module @@ -712,7 +728,7 @@ impl Module for JITModule { .buffer .relocs() .iter() - .map(|reloc| ModuleReloc::from_mach_reloc(reloc, &ctx.func)) + .map(|reloc| ModuleReloc::from_mach_reloc(reloc, &ctx.func, id)) .collect(); self.record_function_for_perf(ptr, size, &decl.linkage_name(id)); @@ -720,7 +736,7 @@ impl Module for JITModule { if self.isa.flags().is_pic() { self.pending_got_updates.push(GotUpdate { - entry: self.function_got_entries[id].unwrap(), + entry: self.function_got_entries[id].unwrap().0, ptr, }) } @@ -737,7 +753,8 @@ impl Module for JITModule { ModuleRelocTarget::LibCall(ref libcall) => self .libcall_plt_entries .get(libcall) - .unwrap_or_else(|| panic!("can't resolve libcall {}", libcall)) + .unwrap_or_else(|| panic!("can't resolve libcall {libcall}")) + .0 .as_ptr() .cast::(), _ => panic!("invalid name"), @@ -797,13 +814,13 @@ impl Module for JITModule { size, relocs: relocs .iter() - .map(|reloc| ModuleReloc::from_mach_reloc(reloc, func)) + .map(|reloc| ModuleReloc::from_mach_reloc(reloc, func, id)) .collect(), }); if self.isa.flags().is_pic() { self.pending_got_updates.push(GotUpdate { - entry: self.function_got_entries[id].unwrap(), + entry: self.function_got_entries[id].unwrap().0, ptr, }) } @@ -851,7 +868,11 @@ impl Module for JITModule { } = data; let size = init.size(); - let ptr = if decl.writable { + let ptr = if size == 0 { + // Return a correctly aligned non-null pointer to avoid UB in write_bytes and + // copy_nonoverlapping. + usize::try_from(align.unwrap_or(WRITABLE_DATA_ALIGNMENT)).unwrap() as *mut u8 + } else if decl.writable { self.memory .writable .allocate(size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT)) @@ -869,6 +890,17 @@ impl Module for JITModule { })? }; + if ptr.is_null() { + // FIXME pass a Layout to allocate and only compute the layout once. + std::alloc::handle_alloc_error( + std::alloc::Layout::from_size_align( + size, + align.unwrap_or(READONLY_DATA_ALIGNMENT).try_into().unwrap(), + ) + .unwrap(), + ); + } + match *init { Init::Uninitialized => { panic!("data is not initialized yet"); @@ -893,7 +925,7 @@ impl Module for JITModule { self.data_objects_to_finalize.push(id); if self.isa.flags().is_pic() { self.pending_got_updates.push(GotUpdate { - entry: self.data_object_got_entries[id].unwrap(), + entry: self.data_object_got_entries[id].unwrap().0, ptr, }) } diff --git a/cranelift/jit/src/compiled_blob.rs b/cranelift/jit/src/compiled_blob.rs index de01855c6d7b..4eab207ff011 100644 --- a/cranelift/jit/src/compiled_blob.rs +++ b/cranelift/jit/src/compiled_blob.rs @@ -1,7 +1,6 @@ use cranelift_codegen::binemit::Reloc; use cranelift_module::ModuleReloc; use cranelift_module::ModuleRelocTarget; -use std::convert::TryFrom; /// Reads a 32bit instruction at `iptr`, and writes it again after /// being altered by `modifier` @@ -18,6 +17,8 @@ pub(crate) struct CompiledBlob { pub(crate) relocs: Vec, } +unsafe impl Send for CompiledBlob {} + impl CompiledBlob { pub(crate) fn perform_relocations( &self, @@ -93,8 +94,37 @@ impl CompiledBlob { let imm26 = (diff as u32) << chop >> chop; unsafe { modify_inst32(iptr, |inst| inst | imm26) }; } - Reloc::RiscvCall => { - // A R_RISCV_CALL relocation expects auipc+jalr instruction pair. + Reloc::Aarch64AdrGotPage21 => { + // Set the immediate value of an ADRP to bits [32:12] of X; check that –2^32 <= X < 2^32 + assert_eq!(addend, 0, "addend affects the address looked up in get_got_entry, which is currently only called with a symbol"); + let what = get_got_entry(name); + let what_page = (what as usize) & !0xfff; + let at_page = (at as usize) & !0xfff; + let pcrel = (what_page as isize).checked_sub(at_page as isize).unwrap(); + assert!( + (-1 << 32) <= pcrel && pcrel < (1 << 32), + "can't reach GOT page with ±4GB `adrp` instruction" + ); + let val = pcrel >> 12; + + let immlo = ((val as u32) & 0b11) << 29; + let immhi = (((val as u32) >> 2) & &0x7ffff) << 5; + let mask = !((0x7ffff << 5) | (0b11 << 29)); + unsafe { modify_inst32(at as *mut u32, |adrp| (adrp & mask) | immlo | immhi) }; + } + Reloc::Aarch64Ld64GotLo12Nc => { + // Set the LD/ST immediate field to bits 11:3 of X. No overflow check; check that X&7 = 0 + assert_eq!(addend, 0); + let base = get_got_entry(name); + let what = base as u32; + assert_eq!(what & 0b111, 0); + let val = what >> 3; + let imm9 = (val & 0x1ff) << 10; + let mask = !(0x1ff << 10); + unsafe { modify_inst32(at as *mut u32, |ldr| (ldr & mask) | imm9) }; + } + Reloc::RiscvCallPlt => { + // A R_RISCV_CALL_PLT relocation expects auipc+jalr instruction pair. // It is the equivalent of two relocations: // 1. R_RISCV_PCREL_HI20 on the `auipc` // 2. R_RISCV_PCREL_LO12_I on the `jalr` diff --git a/cranelift/jit/src/lib.rs b/cranelift/jit/src/lib.rs index 1565bb5bcf15..a7e4190cd12f 100644 --- a/cranelift/jit/src/lib.rs +++ b/cranelift/jit/src/lib.rs @@ -3,14 +3,7 @@ //! There is an [example project](https://github.com/bytecodealliance/cranelift-jit-demo/) //! which shows how to use some of the features of `cranelift_jit`. -#![deny( - missing_docs, - trivial_numeric_casts, - unused_extern_crates, - unstable_features, - unreachable_pub -)] -#![warn(unused_import_braces)] +#![deny(missing_docs, unreachable_pub)] mod backend; mod compiled_blob; diff --git a/cranelift/jit/src/memory.rs b/cranelift/jit/src/memory.rs index 6fa369b73294..abbb513c663f 100644 --- a/cranelift/jit/src/memory.rs +++ b/cranelift/jit/src/memory.rs @@ -5,7 +5,6 @@ use memmap2::MmapMut; #[cfg(not(any(feature = "selinux-fix", windows)))] use std::alloc; -use std::convert::TryFrom; use std::ffi::c_void; use std::io; use std::mem; @@ -133,6 +132,8 @@ pub(crate) struct Memory { branch_protection: BranchProtection, } +unsafe impl Send for Memory {} + impl Memory { pub(crate) fn new(branch_protection: BranchProtection) -> Self { Self { diff --git a/cranelift/jit/tests/basic.rs b/cranelift/jit/tests/basic.rs index a6e5a04641a7..6220782c7d9d 100644 --- a/cranelift/jit/tests/basic.rs +++ b/cranelift/jit/tests/basic.rs @@ -14,7 +14,7 @@ fn error_on_incompatible_sig_in_declare_function() { // FIXME set back to true once the x64 backend supports it. flag_builder.set("is_pic", "false").unwrap(); let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { - panic!("host machine is not supported: {}", msg); + panic!("host machine is not supported: {msg}"); }); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) @@ -70,7 +70,7 @@ fn panic_on_define_after_finalize() { // FIXME set back to true once the x64 backend supports it. flag_builder.set("is_pic", "false").unwrap(); let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { - panic!("host machine is not supported: {}", msg); + panic!("host machine is not supported: {msg}"); }); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) @@ -101,7 +101,7 @@ fn switch_error() { let bb1 = bcx.create_block(); let bb2 = bcx.create_block(); let bb3 = bcx.create_block(); - println!("{} {} {} {} {}", start, bb0, bb1, bb2, bb3); + println!("{start} {bb0} {bb1} {bb2} {bb3}"); bcx.declare_var(Variable::new(0), types::I32); bcx.declare_var(Variable::new(1), types::I32); @@ -149,7 +149,7 @@ fn switch_error() { Err(err) => { let pretty_error = cranelift_codegen::print_errors::pretty_verifier_error(&func, None, err); - panic!("pretty_error:\n{}", pretty_error); + panic!("pretty_error:\n{pretty_error}"); } } } @@ -161,7 +161,7 @@ fn libcall_function() { // FIXME set back to true once the x64 backend supports it. flag_builder.set("is_pic", "false").unwrap(); let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { - panic!("host machine is not supported: {}", msg); + panic!("host machine is not supported: {msg}"); }); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) @@ -213,3 +213,27 @@ fn libcall_function() { module.finalize_definitions().unwrap(); } + +// This used to cause UB. See https://github.com/bytecodealliance/wasmtime/issues/7918. +#[test] +fn empty_data_object() { + let mut flag_builder = settings::builder(); + flag_builder.set("use_colocated_libcalls", "false").unwrap(); + // FIXME set back to true once the x64 backend supports it. + flag_builder.set("is_pic", "false").unwrap(); + let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { + panic!("host machine is not supported: {msg}"); + }); + let isa = isa_builder + .finish(settings::Flags::new(flag_builder)) + .unwrap(); + let mut module = JITModule::new(JITBuilder::with_isa(isa, default_libcall_names())); + + let data_id = module + .declare_data("empty", Linkage::Export, false, false) + .unwrap(); + + let mut data = DataDescription::new(); + data.define(Box::new([])); + module.define_data(data_id, &data).unwrap(); +} diff --git a/cranelift/module/Cargo.toml b/cranelift/module/Cargo.toml index e89a1e20ed61..7cd0a4bf7a92 100644 --- a/cranelift/module/Cargo.toml +++ b/cranelift/module/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-module" -version = "0.101.0" +version = "0.112.0" authors = ["The Cranelift Project Developers"] description = "Support for linking functions and data with Cranelift" repository = "https://github.com/bytecodealliance/wasmtime" @@ -9,14 +9,18 @@ categories = ["no-std"] license = "Apache-2.0 WITH LLVM-exception" readme = "README.md" edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [dependencies] cranelift-codegen = { workspace = true } cranelift-control = { workspace = true } hashbrown = { workspace = true, optional = true } -anyhow = { workspace = true } -serde = { version = "1.0.188", optional = true } -serde_derive = { version = "1.0.188", optional = true } +anyhow = { workspace = true, features = ['std'] } +serde = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } [features] default = ["std"] diff --git a/cranelift/module/src/lib.rs b/cranelift/module/src/lib.rs index 88313c3a224b..7528ad67caad 100644 --- a/cranelift/module/src/lib.rs +++ b/cranelift/module/src/lib.rs @@ -1,8 +1,6 @@ //! Top-level lib.rs for `cranelift_module`. -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces)] -#![cfg_attr(feature = "std", deny(unstable_features))] +#![deny(missing_docs)] #![no_std] #[cfg(not(feature = "std"))] diff --git a/cranelift/module/src/module.rs b/cranelift/module/src/module.rs index cae49ef17e97..d9494b851ed2 100644 --- a/cranelift/module/src/module.rs +++ b/cranelift/module/src/module.rs @@ -11,13 +11,14 @@ use core::fmt::Display; use cranelift_codegen::binemit::{CodeOffset, Reloc}; use cranelift_codegen::entity::{entity_impl, PrimaryMap}; use cranelift_codegen::ir::function::{Function, VersionMarker}; -use cranelift_codegen::ir::{ExternalName, UserFuncName}; +use cranelift_codegen::ir::ExternalName; use cranelift_codegen::settings::SetError; use cranelift_codegen::{ ir, isa, CodegenError, CompileError, Context, FinalizedMachReloc, FinalizedRelocTarget, }; use cranelift_control::ControlPlane; use std::borrow::{Cow, ToOwned}; +use std::boxed::Box; use std::string::String; /// A module relocation. @@ -36,7 +37,11 @@ pub struct ModuleReloc { impl ModuleReloc { /// Converts a `FinalizedMachReloc` produced from a `Function` into a `ModuleReloc`. - pub fn from_mach_reloc(mach_reloc: &FinalizedMachReloc, func: &Function) -> Self { + pub fn from_mach_reloc( + mach_reloc: &FinalizedMachReloc, + func: &Function, + func_id: FuncId, + ) -> Self { let name = match mach_reloc.target { FinalizedRelocTarget::ExternalName(ExternalName::User(reff)) => { let name = &func.params.user_named_funcs()[reff]; @@ -50,7 +55,7 @@ impl ModuleReloc { ModuleRelocTarget::KnownSymbol(ks) } FinalizedRelocTarget::Func(offset) => { - ModuleRelocTarget::FunctionOffset(func.name.clone(), offset) + ModuleRelocTarget::FunctionOffset(func_id, offset) } }; Self { @@ -321,36 +326,34 @@ impl std::fmt::Display for ModuleError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Self::Undeclared(name) => { - write!(f, "Undeclared identifier: {}", name) + write!(f, "Undeclared identifier: {name}") } Self::IncompatibleDeclaration(name) => { - write!(f, "Incompatible declaration of identifier: {}", name,) + write!(f, "Incompatible declaration of identifier: {name}",) } Self::IncompatibleSignature(name, prev_sig, new_sig) => { write!( f, - "Function {} signature {:?} is incompatible with previous declaration {:?}", - name, new_sig, prev_sig, + "Function {name} signature {new_sig:?} is incompatible with previous declaration {prev_sig:?}", ) } Self::DuplicateDefinition(name) => { - write!(f, "Duplicate definition of identifier: {}", name) + write!(f, "Duplicate definition of identifier: {name}") } Self::InvalidImportDefinition(name) => { write!( f, - "Invalid to define identifier declared as an import: {}", - name, + "Invalid to define identifier declared as an import: {name}", ) } Self::Compilation(err) => { - write!(f, "Compilation error: {}", err) + write!(f, "Compilation error: {err}") } Self::Allocation { message, err } => { - write!(f, "Allocation error: {}: {}", message, err) + write!(f, "Allocation error: {message}: {err}") } - Self::Backend(err) => write!(f, "Backend error: {}", err), - Self::Flag(err) => write!(f, "Flag error: {}", err), + Self::Backend(err) => write!(f, "Backend error: {err}"), + Self::Flag(err) => write!(f, "Flag error: {err}"), } } } @@ -430,7 +433,7 @@ pub enum ModuleRelocTarget { /// Symbols known to the linker. KnownSymbol(ir::KnownSymbol), /// A offset inside a function - FunctionOffset(UserFuncName, CodeOffset), + FunctionOffset(FuncId, CodeOffset), } impl ModuleRelocTarget { @@ -443,9 +446,9 @@ impl ModuleRelocTarget { impl Display for ModuleRelocTarget { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::User { namespace, index } => write!(f, "u{}:{}", namespace, index), - Self::LibCall(lc) => write!(f, "%{}", lc), - Self::KnownSymbol(ks) => write!(f, "{}", ks), + Self::User { namespace, index } => write!(f, "u{namespace}:{index}"), + Self::LibCall(lc) => write!(f, "%{lc}"), + Self::KnownSymbol(ks) => write!(f, "{ks}"), Self::FunctionOffset(fname, offset) => write!(f, "{fname}+{offset}"), } } @@ -986,7 +989,112 @@ pub trait Module { fn define_data(&mut self, data_id: DataId, data: &DataDescription) -> ModuleResult<()>; } -impl Module for &mut M { +impl Module for &mut M { + fn isa(&self) -> &dyn isa::TargetIsa { + (**self).isa() + } + + fn declarations(&self) -> &ModuleDeclarations { + (**self).declarations() + } + + fn get_name(&self, name: &str) -> Option { + (**self).get_name(name) + } + + fn target_config(&self) -> isa::TargetFrontendConfig { + (**self).target_config() + } + + fn make_context(&self) -> Context { + (**self).make_context() + } + + fn clear_context(&self, ctx: &mut Context) { + (**self).clear_context(ctx) + } + + fn make_signature(&self) -> ir::Signature { + (**self).make_signature() + } + + fn clear_signature(&self, sig: &mut ir::Signature) { + (**self).clear_signature(sig) + } + + fn declare_function( + &mut self, + name: &str, + linkage: Linkage, + signature: &ir::Signature, + ) -> ModuleResult { + (**self).declare_function(name, linkage, signature) + } + + fn declare_anonymous_function(&mut self, signature: &ir::Signature) -> ModuleResult { + (**self).declare_anonymous_function(signature) + } + + fn declare_data( + &mut self, + name: &str, + linkage: Linkage, + writable: bool, + tls: bool, + ) -> ModuleResult { + (**self).declare_data(name, linkage, writable, tls) + } + + fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult { + (**self).declare_anonymous_data(writable, tls) + } + + fn declare_func_in_func(&mut self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef { + (**self).declare_func_in_func(func, in_func) + } + + fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue { + (**self).declare_data_in_func(data, func) + } + + fn declare_func_in_data(&self, func_id: FuncId, data: &mut DataDescription) -> ir::FuncRef { + (**self).declare_func_in_data(func_id, data) + } + + fn declare_data_in_data(&self, data_id: DataId, data: &mut DataDescription) -> ir::GlobalValue { + (**self).declare_data_in_data(data_id, data) + } + + fn define_function(&mut self, func: FuncId, ctx: &mut Context) -> ModuleResult<()> { + (**self).define_function(func, ctx) + } + + fn define_function_with_control_plane( + &mut self, + func: FuncId, + ctx: &mut Context, + ctrl_plane: &mut ControlPlane, + ) -> ModuleResult<()> { + (**self).define_function_with_control_plane(func, ctx, ctrl_plane) + } + + fn define_function_bytes( + &mut self, + func_id: FuncId, + func: &ir::Function, + alignment: u64, + bytes: &[u8], + relocs: &[FinalizedMachReloc], + ) -> ModuleResult<()> { + (**self).define_function_bytes(func_id, func, alignment, bytes, relocs) + } + + fn define_data(&mut self, data_id: DataId, data: &DataDescription) -> ModuleResult<()> { + (**self).define_data(data_id, data) + } +} + +impl Module for Box { fn isa(&self) -> &dyn isa::TargetIsa { (**self).isa() } diff --git a/cranelift/native/Cargo.toml b/cranelift/native/Cargo.toml index b9556a8c589d..599536d27517 100644 --- a/cranelift/native/Cargo.toml +++ b/cranelift/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-native" -version = "0.101.0" +version = "0.112.0" authors = ["The Cranelift Project Developers"] description = "Support for targeting the host with Cranelift" documentation = "https://docs.rs/cranelift-native" @@ -9,13 +9,14 @@ categories = ["no-std"] license = "Apache-2.0 WITH LLVM-exception" readme = "README.md" edition.workspace = true +rust-version.workspace = true [dependencies] cranelift-codegen = { workspace = true } target-lexicon = { workspace = true } [target.'cfg(any(target_arch = "s390x", target_arch = "riscv64"))'.dependencies] -libc = "0.2.95" +libc = { workspace = true } [features] default = ["std"] diff --git a/cranelift/native/src/lib.rs b/cranelift/native/src/lib.rs index 42d20a326191..a9d638b188ba 100644 --- a/cranelift/native/src/lib.rs +++ b/cranelift/native/src/lib.rs @@ -1,13 +1,7 @@ //! Performs autodetection of the host for the purposes of running //! Cranelift to generate code to run on the same machine. -#![deny( - missing_docs, - trivial_numeric_casts, - unused_extern_crates, - unstable_features -)] -#![warn(unused_import_braces)] +#![deny(missing_docs)] use cranelift_codegen::isa; use cranelift_codegen::settings::Configurable; @@ -28,7 +22,7 @@ pub fn builder() -> Result { /// in the current configuration. /// /// Selects the given backend variant specifically; this is -/// useful when more than oen backend exists for a given target +/// useful when more than one backend exists for a given target /// (e.g., on x86-64). pub fn builder_with_options(infer_native_flags: bool) -> Result { let mut isa_builder = isa::lookup(Triple::host()).map_err(|err| match err { @@ -46,7 +40,7 @@ pub fn builder_with_options(infer_native_flags: bool) -> Result Result<(), &'static str> { #[cfg(target_arch = "x86_64")] @@ -115,6 +109,10 @@ pub fn infer_native_flags(isa_builder: &mut dyn Configurable) -> Result<(), &'st isa_builder.enable("has_pauth").unwrap(); } + if std::arch::is_aarch64_feature_detected!("fp16") { + isa_builder.enable("has_fp16").unwrap(); + } + if cfg!(target_os = "macos") { // Pointer authentication is always available on Apple Silicon. isa_builder.enable("sign_return_address").unwrap(); @@ -150,9 +148,16 @@ pub fn infer_native_flags(isa_builder: &mut dyn Configurable) -> Result<(), &'st // the cpuinfo interface, so we can't rely on it being present for now. let _ = riscv::cpuinfo_detect(isa_builder); } + + // On all other architectures (e.g. wasm32) we won't infer any native flags, + // but still need to use the `isa_builder` to avoid compiler warnings. + let _ = isa_builder; Ok(()) } +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); + #[cfg(test)] mod tests { use super::builder; @@ -169,7 +174,7 @@ mod tests { if cfg!(all(target_os = "macos", target_arch = "aarch64")) { assert_eq!(isa.default_call_conv(), CallConv::AppleAarch64); - } else if cfg!(any(unix, target_os = "nebulet")) { + } else if cfg!(unix) { assert_eq!(isa.default_call_conv(), CallConv::SystemV); } else if cfg!(windows) { assert_eq!(isa.default_call_conv(), CallConv::WindowsFastcall); @@ -185,6 +190,3 @@ mod tests { } } } - -/// Version number of this crate. -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/cranelift/native/src/riscv.rs b/cranelift/native/src/riscv.rs index edcf306cf70a..4152816f7ab1 100644 --- a/cranelift/native/src/riscv.rs +++ b/cranelift/native/src/riscv.rs @@ -50,7 +50,7 @@ pub fn hwcap_detect(isa_builder: &mut dyn Configurable) -> Result<(), &'static s /// Read the /proc/cpuinfo file and detect the extensions. /// /// We are looking for the isa line string, which contains the extensions. -/// The format for this string is specifid in the linux user space ABI for RISC-V: +/// The format for this string is specified in the linux user space ABI for RISC-V: /// https://github.com/torvalds/linux/blob/09a9639e56c01c7a00d6c0ca63f4c7c41abe075d/Documentation/riscv/uabi.rst /// /// The format is fairly similar to the one specified in the RISC-V ISA manual, but diff --git a/cranelift/object/Cargo.toml b/cranelift/object/Cargo.toml index 5b3260dc950f..bbacba0d3122 100644 --- a/cranelift/object/Cargo.toml +++ b/cranelift/object/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-object" -version = "0.101.0" +version = "0.112.0" authors = ["The Cranelift Project Developers"] description = "Emit Cranelift output to native object files with `object`" repository = "https://github.com/bytecodealliance/wasmtime" @@ -8,12 +8,16 @@ documentation = "https://docs.rs/cranelift-object" license = "Apache-2.0 WITH LLVM-exception" readme = "README.md" edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [dependencies] cranelift-module = { workspace = true } cranelift-codegen = { workspace = true, features = ["std"] } cranelift-control = { workspace = true } -object = { workspace = true, features = ["write"] } +object = { workspace = true, features = ["write", "std"] } target-lexicon = { workspace = true } anyhow = { workspace = true } log = { workspace = true } diff --git a/cranelift/object/src/backend.rs b/cranelift/object/src/backend.rs index d083d84ce12f..2d48e9b613f1 100644 --- a/cranelift/object/src/backend.rs +++ b/cranelift/object/src/backend.rs @@ -3,9 +3,8 @@ use anyhow::anyhow; use cranelift_codegen::binemit::{Addend, CodeOffset, Reloc}; use cranelift_codegen::entity::SecondaryMap; -use cranelift_codegen::ir::UserFuncName; use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; -use cranelift_codegen::{self, ir, FinalizedMachReloc}; +use cranelift_codegen::{ir, FinalizedMachReloc}; use cranelift_control::ControlPlane; use cranelift_module::{ DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleDeclarations, ModuleError, @@ -16,7 +15,8 @@ use object::write::{ Object, Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection, }; use object::{ - RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, SymbolScope, + RelocationEncoding, RelocationFlags, RelocationKind, SectionKind, SymbolFlags, SymbolKind, + SymbolScope, }; use std::collections::hash_map::Entry; use std::collections::HashMap; @@ -39,10 +39,10 @@ impl ObjectBuilder { /// Create a new `ObjectBuilder` using the given Cranelift target, that /// can be passed to [`ObjectModule::new`]. /// - /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s [ir::LibCall] + /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s [`ir::LibCall`] /// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain /// floating point instructions, and for stack probes. If you don't know what to use for this - /// argument, use [cranelift_module::default_libcall_names](). + /// argument, use [`cranelift_module::default_libcall_names`]. pub fn new>>( isa: OwnedTargetIsa, name: V, @@ -80,11 +80,24 @@ impl ObjectBuilder { binary_format, ))); } - // FIXME(#4994) get the right variant from the TargetIsa + + // FIXME(#4994): Get the right float ABI variant from the TargetIsa + let mut eflags = object::elf::EF_RISCV_FLOAT_ABI_DOUBLE; + + // Set the RVC eflag if we have the C extension enabled. + let has_c = isa + .isa_flags() + .iter() + .filter(|f| f.name == "has_zca" || f.name == "has_zcd") + .all(|f| f.as_bool().unwrap_or_default()); + if has_c { + eflags |= object::elf::EF_RISCV_RVC; + } + file_flags = object::FileFlags::Elf { os_abi: object::elf::ELFOSABI_NONE, abi_version: 0, - e_flags: object::elf::EF_RISCV_RVC | object::elf::EF_RISCV_FLOAT_ABI_DOUBLE, + e_flags: eflags, }; object::Architecture::Riscv64 } @@ -132,7 +145,7 @@ pub struct ObjectModule { libcalls: HashMap, libcall_names: Box String + Send + Sync>, known_symbols: HashMap, - known_labels: HashMap<(UserFuncName, CodeOffset), SymbolId>, + known_labels: HashMap<(FuncId, CodeOffset), SymbolId>, per_function_section: bool, } @@ -141,6 +154,7 @@ impl ObjectModule { pub fn new(builder: ObjectBuilder) -> Self { let mut object = Object::new(builder.binary_format, builder.architecture, builder.endian); object.flags = builder.flags; + object.set_subsections_via_symbols(); object.add_file_symbol(builder.name); Self { isa: builder.isa, @@ -355,24 +369,21 @@ impl Module for ObjectModule { let align = alignment .max(self.isa.function_alignment().minimum.into()) .max(self.isa.symbol_alignment()); - let (section, offset) = if self.per_function_section { + let section = if self.per_function_section { let symbol_name = self.object.symbol(symbol).name.clone(); - let (section, offset) = - self.object - .add_subsection(StandardSection::Text, &symbol_name, bytes, align); - self.object.symbol_mut(symbol).section = SymbolSection::Section(section); - self.object.symbol_mut(symbol).value = offset; - (section, offset) + self.object + .add_subsection(StandardSection::Text, &symbol_name) } else { - let section = self.object.section_id(StandardSection::Text); - let offset = self.object.add_symbol_data(symbol, section, bytes, align); - (section, offset) + self.object.section_id(StandardSection::Text) }; + let offset = self.object.add_symbol_data(symbol, section, bytes, align); if !relocs.is_empty() { let relocs = relocs .iter() - .map(|record| self.process_reloc(&ModuleReloc::from_mach_reloc(&record, func))) + .map(|record| { + self.process_reloc(&ModuleReloc::from_mach_reloc(&record, func, func_id)) + }) .collect(); self.relocs.push(SymbolRelocs { section, @@ -488,9 +499,7 @@ impl ObjectModule { for &ObjectRelocRecord { offset, ref name, - kind, - encoding, - size, + flags, addend, } in &symbol.relocs { @@ -500,9 +509,7 @@ impl ObjectModule { symbol.section, Relocation { offset: symbol.offset + u64::from(offset), - size, - kind, - encoding, + flags, symbol: target_symbol, addend, }, @@ -594,15 +601,10 @@ impl ObjectModule { } } - ModuleRelocTarget::FunctionOffset(ref fname, offset) => { - match self.known_labels.entry((fname.clone(), offset)) { + ModuleRelocTarget::FunctionOffset(func_id, offset) => { + match self.known_labels.entry((func_id, offset)) { Entry::Occupied(o) => *o.get(), Entry::Vacant(v) => { - let func_user_name = fname.get_user().unwrap(); - let func_id = FuncId::from_name(&ModuleRelocTarget::user( - func_user_name.namespace, - func_user_name.index, - )); let func_symbol_id = self.functions[func_id].unwrap().0; let func_symbol = self.object.symbol(func_symbol_id); @@ -627,41 +629,58 @@ impl ObjectModule { } fn process_reloc(&self, record: &ModuleReloc) -> ObjectRelocRecord { - let mut addend = record.addend; - let (kind, encoding, size) = match record.kind { - Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), - Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), - Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32), - Reloc::X86CallPCRel4 => (RelocationKind::Relative, RelocationEncoding::X86Branch, 32), + let flags = match record.kind { + Reloc::Abs4 => RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 32, + }, + Reloc::Abs8 => RelocationFlags::Generic { + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + size: 64, + }, + Reloc::X86PCRel4 => RelocationFlags::Generic { + kind: RelocationKind::Relative, + encoding: RelocationEncoding::Generic, + size: 32, + }, + Reloc::X86CallPCRel4 => RelocationFlags::Generic { + kind: RelocationKind::Relative, + encoding: RelocationEncoding::X86Branch, + size: 32, + }, // TODO: Get Cranelift to tell us when we can use // R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX. - Reloc::X86CallPLTRel4 => ( - RelocationKind::PltRelative, - RelocationEncoding::X86Branch, - 32, - ), - Reloc::X86SecRel => ( - RelocationKind::SectionOffset, - RelocationEncoding::Generic, - 32, - ), - Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32), - Reloc::Arm64Call => ( - RelocationKind::Relative, - RelocationEncoding::AArch64Call, - 26, - ), + Reloc::X86CallPLTRel4 => RelocationFlags::Generic { + kind: RelocationKind::PltRelative, + encoding: RelocationEncoding::X86Branch, + size: 32, + }, + Reloc::X86SecRel => RelocationFlags::Generic { + kind: RelocationKind::SectionOffset, + encoding: RelocationEncoding::Generic, + size: 32, + }, + Reloc::X86GOTPCRel4 => RelocationFlags::Generic { + kind: RelocationKind::GotRelative, + encoding: RelocationEncoding::Generic, + size: 32, + }, + Reloc::Arm64Call => RelocationFlags::Generic { + kind: RelocationKind::Relative, + encoding: RelocationEncoding::AArch64Call, + size: 26, + }, Reloc::ElfX86_64TlsGd => { assert_eq!( self.object.format(), object::BinaryFormat::Elf, "ElfX86_64TlsGd is not supported for this file format" ); - ( - RelocationKind::Elf(object::elf::R_X86_64_TLSGD), - RelocationEncoding::Generic, - 32, - ) + RelocationFlags::Elf { + r_type: object::elf::R_X86_64_TLSGD, + } } Reloc::MachOX86_64Tlv => { assert_eq!( @@ -669,15 +688,11 @@ impl ObjectModule { object::BinaryFormat::MachO, "MachOX86_64Tlv is not supported for this file format" ); - addend += 4; // X86_64_RELOC_TLV has an implicit addend of -4 - ( - RelocationKind::MachO { - value: object::macho::X86_64_RELOC_TLV, - relative: true, - }, - RelocationEncoding::Generic, - 32, - ) + RelocationFlags::MachO { + r_type: object::macho::X86_64_RELOC_TLV, + r_pcrel: true, + r_length: 2, + } } Reloc::MachOAarch64TlsAdrPage21 => { assert_eq!( @@ -685,14 +700,11 @@ impl ObjectModule { object::BinaryFormat::MachO, "MachOAarch64TlsAdrPage21 is not supported for this file format" ); - ( - RelocationKind::MachO { - value: object::macho::ARM64_RELOC_TLVP_LOAD_PAGE21, - relative: true, - }, - RelocationEncoding::Generic, - 21, - ) + RelocationFlags::MachO { + r_type: object::macho::ARM64_RELOC_TLVP_LOAD_PAGE21, + r_pcrel: true, + r_length: 2, + } } Reloc::MachOAarch64TlsAdrPageOff12 => { assert_eq!( @@ -700,88 +712,94 @@ impl ObjectModule { object::BinaryFormat::MachO, "MachOAarch64TlsAdrPageOff12 is not supported for this file format" ); - ( - RelocationKind::MachO { - value: object::macho::ARM64_RELOC_TLVP_LOAD_PAGEOFF12, - relative: false, - }, - RelocationEncoding::Generic, - 12, - ) + RelocationFlags::MachO { + r_type: object::macho::ARM64_RELOC_TLVP_LOAD_PAGEOFF12, + r_pcrel: false, + r_length: 2, + } + } + Reloc::Aarch64TlsDescAdrPage21 => { + assert_eq!( + self.object.format(), + object::BinaryFormat::Elf, + "Aarch64TlsDescAdrPage21 is not supported for this file format" + ); + RelocationFlags::Elf { + r_type: object::elf::R_AARCH64_TLSDESC_ADR_PAGE21, + } + } + Reloc::Aarch64TlsDescLd64Lo12 => { + assert_eq!( + self.object.format(), + object::BinaryFormat::Elf, + "Aarch64TlsDescLd64Lo12 is not supported for this file format" + ); + RelocationFlags::Elf { + r_type: object::elf::R_AARCH64_TLSDESC_LD64_LO12, + } } - Reloc::Aarch64TlsGdAdrPage21 => { + Reloc::Aarch64TlsDescAddLo12 => { assert_eq!( self.object.format(), object::BinaryFormat::Elf, - "Aarch64TlsGdAdrPrel21 is not supported for this file format" + "Aarch64TlsDescAddLo12 is not supported for this file format" ); - ( - RelocationKind::Elf(object::elf::R_AARCH64_TLSGD_ADR_PAGE21), - RelocationEncoding::Generic, - 21, - ) + RelocationFlags::Elf { + r_type: object::elf::R_AARCH64_TLSDESC_ADD_LO12, + } } - Reloc::Aarch64TlsGdAddLo12Nc => { + Reloc::Aarch64TlsDescCall => { assert_eq!( self.object.format(), object::BinaryFormat::Elf, - "Aarch64TlsGdAddLo12Nc is not supported for this file format" + "Aarch64TlsDescCall is not supported for this file format" ); - ( - RelocationKind::Elf(object::elf::R_AARCH64_TLSGD_ADD_LO12_NC), - RelocationEncoding::Generic, - 12, - ) + RelocationFlags::Elf { + r_type: object::elf::R_AARCH64_TLSDESC_CALL, + } } + Reloc::Aarch64AdrGotPage21 => match self.object.format() { - object::BinaryFormat::Elf => ( - RelocationKind::Elf(object::elf::R_AARCH64_ADR_GOT_PAGE), - RelocationEncoding::Generic, - 21, - ), - object::BinaryFormat::MachO => ( - RelocationKind::MachO { - value: object::macho::ARM64_RELOC_GOT_LOAD_PAGE21, - relative: true, - }, - RelocationEncoding::Generic, - 21, - ), + object::BinaryFormat::Elf => RelocationFlags::Elf { + r_type: object::elf::R_AARCH64_ADR_GOT_PAGE, + }, + object::BinaryFormat::MachO => RelocationFlags::MachO { + r_type: object::macho::ARM64_RELOC_GOT_LOAD_PAGE21, + r_pcrel: true, + r_length: 2, + }, _ => unimplemented!("Aarch64AdrGotPage21 is not supported for this file format"), }, Reloc::Aarch64Ld64GotLo12Nc => match self.object.format() { - object::BinaryFormat::Elf => ( - RelocationKind::Elf(object::elf::R_AARCH64_LD64_GOT_LO12_NC), - RelocationEncoding::Generic, - 12, - ), - object::BinaryFormat::MachO => ( - RelocationKind::MachO { - value: object::macho::ARM64_RELOC_GOT_LOAD_PAGEOFF12, - relative: false, - }, - RelocationEncoding::Generic, - 12, - ), + object::BinaryFormat::Elf => RelocationFlags::Elf { + r_type: object::elf::R_AARCH64_LD64_GOT_LO12_NC, + }, + object::BinaryFormat::MachO => RelocationFlags::MachO { + r_type: object::macho::ARM64_RELOC_GOT_LOAD_PAGEOFF12, + r_pcrel: false, + r_length: 2, + }, _ => unimplemented!("Aarch64Ld64GotLo12Nc is not supported for this file format"), }, - Reloc::S390xPCRel32Dbl => (RelocationKind::Relative, RelocationEncoding::S390xDbl, 32), - Reloc::S390xPLTRel32Dbl => ( - RelocationKind::PltRelative, - RelocationEncoding::S390xDbl, - 32, - ), + Reloc::S390xPCRel32Dbl => RelocationFlags::Generic { + kind: RelocationKind::Relative, + encoding: RelocationEncoding::S390xDbl, + size: 32, + }, + Reloc::S390xPLTRel32Dbl => RelocationFlags::Generic { + kind: RelocationKind::PltRelative, + encoding: RelocationEncoding::S390xDbl, + size: 32, + }, Reloc::S390xTlsGd64 => { assert_eq!( self.object.format(), object::BinaryFormat::Elf, "S390xTlsGd64 is not supported for this file format" ); - ( - RelocationKind::Elf(object::elf::R_390_TLS_GD64), - RelocationEncoding::Generic, - 64, - ) + RelocationFlags::Elf { + r_type: object::elf::R_390_TLS_GD64, + } } Reloc::S390xTlsGdCall => { assert_eq!( @@ -789,23 +807,19 @@ impl ObjectModule { object::BinaryFormat::Elf, "S390xTlsGdCall is not supported for this file format" ); - ( - RelocationKind::Elf(object::elf::R_390_TLS_GDCALL), - RelocationEncoding::Generic, - 0, - ) + RelocationFlags::Elf { + r_type: object::elf::R_390_TLS_GDCALL, + } } - Reloc::RiscvCall => { + Reloc::RiscvCallPlt => { assert_eq!( self.object.format(), object::BinaryFormat::Elf, - "RiscvCall is not supported for this file format" + "RiscvCallPlt is not supported for this file format" ); - ( - RelocationKind::Elf(object::elf::R_RISCV_CALL), - RelocationEncoding::Generic, - 0, - ) + RelocationFlags::Elf { + r_type: object::elf::R_RISCV_CALL_PLT, + } } Reloc::RiscvTlsGdHi20 => { assert_eq!( @@ -813,11 +827,9 @@ impl ObjectModule { object::BinaryFormat::Elf, "RiscvTlsGdHi20 is not supported for this file format" ); - ( - RelocationKind::Elf(object::elf::R_RISCV_TLS_GD_HI20), - RelocationEncoding::Generic, - 0, - ) + RelocationFlags::Elf { + r_type: object::elf::R_RISCV_TLS_GD_HI20, + } } Reloc::RiscvPCRelLo12I => { assert_eq!( @@ -825,11 +837,19 @@ impl ObjectModule { object::BinaryFormat::Elf, "RiscvPCRelLo12I is not supported for this file format" ); - ( - RelocationKind::Elf(object::elf::R_RISCV_PCREL_LO12_I), - RelocationEncoding::Generic, - 0, - ) + RelocationFlags::Elf { + r_type: object::elf::R_RISCV_PCREL_LO12_I, + } + } + Reloc::RiscvGotHi20 => { + assert_eq!( + self.object.format(), + object::BinaryFormat::Elf, + "RiscvGotHi20 is not supported for this file format" + ); + RelocationFlags::Elf { + r_type: object::elf::R_RISCV_GOT_HI20, + } } // FIXME reloc => unimplemented!("{:?}", reloc), @@ -838,10 +858,8 @@ impl ObjectModule { ObjectRelocRecord { offset: record.offset, name: record.name.clone(), - kind, - encoding, - size, - addend, + flags, + addend: record.addend, } } } @@ -902,8 +920,6 @@ struct SymbolRelocs { struct ObjectRelocRecord { offset: CodeOffset, name: ModuleRelocTarget, - kind: RelocationKind, - encoding: RelocationEncoding, - size: u8, + flags: RelocationFlags, addend: Addend, } diff --git a/cranelift/object/src/lib.rs b/cranelift/object/src/lib.rs index dd934af63765..6967f890df4f 100644 --- a/cranelift/object/src/lib.rs +++ b/cranelift/object/src/lib.rs @@ -2,13 +2,7 @@ //! //! This re-exports `object` so you don't have to explicitly keep the versions in sync. -#![deny( - missing_docs, - trivial_numeric_casts, - unused_extern_crates, - unstable_features -)] -#![warn(unused_import_braces)] +#![deny(missing_docs)] mod backend; diff --git a/cranelift/object/tests/basic.rs b/cranelift/object/tests/basic.rs index d918f55e343d..51b500dc8d3e 100644 --- a/cranelift/object/tests/basic.rs +++ b/cranelift/object/tests/basic.rs @@ -91,7 +91,7 @@ fn switch_error() { let bb1 = bcx.create_block(); let bb2 = bcx.create_block(); let bb3 = bcx.create_block(); - println!("{} {} {} {} {}", start, bb0, bb1, bb2, bb3); + println!("{start} {bb0} {bb1} {bb2} {bb3}"); bcx.declare_var(Variable::new(0), types::I32); bcx.declare_var(Variable::new(1), types::I32); @@ -139,7 +139,7 @@ fn switch_error() { Err(err) => { let pretty_error = cranelift_codegen::print_errors::pretty_verifier_error(&func, None, err); - panic!("pretty_error:\n{}", pretty_error); + panic!("pretty_error:\n{pretty_error}"); } } } diff --git a/cranelift/reader/Cargo.toml b/cranelift/reader/Cargo.toml index 36f77d6cb7fb..29579bc8aa72 100644 --- a/cranelift/reader/Cargo.toml +++ b/cranelift/reader/Cargo.toml @@ -1,16 +1,20 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-reader" -version = "0.101.0" +version = "0.112.0" description = "Cranelift textual IR reader" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-reader" repository = "https://github.com/bytecodealliance/wasmtime" readme = "README.md" edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [dependencies] -anyhow.workspace = true +anyhow = { workspace = true, features = ['std'] } cranelift-codegen = { workspace = true } smallvec = { workspace = true } -target-lexicon = { workspace = true } +target-lexicon = { workspace = true, features = ['std'] } diff --git a/cranelift/reader/src/isaspec.rs b/cranelift/reader/src/isaspec.rs index c27743a382c5..4fe7d27ffb9d 100644 --- a/cranelift/reader/src/isaspec.rs +++ b/cranelift/reader/src/isaspec.rs @@ -64,12 +64,12 @@ impl From for ParseError { ParseOptionError::Generic(err) => err, ParseOptionError::UnknownFlag { loc, name } => Self { location: loc, - message: format!("unknown setting '{}'", name), + message: format!("unknown setting '{name}'"), is_warning: false, }, ParseOptionError::UnknownValue { loc, name, value } => Self { location: loc, - message: format!("unknown setting '{}={}'", name, value), + message: format!("unknown setting '{name}={value}'"), is_warning: false, }, } diff --git a/cranelift/reader/src/lexer.rs b/cranelift/reader/src/lexer.rs index e2c80cf93db9..28d75feb5137 100644 --- a/cranelift/reader/src/lexer.rs +++ b/cranelift/reader/src/lexer.rs @@ -28,11 +28,12 @@ pub enum Token<'a> { Dot, // '.' Colon, // ':' Equal, // '=' - Not, // '!' + Bang, // '!' + At, // '@' Arrow, // '->' Float(&'a str), // Floating point immediate Integer(&'a str), // Integer immediate - Type(types::Type), // i32, f32, b32x4, ... + Type(types::Type), // i32, f32, i32x4, ... DynamicType(u32), // dt5 Value(Value), // v12, v7 Block(Block), // block3 @@ -40,7 +41,7 @@ pub enum Token<'a> { StackSlot(u32), // ss3 DynamicStackSlot(u32), // dss4 GlobalValue(u32), // gv3 - Table(u32), // table2 + MemoryType(u32), // mt0 Constant(u32), // const2 FuncRef(u32), // fn2 SigRef(u32), // sig2 @@ -344,7 +345,7 @@ impl<'a> Lexer<'a> { "dss" => Some(Token::DynamicStackSlot(number)), "dt" => Some(Token::DynamicType(number)), "gv" => Some(Token::GlobalValue(number)), - "table" => Some(Token::Table(number)), + "mt" => Some(Token::MemoryType(number)), "const" => Some(Token::Constant(number)), "fn" => Some(Token::FuncRef(number)), "sig" => Some(Token::SigRef(number)), @@ -368,10 +369,10 @@ impl<'a> Lexer<'a> { "i32" => types::I32, "i64" => types::I64, "i128" => types::I128, + "f16" => types::F16, "f32" => types::F32, "f64" => types::F64, - "r32" => types::R32, - "r64" => types::R64, + "f128" => types::F128, _ => return None, }; if is_vector { @@ -439,12 +440,17 @@ impl<'a> Lexer<'a> { token(Token::HexSequence(&self.source[begin..end]), loc) } - fn scan_srcloc(&mut self) -> Result, LocatedError> { - let loc = self.loc(); - let begin = self.pos + 1; - - assert_eq!(self.lookahead, Some('@')); + /// Given that we've consumed an `@` character, are we looking at a source + /// location? + fn looking_at_srcloc(&self) -> bool { + match self.lookahead { + Some(c) => char::is_digit(c, 16), + _ => false, + } + } + fn scan_srcloc(&mut self, pos: usize, loc: Location) -> Result, LocatedError> { + let begin = pos + 1; while let Some(c) = self.next_ch() { if !char::is_digit(c, 16) { break; @@ -474,7 +480,7 @@ impl<'a> Lexer<'a> { Some('.') => Some(self.scan_char(Token::Dot)), Some(':') => Some(self.scan_char(Token::Colon)), Some('=') => Some(self.scan_char(Token::Equal)), - Some('!') => Some(self.scan_char(Token::Not)), + Some('!') => Some(self.scan_char(Token::Bang)), Some('+') => Some(self.scan_number()), Some('*') => Some(self.scan_char(Token::Multiply)), Some('-') => { @@ -495,7 +501,16 @@ impl<'a> Lexer<'a> { Some('%') => Some(self.scan_name()), Some('"') => Some(self.scan_string()), Some('#') => Some(self.scan_hex_sequence()), - Some('@') => Some(self.scan_srcloc()), + Some('@') => { + let pos = self.pos; + let loc = self.loc(); + self.next_ch(); + if self.looking_at_srcloc() { + Some(self.scan_srcloc(pos, loc)) + } else { + Some(token(Token::At, loc)) + } + } // all ascii whitespace Some(' ') | Some('\x09'..='\x0d') => { self.next_ch(); @@ -513,11 +528,7 @@ impl<'a> Lexer<'a> { #[cfg(test)] mod tests { - use super::trailing_digits; use super::*; - use crate::error::Location; - use cranelift_codegen::ir::types; - use cranelift_codegen::ir::{Block, Value}; #[test] fn digits() { @@ -615,7 +626,7 @@ mod tests { fn lex_identifiers() { let mut lex = Lexer::new( "v0 v00 vx01 block1234567890 block5234567890 v1x vx1 vxvx4 \ - function0 function i8 i32x4 f32x5", + function0 function i8 i32x4 f32x5 f16 f128", ); assert_eq!( lex.next(), @@ -636,6 +647,8 @@ mod tests { assert_eq!(lex.next(), token(Token::Type(types::I8), 1)); assert_eq!(lex.next(), token(Token::Type(types::I32X4), 1)); assert_eq!(lex.next(), token(Token::Identifier("f32x5"), 1)); + assert_eq!(lex.next(), token(Token::Type(types::F16), 1)); + assert_eq!(lex.next(), token(Token::Type(types::F128), 1)); assert_eq!(lex.next(), None); } diff --git a/cranelift/reader/src/lib.rs b/cranelift/reader/src/lib.rs index c54435d80044..2ac7570eb269 100644 --- a/cranelift/reader/src/lib.rs +++ b/cranelift/reader/src/lib.rs @@ -3,13 +3,7 @@ //! The `cranelift_reader` library supports reading .clif files. This functionality is needed for //! testing Cranelift, but is not essential for a JIT compiler. -#![deny( - missing_docs, - trivial_numeric_casts, - unused_extern_crates, - unstable_features -)] -#![warn(unused_import_braces)] +#![deny(missing_docs)] pub use crate::error::{Location, ParseError, ParseResult}; pub use crate::isaspec::{parse_option, parse_options, IsaSpec, ParseOptionError}; @@ -64,7 +58,7 @@ pub fn parse_sets_and_triple(flag_set: &[String], flag_triple: &str) -> Result { - unknown_settings.push(format!("{}={}", name, value)); + unknown_settings.push(format!("{name}={value}")); } Err(ParseOptionError::Generic(err)) => return Err(err.into()), Ok(()) => {} diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 28b6ae18600c..a4160305a726 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -9,18 +9,20 @@ 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}; -use cranelift_codegen::ir::immediates::{Ieee32, Ieee64, Imm64, Offset32, Uimm32, Uimm64}; +use cranelift_codegen::ir::entities::{AnyEntity, DynamicType, MemoryType}; +use cranelift_codegen::ir::immediates::{ + Ieee128, Ieee16, Ieee32, Ieee64, Imm64, Offset32, Uimm32, Uimm64, +}; use cranelift_codegen::ir::instructions::{InstructionData, InstructionFormat, VariableArgs}; +use cranelift_codegen::ir::pcc::{BaseExpr, Expr, Fact}; use cranelift_codegen::ir::types; -use cranelift_codegen::ir::types::INVALID; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{self, UserExternalNameRef}; use cranelift_codegen::ir::{ AbiParam, ArgumentExtension, ArgumentPurpose, Block, Constant, ConstantData, DynamicStackSlot, DynamicStackSlotData, DynamicTypeData, ExtFuncData, ExternalName, FuncRef, Function, - GlobalValue, GlobalValueData, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, - StackSlotData, StackSlotKind, Table, TableData, Type, UserFuncName, Value, + GlobalValue, GlobalValueData, JumpTableData, MemFlags, MemoryTypeData, MemoryTypeField, Opcode, + SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, UserFuncName, Value, }; use cranelift_codegen::isa::{self, CallConv}; use cranelift_codegen::packed_option::ReservedValue; @@ -179,7 +181,7 @@ pub fn parse_test<'a>(text: &'a str, options: ParseOptions<'a>) -> ParseResult(text: &str, signature: &Signature) -> ParseResult> { +pub fn parse_run_command(text: &str, signature: &Signature) -> ParseResult> { let _tt = timing::parse_text(); // We remove leading spaces and semi-colons for convenience here instead of at the call sites // since this function will be attempting to parse a RunCommand from a CLIF comment. @@ -245,8 +247,11 @@ impl Context { fn add_ss(&mut self, ss: StackSlot, data: StackSlotData, loc: Location) -> ParseResult<()> { self.map.def_ss(ss, loc)?; while self.function.sized_stack_slots.next_key().index() <= ss.index() { - self.function - .create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 0)); + self.function.create_sized_stack_slot(StackSlotData::new( + StackSlotKind::ExplicitSlot, + 0, + 0, + )); } self.function.sized_stack_slots[ss] = data; Ok(()) @@ -303,7 +308,13 @@ impl Context { } // Allocate a global value slot. - fn add_gv(&mut self, gv: GlobalValue, data: GlobalValueData, loc: Location) -> ParseResult<()> { + fn add_gv( + &mut self, + gv: GlobalValue, + data: GlobalValueData, + maybe_fact: Option, + 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 { @@ -314,6 +325,19 @@ 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(()) } @@ -326,30 +350,6 @@ impl Context { } } - // Allocate a table slot. - fn add_table(&mut self, table: Table, data: TableData, loc: Location) -> ParseResult<()> { - while self.function.tables.next_key().index() <= table.index() { - self.function.create_table(TableData { - base_gv: GlobalValue::reserved_value(), - min_size: Uimm64::new(0), - bound_gv: GlobalValue::reserved_value(), - element_size: Uimm64::new(0), - index_type: INVALID, - }); - } - self.function.tables[table] = data; - self.map.def_table(table, loc) - } - - // Resolve a reference to a table. - fn check_table(&self, table: Table, loc: Location) -> ParseResult<()> { - if !self.map.contains_table(table) { - err!(loc, "undefined table {}", table) - } else { - Ok(()) - } - } - // Allocate a new signature. fn add_sig( &mut self, @@ -644,12 +644,12 @@ impl<'a> Parser<'a> { err!(self.loc, err_msg) } - // Match and consume a table reference. - fn match_table(&mut self, err_msg: &str) -> ParseResult { - if let Some(Token::Table(table)) = self.token() { + // 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(table) = Table::with_number(table) { - return Ok(table); + if let Some(mt) = MemoryType::with_number(mt) { + return Ok(mt); } } err!(self.loc, err_msg) @@ -721,8 +721,7 @@ impl<'a> Parser<'a> { self.consume(); text.parse().map_err(|e| { self.error(&format!( - "expected hexadecimal immediate, failed to parse: {}", - e + "expected hexadecimal immediate, failed to parse: {e}" )) }) } else { @@ -751,8 +750,7 @@ impl<'a> Parser<'a> { Ok(constant_data) } else { Err(self.error(&format!( - "expected parsed constant to have {} bytes", - expected_size + "expected parsed constant to have {expected_size} bytes" ))) } } @@ -788,9 +786,9 @@ impl<'a> Parser<'a> { if let Some(Token::Integer(text)) = self.token() { self.consume(); // Lexer just gives us raw text that looks like an integer. - if text.starts_with("0x") { + if let Some(num) = text.strip_prefix("0x") { // Parse it as a u8 in hexadecimal form. - u8::from_str_radix(&text[2..], 16) + u8::from_str_radix(num, 16) .map_err(|_| self.error("unable to parse u8 as a hexadecimal immediate")) } else { // Parse it as a u8 to check for overflow and other issues. @@ -857,6 +855,18 @@ impl<'a> Parser<'a> { Ok(Imm64::new(0)) } + // Match and consume an Ieee16 immediate. + fn match_ieee16(&mut self, err_msg: &str) -> ParseResult { + if let Some(Token::Float(text)) = self.token() { + self.consume(); + // Lexer just gives us raw text that looks like a float. + // Parse it as an Ieee16 to check for the right number of digits and other issues. + text.parse().map_err(|e| self.error(e)) + } else { + err!(self.loc, err_msg) + } + } + // Match and consume an Ieee32 immediate. fn match_ieee32(&mut self, err_msg: &str) -> ParseResult { if let Some(Token::Float(text)) = self.token() { @@ -881,6 +891,18 @@ impl<'a> Parser<'a> { } } + // Match and consume an Ieee128 immediate. + fn match_ieee128(&mut self, err_msg: &str) -> ParseResult { + if let Some(Token::Float(text)) = self.token() { + self.consume(); + // Lexer just gives us raw text that looks like a float. + // Parse it as an Ieee128 to check for the right number of digits and other issues. + text.parse().map_err(|e| self.error(e)) + } else { + err!(self.loc, err_msg) + } + } + // Match and consume an enumerated immediate, like one of the condition codes. fn match_enum(&mut self, err_msg: &str) -> ParseResult { if let Some(Token::Identifier(text)) = self.token() { @@ -892,16 +914,18 @@ impl<'a> Parser<'a> { } // Match and a consume a possibly empty sequence of memory operation flags. - fn optional_memflags(&mut self) -> MemFlags { + fn optional_memflags(&mut self) -> ParseResult { let mut flags = MemFlags::new(); while let Some(Token::Identifier(text)) = self.token() { - if flags.set_by_name(text) { - self.consume(); - } else { - break; + match flags.set_by_name(text) { + Ok(true) => { + self.consume(); + } + Ok(false) => break, + Err(msg) => return err!(self.loc, msg), } } - flags + Ok(flags) } // Match and consume an identifier. @@ -1013,7 +1037,7 @@ impl<'a> Parser<'a> { .finish(settings::Flags::new(flag_builder.clone())) .map_err(|e| ParseError { location: loc, - message: format!("invalid ISA flags for '{}': {:?}", targ, e), + message: format!("invalid ISA flags for '{targ}': {e:?}"), is_warning: false, })?, ); @@ -1105,10 +1129,7 @@ impl<'a> Parser<'a> { .finish(settings::Flags::new(flag_builder.clone())) .map_err(|e| ParseError { location: loc, - message: format!( - "invalid ISA flags for '{}': {:?}", - target_name, e - ), + message: format!("invalid ISA flags for '{target_name}': {e:?}"), is_warning: false, })?, ); @@ -1135,7 +1156,7 @@ impl<'a> Parser<'a> { let mut list = Vec::new(); while self.token() == Some(Token::Identifier("feature")) { self.consume(); - let has = !self.optional(Token::Not); + let has = !self.optional(Token::Bang); match (self.token(), has) { (Some(Token::String(flag)), true) => list.push(Feature::With(flag)), (Some(Token::String(flag)), false) => list.push(Feature::Without(flag)), @@ -1347,14 +1368,20 @@ impl<'a> Parser<'a> { } // The calling convention is optional. - if let Some(Token::Identifier(text)) = self.token() { - match text.parse() { + match self.token() { + Some(Token::Identifier(text)) => match text.parse() { Ok(cc) => { self.consume(); sig.call_conv = cc; } _ => return err!(self.loc, "unknown calling convention: {}", text), + }, + + Some(Token::Cold) => { + self.consume(); + sig.call_conv = CallConv::Cold; } + _ => {} } Ok(sig) @@ -1445,12 +1472,12 @@ impl<'a> Parser<'a> { Some(Token::GlobalValue(..)) => { self.start_gathering_comments(); self.parse_global_value_decl() - .and_then(|(gv, dat)| ctx.add_gv(gv, dat, self.loc)) + .and_then(|(gv, dat, maybe_fact)| ctx.add_gv(gv, dat, maybe_fact, self.loc)) } - Some(Token::Table(..)) => { + Some(Token::MemoryType(..)) => { self.start_gathering_comments(); - self.parse_table_decl() - .and_then(|(table, dat)| ctx.add_table(table, dat, self.loc)) + self.parse_memory_type_decl() + .and_then(|(mt, dat)| ctx.add_mt(mt, dat, self.loc)) } Some(Token::SigRef(..)) => { self.start_gathering_comments(); @@ -1486,6 +1513,7 @@ impl<'a> Parser<'a> { // | "spill_slot" // | "incoming_arg" // | "outgoing_arg" + // stack-slot-flag ::= "align" "=" Bytes fn parse_stack_slot_decl(&mut self) -> ParseResult<(StackSlot, StackSlotData)> { let ss = self.match_ss("expected stack slot number: ss«n»")?; self.match_token(Token::Equal, "expected '=' in stack slot declaration")?; @@ -1501,7 +1529,30 @@ impl<'a> Parser<'a> { if bytes > i64::from(u32::MAX) { return err!(self.loc, "stack slot too large"); } - let data = StackSlotData::new(kind, bytes as u32); + + // Parse flags. + let align = if self.token() == Some(Token::Comma) { + self.consume(); + self.match_token( + Token::Identifier("align"), + "expected a valid stack-slot flag (currently only `align`)", + )?; + self.match_token(Token::Equal, "expected `=` after flag")?; + let align: i64 = self + .match_imm64("expected alignment-size after `align` flag")? + .into(); + u32::try_from(align) + .map_err(|_| self.error("alignment must be a 32-bit unsigned integer"))? + } else { + 1 + }; + + if !align.is_power_of_two() { + return err!(self.loc, "stack slot alignment is not a power of two"); + } + let align_shift = u8::try_from(align.ilog2()).unwrap(); // Always succeeds: range 0..=31. + + let data = StackSlotData::new(kind, bytes as u32, align_shift); // Collect any trailing comments. self.token(); @@ -1546,16 +1597,25 @@ impl<'a> Parser<'a> { // Parse a global value decl. // - // global-val-decl ::= * GlobalValue(gv) "=" global-val-desc + // global-val-decl ::= * GlobalValue(gv) [ "!" fact ] "=" 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)> { + fn parse_global_value_decl( + &mut self, + ) -> ParseResult<(GlobalValue, GlobalValueData, Option)> { 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")? { @@ -1566,7 +1626,7 @@ impl<'a> Parser<'a> { "expected '.' followed by type in load global value decl", )?; let global_type = self.match_type("expected load type")?; - let flags = self.optional_memflags(); + let flags = self.optional_memflags()?; let base = self.match_gv("expected global value: gv«n»")?; let offset = self.optional_offset32()?; @@ -1577,7 +1637,7 @@ impl<'a> Parser<'a> { base, offset, global_type, - readonly: flags.readonly(), + flags, } } "iadd_imm" => { @@ -1626,72 +1686,114 @@ impl<'a> Parser<'a> { self.token(); self.claim_gathered_comments(gv); - Ok((gv, data)) + Ok((gv, data, fact)) } - // Parse a table decl. - // - // table-decl ::= * Table(table) "=" table-desc - // table-desc ::= table-style table-base { "," table-attr } - // table-style ::= "dynamic" - // table-base ::= GlobalValue(base) - // table-attr ::= "min" Imm64(bytes) - // | "bound" Imm64(bytes) - // | "element_size" Imm64(bytes) - // | "index_type" type + // Parse one field definition in a memory-type struct decl. // - fn parse_table_decl(&mut self) -> ParseResult<(Table, TableData)> { - let table = self.match_table("expected table number: table«n»")?; - self.match_token(Token::Equal, "expected '=' in table declaration")?; - - let style_name = self.match_any_identifier("expected 'static' or 'dynamic'")?; - - // table-desc ::= table-style * table-base { "," table-attr } - // table-base ::= * GlobalValue(base) - let base = match self.token() { - Some(Token::GlobalValue(base_num)) => match GlobalValue::with_number(base_num) { - Some(gv) => gv, - None => return err!(self.loc, "invalid global value number for table base"), - }, - _ => return err!(self.loc, "expected table base"), + // 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 }; - self.consume(); - - let mut data = TableData { - base_gv: base, - min_size: 0.into(), - bound_gv: GlobalValue::reserved_value(), - element_size: 0.into(), - index_type: ir::types::I32, + 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, + }) + } - // table-desc ::= * { "," table-attr } - while self.optional(Token::Comma) { - match self.match_any_identifier("expected table attribute name")? { - "min" => { - data.min_size = self.match_uimm64("expected integer min size")?; - } - "bound" => { - data.bound_gv = match style_name { - "dynamic" => self.match_gv("expected gv bound")?, - t => return err!(self.loc, "unknown table style '{}'", t), - }; - } - "element_size" => { - data.element_size = self.match_uimm64("expected integer element size")?; - } - "index_type" => { - data.index_type = self.match_type("expected index type")?; + // 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; + } } - t => return err!(self.loc, "unknown table attribute '{}'", t), + 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(table); + self.claim_gathered_comments(mt); - Ok((table, data)) + Ok((mt, data)) } // Parse a signature decl. @@ -1943,7 +2045,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()?; + let results = self.parse_inst_results(ctx)?; for result in &results { while ctx.function.dfg.num_values() <= result.index() { @@ -1968,14 +2070,19 @@ impl<'a> Parser<'a> { Ok(()) } - // Parse parenthesized list of block parameters. Returns a vector of (u32, Type) pairs with the - // value numbers of the defined values and the defined types. + // Parse parenthesized list of block parameters. // - // block-params ::= * "(" block-param { "," block-param } ")" + // block-params ::= * "(" ( block-param { "," block-param } )? ")" fn parse_block_params(&mut self, ctx: &mut Context, block: Block) -> ParseResult<()> { - // block-params ::= * "(" block-param { "," block-param } ")" + // block-params ::= * "(" ( block-param { "," block-param } )? ")" self.match_token(Token::LPar, "expected '(' before block parameters")?; + // block-params ::= "(" * ")" + if self.token() == Some(Token::RPar) { + self.consume(); + return Ok(()); + } + // block-params ::= "(" * block-param { "," block-param } ")" self.parse_block_param(ctx, block)?; @@ -1993,16 +2100,23 @@ impl<'a> Parser<'a> { // Parse a single block parameter declaration, and append it to `block`. // - // block-param ::= * Value(v) ":" Type(t) arg-loc? + // block-param ::= * Value(v) [ "!" fact ] ":" Type(t) arg-loc? // arg-loc ::= "[" value-location "]" // fn parse_block_param(&mut self, ctx: &mut Context, block: Block) -> ParseResult<()> { - // block-param ::= * Value(v) ":" Type(t) arg-loc? + // block-param ::= * Value(v) [ "!" fact ] ":" Type(t) arg-loc? let v = self.match_value("block argument must be a value")?; let v_location = self.loc; - // block-param ::= Value(v) * ":" Type(t) arg-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) ":" * Type(t) arg-loc? + // block-param ::= Value(v) [ "!" fact ] ":" * Type(t) arg-loc? while ctx.function.dfg.num_values() <= v.index() { ctx.function.dfg.make_invalid_value_for_parser(); @@ -2012,15 +2126,246 @@ 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: min.into(), + max: max.into(), + }) + } + 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) -> ParseResult> { + fn parse_inst_results(&mut self, ctx: &mut Context) -> ParseResult> { // Result value numbers. let mut results = SmallVec::new(); @@ -2031,10 +2376,29 @@ 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) } - results.push(self.match_value("expected result value")?); + 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; } } @@ -2129,13 +2493,40 @@ impl<'a> Parser<'a> { // instruction ::= [inst-results "="] Opcode(opc) ["." Type] * ... let inst_data = self.parse_inst_operands(ctx, opcode, explicit_ctrl_type)?; - // We're done parsing the instruction now. + // We're done parsing the instruction data itself. // - // We still need to check that the number of result values in the source matches the opcode - // or function call signature. We also need to create values with the right type for all - // the instruction results. + // We still need to check that the number of result values in the source + // matches the opcode or function call signature. We also need to create + // values with the right type for all the instruction results and parse + // and attach stack map entries, if present. let ctrl_typevar = self.infer_typevar(ctx, opcode, explicit_ctrl_type, &inst_data)?; let inst = ctx.function.dfg.make_inst(inst_data); + if opcode.is_call() && !opcode.is_return() && self.optional(Token::Comma) { + self.match_identifier("stack_map", "expected `stack_map = [...]`")?; + self.match_token(Token::Equal, "expected `= [...]`")?; + self.match_token(Token::LBracket, "expected `[...]`")?; + while !self.optional(Token::RBracket) { + let ty = self.match_type("expected ` @ + `")?; + self.match_token(Token::At, "expected `@ + `")?; + let slot = self.match_ss("expected ` + `")?; + let offset: u32 = match self.token() { + Some(Token::Integer(s)) if s.starts_with('+') => { + self.match_uimm32("expected a u32 offset")?.into() + } + _ => { + self.match_token(Token::Plus, "expected `+ `")?; + self.match_uimm32("expected a u32 offset")?.into() + } + }; + ctx.function + .dfg + .append_user_stack_map_entry(inst, ir::UserStackMapEntry { ty, slot, offset }); + if !self.optional(Token::Comma) { + self.match_token(Token::RBracket, "expected `,` or `]`")?; + break; + } + } + } let num_results = ctx.function .dfg @@ -2368,7 +2759,7 @@ impl<'a> Parser<'a> { if self.optional(Token::Equal) { self.match_token(Token::Equal, "expected another =")?; Ok(Comparison::Equals) - } else if self.optional(Token::Not) { + } else if self.optional(Token::Bang) { self.match_token(Token::Equal, "expected a =")?; Ok(Comparison::NotEquals) } else { @@ -2423,8 +2814,10 @@ impl<'a> Parser<'a> { I32 => DataValue::from(self.match_imm32("expected an i32")?), I64 => DataValue::from(Into::::into(self.match_imm64("expected an i64")?)), I128 => DataValue::from(self.match_imm128("expected an i128")?), + F16 => DataValue::from(self.match_ieee16("expected an f16")?), F32 => DataValue::from(self.match_ieee32("expected an f32")?), F64 => DataValue::from(self.match_ieee64("expected an f64")?), + F128 => DataValue::from(self.match_ieee128("expected an f128")?), _ if (ty.is_vector() || ty.is_dynamic_vector()) => { let as_vec = self.match_uimm128(ty)?.into_vec(); if as_vec.len() == 16 { @@ -2439,7 +2832,7 @@ impl<'a> Parser<'a> { return Err(self.error("only 128-bit vectors are currently supported")); } } - _ => return Err(self.error(&format!("don't know how to parse data values of: {}", ty))), + _ => return Err(self.error(&format!("don't know how to parse data values of: {ty}"))), }; Ok(dv) } @@ -2476,6 +2869,10 @@ impl<'a> Parser<'a> { imm: Imm64::new(unsigned), } } + InstructionFormat::UnaryIeee16 => InstructionData::UnaryIeee16 { + opcode, + imm: self.match_ieee16("expected immediate 16-bit float operand")?, + }, InstructionFormat::UnaryIeee32 => InstructionData::UnaryIeee32 { opcode, imm: self.match_ieee32("expected immediate 32-bit float operand")?, @@ -2490,6 +2887,9 @@ impl<'a> Parser<'a> { let c = self.match_constant()?; ctx.check_constant(c, self.loc)?; c + } else if opcode == Opcode::F128const { + let ieee128 = self.match_ieee128("expected immediate 128-bit float operand")?; + ctx.function.dfg.constants.insert(ieee128.into()) } else if let Some(controlling_type) = explicit_control_type { // If an explicit control type is present, we expect a sized value and insert // it in the constant pool. @@ -2732,21 +3132,8 @@ impl<'a> Parser<'a> { dynamic_stack_slot: dss, } } - InstructionFormat::TableAddr => { - let table = self.match_table("expected table identifier")?; - ctx.check_table(table, self.loc)?; - self.match_token(Token::Comma, "expected ',' between operands")?; - let arg = self.match_value("expected SSA value table address")?; - let offset = self.optional_offset32()?; - InstructionData::TableAddr { - opcode, - table, - arg, - offset, - } - } InstructionFormat::Load => { - let flags = self.optional_memflags(); + let flags = self.optional_memflags()?; let addr = self.match_value("expected SSA value address")?; let offset = self.optional_offset32()?; InstructionData::Load { @@ -2757,7 +3144,7 @@ impl<'a> Parser<'a> { } } InstructionFormat::Store => { - let flags = self.optional_memflags(); + let flags = self.optional_memflags()?; let arg = self.match_value("expected SSA value operand")?; self.match_token(Token::Comma, "expected ',' between operands")?; let addr = self.match_value("expected SSA value address")?; @@ -2780,7 +3167,7 @@ impl<'a> Parser<'a> { InstructionData::CondTrap { opcode, arg, code } } InstructionFormat::AtomicCas => { - let flags = self.optional_memflags(); + let flags = self.optional_memflags()?; let addr = self.match_value("expected SSA value address")?; self.match_token(Token::Comma, "expected ',' between operands")?; let expected = self.match_value("expected SSA value address")?; @@ -2793,7 +3180,7 @@ impl<'a> Parser<'a> { } } InstructionFormat::AtomicRmw => { - let flags = self.optional_memflags(); + let flags = self.optional_memflags()?; let op = self.match_enum("expected AtomicRmwOp")?; let addr = self.match_value("expected SSA value address")?; self.match_token(Token::Comma, "expected ',' between operands")?; @@ -2806,7 +3193,7 @@ impl<'a> Parser<'a> { } } InstructionFormat::LoadNoOffset => { - let flags = self.optional_memflags(); + let flags = self.optional_memflags()?; let addr = self.match_value("expected SSA value address")?; InstructionData::LoadNoOffset { opcode, @@ -2815,7 +3202,7 @@ impl<'a> Parser<'a> { } } InstructionFormat::StoreNoOffset => { - let flags = self.optional_memflags(); + let flags = self.optional_memflags()?; let arg = self.match_value("expected SSA value operand")?; self.match_token(Token::Comma, "expected ',' between operands")?; let addr = self.match_value("expected SSA value address")?; @@ -2845,14 +3232,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use super::*; - use crate::error::ParseError; use crate::isaspec::IsaSpec; - use crate::testfile::{Comment, Details}; - use cranelift_codegen::ir::entities::AnyEntity; - use cranelift_codegen::ir::types; - use cranelift_codegen::ir::StackSlotKind; - use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose}; - use cranelift_codegen::isa::CallConv; #[test] fn argument_type() { @@ -2893,7 +3273,7 @@ mod tests { let aliased_to = func.dfg.resolve_aliases(v3); assert_eq!(aliased_to.to_string(), "v4"); } - _ => panic!("expected value: {}", v3), + _ => panic!("expected value: {v3}"), } } @@ -2904,12 +3284,13 @@ mod tests { assert_eq!(sig.returns.len(), 0); assert_eq!(sig.call_conv, CallConv::SystemV); - let sig2 = Parser::new("(i8 uext, f32, f64, i32 sret) -> i32 sext, f64 system_v") - .parse_signature() - .unwrap(); + let sig2 = + Parser::new("(i8 uext, f16, f32, f64, f128, i32 sret) -> i32 sext, f64 system_v") + .parse_signature() + .unwrap(); assert_eq!( sig2.to_string(), - "(i8 uext, f32, f64, i32 sret) -> i32 sext, f64 system_v" + "(i8 uext, f16, f32, f64, f128, i32 sret) -> i32 sext, f64 system_v" ); assert_eq!(sig2.call_conv, CallConv::SystemV); @@ -3494,7 +3875,7 @@ mod tests { "print: %default()" ); - // Demonstrate some unparseable cases. + // Demonstrate some unparsable cases. assert!(parse("print", &sig(&[I32], &[I32])).is_err()); assert!(parse("print:", &sig(&[], &[])).is_err()); assert!(parse("run: ", &sig(&[], &[])).is_err()); @@ -3515,8 +3896,13 @@ mod tests { "1512366032949150931280199141537564007" ); assert_eq!(parse("1234567", I128).to_string(), "1234567"); + assert_eq!(parse("0x16.1", F16).to_string(), "0x1.610p4"); assert_eq!(parse("0x32.32", F32).to_string(), "0x1.919000p5"); assert_eq!(parse("0x64.64", F64).to_string(), "0x1.9190000000000p6"); + assert_eq!( + parse("0x128.128", F128).to_string(), + "0x1.2812800000000000000000000000p8" + ); assert_eq!( parse("[0 1 2 3]", I32X4).to_string(), "0x00000003000000020000000100000000" diff --git a/cranelift/reader/src/run_command.rs b/cranelift/reader/src/run_command.rs index 643ceaeee77b..0c1b2876d362 100644 --- a/cranelift/reader/src/run_command.rs +++ b/cranelift/reader/src/run_command.rs @@ -42,7 +42,7 @@ impl RunCommand { let matched = Self::compare_results(compare, &actual, expected); if !matched { let actual = DisplayDataValues(&actual); - return Err(format!("Failed test: {}, actual: {}", self, actual)); + return Err(format!("Failed test: {self}, actual: {actual}")); } } } @@ -70,10 +70,10 @@ impl RunCommand { impl Display for RunCommand { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - RunCommand::Print(invocation) => write!(f, "print: {}", invocation), + RunCommand::Print(invocation) => write!(f, "print: {invocation}"), RunCommand::Run(invocation, comparison, expected) => { let expected = DisplayDataValues(expected); - write!(f, "run: {} {} {}", invocation, comparison, expected) + write!(f, "run: {invocation} {comparison} {expected}") } } } diff --git a/cranelift/reader/src/sourcemap.rs b/cranelift/reader/src/sourcemap.rs index d8c21ebb10b5..36b10f1c8d91 100644 --- a/cranelift/reader/src/sourcemap.rs +++ b/cranelift/reader/src/sourcemap.rs @@ -10,8 +10,8 @@ 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, SigRef, StackSlot, Table, - Value, + Block, Constant, DynamicStackSlot, FuncRef, GlobalValue, JumpTable, MemoryType, SigRef, + StackSlot, Value, }; use std::collections::HashMap; @@ -49,11 +49,6 @@ impl SourceMap { self.locations.contains_key(&gv.into()) } - /// Look up a table entity. - pub fn contains_table(&self, table: Table) -> bool { - self.locations.contains_key(&table.into()) - } - /// Look up a signature entity. pub fn contains_sig(&self, sig: SigRef) -> bool { self.locations.contains_key(&sig.into()) @@ -106,13 +101,6 @@ impl SourceMap { Some(gv.into()) } }), - "table" => Table::with_number(num).and_then(|table| { - if !self.contains_table(table) { - None - } else { - Some(table.into()) - } - }), "sig" => SigRef::with_number(num).and_then(|sig| { if !self.contains_sig(sig) { None @@ -182,8 +170,8 @@ impl SourceMap { self.def_entity(entity.into(), loc) } - /// Define the table `entity`. - pub fn def_table(&mut self, entity: Table, loc: Location) -> ParseResult<()> { + /// Define the memory type `entity`. + pub fn def_mt(&mut self, entity: MemoryType, loc: Location) -> ParseResult<()> { self.def_entity(entity.into(), loc) } diff --git a/cranelift/reader/src/testcommand.rs b/cranelift/reader/src/testcommand.rs index 17896a070c02..1ed598b042d0 100644 --- a/cranelift/reader/src/testcommand.rs +++ b/cranelift/reader/src/testcommand.rs @@ -4,7 +4,7 @@ //! The general syntax is: //! //!
-//! test <command> [options]...
+//! test <command> [options]...
 //! 
//! //! The options are either a single identifier flag, or setting values like `identifier=value`. @@ -52,7 +52,7 @@ impl<'a> Display for TestCommand<'a> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.command)?; for opt in &self.options { - write!(f, " {}", opt)?; + write!(f, " {opt}")?; } writeln!(f) } @@ -72,8 +72,8 @@ impl<'a> TestOption<'a> { impl<'a> Display for TestOption<'a> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match *self { - TestOption::Flag(s) => write!(f, "{}", s), - TestOption::Value(s, v) => write!(f, "{}={}", s, v), + TestOption::Flag(s) => write!(f, "{s}"), + TestOption::Value(s, v) => write!(f, "{s}={v}"), } } } diff --git a/cranelift/run-souper.sh b/cranelift/run-souper.sh new file mode 100755 index 000000000000..8643379f0f19 --- /dev/null +++ b/cranelift/run-souper.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +# Runs Souper on the LHSes that were harvested by `clif-util +# souper-harvest`. +# +# This script takes two inputs: +# +# 1. The `souper-check` binary, and +# 2. The directory of harvested left-hand sides (aka the `-o $directory` you +# specified to `clif-util souper-harvest`). +# +# For a left-hand side file `foo` that Souper successfully synthesized a +# right-hand side for, this script will write the whole optimization to a +# sibling file named `foo.result`. +# +# The left-hand sides are processed in smallest-to-largest order. This helps +# give you initial results more quickly, but does mean that progress will slow +# down as we encounter larger and larger left-hand sides. +# +# Usage: +# +# run-souper.sh path/to/souper-check path/to/left-hand-sides + +set -e + +# Knobs for configuring how large of right-hand sides Souper should try to +# generate and how much time we give it to synthesize a result. Feel free to +# play with these! +MAX_INSTS=3 +TIMEOUT=5s + +# Run Souper on one left-hand side. +function run_one { + local souper=$1 + local lhs=$2 + local rhs="$lhs".result + + if [[ -f "$rhs" ]]; then + return + fi + + local temp=$(mktemp) + local cmd="taskset --cpu-list 0-3 $souper --infer-rhs -souper-enumerative-synthesis-max-instructions=$MAX_INSTS $lhs > $temp" + + set +e + $(which timeout) --foreground --kill-after=1s $TIMEOUT bash -c "$cmd" + local exit_code="$?" + set -e + + case "$exit_code" in + "0") + # Success! Copy the RHS to its final destination. + cp $lhs $rhs + cat "$temp" >> "$rhs" + ;; + + # SIGINT. Exit this whole script. + "130") + exit 1 + ;; + + # Timeout (regular). + "124") + return + ;; + + # Timeout (with SIGKILL). + "137") + return + ;; + + # Something else. + *) + exit 1 + esac + +} + +# Run Souper on all the left-hand sides. +function main { + local souper=$1 + local lhs_dir=$2 + local lhs_count=$(ls -1 $lhs_dir | grep -v result | wc -l) + + echo "Processing $lhs_count left-hand sides." + + cd "$lhs_dir" + + local i=0; + for lhs in $(ls -1S $lhs_dir); do + # Ignore '.result' files. + if $(echo "$lhs" | grep -q result); then + continue; + fi + + i=$(( $i + 1 )) + if (( $i % 5 == 0 )); then + local percent=$(( $i * 100 / $lhs_count )) + echo "$i / $lhs_count ($percent%)" + fi + + run_one "$souper" "$lhs" + done + + echo "Done!" +} + +# Kick everything off! +main $1 $2 diff --git a/cranelift/serde/Cargo.toml b/cranelift/serde/Cargo.toml index e19cdfaa6826..f37986220b73 100644 --- a/cranelift/serde/Cargo.toml +++ b/cranelift/serde/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-serde" -version = "0.101.0" +version = "0.112.0" authors = ["The Cranelift Project Developers"] description = "Serializer/Deserializer for Cranelift IR" repository = "https://github.com/bytecodealliance/wasmtime" @@ -8,6 +8,10 @@ license = "Apache-2.0 WITH LLVM-exception" readme = "README.md" keywords = ["webassembly", "serde"] edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [[bin]] name = "clif-json" @@ -15,6 +19,6 @@ path = "src/clif-json.rs" [dependencies] clap = { workspace = true } -serde_json = "1.0.26" +serde_json = { workspace = true } cranelift-codegen = { workspace = true, features = ["enable-serde"] } cranelift-reader = { workspace = true } diff --git a/cranelift/serde/src/clif-json.rs b/cranelift/serde/src/clif-json.rs index 7c3c7dcf0e55..2881b0fb64d7 100644 --- a/cranelift/serde/src/clif-json.rs +++ b/cranelift/serde/src/clif-json.rs @@ -1,19 +1,13 @@ //! Utility for `cranelift_serde`. -#![deny( - missing_docs, - trivial_numeric_casts, - unused_extern_crates, - unstable_features -)] -#![warn(unused_import_braces)] +#![deny(missing_docs)] use clap::Parser; use cranelift_codegen::ir::Function; use cranelift_reader::parse_functions; use std::fs::File; +use std::io; use std::io::prelude::*; -use std::io::{self, Write}; use std::process; fn call_ser(file: &str, pretty: bool) -> Result<(), String> { @@ -25,7 +19,7 @@ fn call_ser(file: &str, pretty: bool) -> Result<(), String> { } else { serde_json::to_string(&funcs).unwrap() }; - println!("{}", ser_str); + println!("{ser_str}"); Ok(()) } Err(_pe) => Err("There was a parsing error".to_string()), @@ -37,18 +31,18 @@ fn call_de(file: &File) -> Result<(), String> { Result::Ok(val) => val, Result::Err(err) => panic!("{}", err), }; - println!("{:?}", de); + println!("{de:?}"); Ok(()) } /// Cranelift JSON serializer/deserializer utility #[derive(Parser, Debug)] -#[clap(about)] +#[command(about)] enum Args { /// Serializes Cranelift IR into JSON Serialize { /// Generate pretty json - #[clap(long, short)] + #[arg(long, short)] pretty: bool, /// Input file for serialization diff --git a/cranelift/src/bugpoint.rs b/cranelift/src/bugpoint.rs index d7a52343291b..bc186d47588b 100644 --- a/cranelift/src/bugpoint.rs +++ b/cranelift/src/bugpoint.rs @@ -26,14 +26,14 @@ pub struct Options { file: PathBuf, /// Configure Cranelift settings - #[clap(long = "set")] + #[arg(long = "set")] settings: Vec, /// Specify the target architecture. target: String, /// Be more verbose - #[clap(short, long)] + #[arg(short, long)] verbose: bool, } @@ -62,8 +62,8 @@ pub fn run(options: &Options) -> Result<()> { match reduce(isa, func, options.verbose) { Ok((func, crash_msg)) => { - println!("Crash message: {}", crash_msg); - println!("\n{}", func); + println!("Crash message: {crash_msg}"); + println!("\n{func}"); println!( "{} blocks {} insts -> {} blocks {} insts", orig_block_count, @@ -72,7 +72,7 @@ pub fn run(options: &Options) -> Result<()> { inst_count(&func) ); } - Err(err) => println!("Warning: {}", err), + Err(err) => println!("Warning: {err}"), } } @@ -134,9 +134,9 @@ impl Mutator for RemoveInst { let msg = if func.layout.block_insts(prev_block).next().is_none() { // Make sure empty blocks are removed, as `next_inst_ret_prev` depends on non empty blocks func.layout.remove_block(prev_block); - format!("Remove inst {} and empty block {}", prev_inst, prev_block) + format!("Remove inst {prev_inst} and empty block {prev_block}") } else { - format!("Remove inst {}", prev_inst) + format!("Remove inst {prev_inst}") }; (func, msg, ProgressStatus::ExpandedOrShrinked) }) @@ -272,11 +272,7 @@ impl Mutator for ReplaceInstWithTrap { func.dfg.replace(prev_inst).trap(TrapCode::User(0)); ProgressStatus::Changed }; - ( - func, - format!("Replace inst {} with trap", prev_inst), - status, - ) + (func, format!("Replace inst {prev_inst} with trap"), status) }, ) } @@ -316,7 +312,7 @@ impl Mutator for MoveInstToEntryBlock { if first_block == prev_block || self.block != prev_block { return ( func, - format!("did nothing for {}", prev_inst), + format!("did nothing for {prev_inst}"), ProgressStatus::Skip, ); } @@ -327,7 +323,7 @@ impl Mutator for MoveInstToEntryBlock { ( func, - format!("Move inst {} to entry block", prev_inst), + format!("Move inst {prev_inst} to entry block"), ProgressStatus::ExpandedOrShrinked, ) }) @@ -365,7 +361,7 @@ impl Mutator for RemoveBlock { func.layout.remove_block(self.block); ( func, - format!("Remove block {}", next_block), + format!("Remove block {next_block}"), ProgressStatus::ExpandedOrShrinked, ) }) @@ -701,7 +697,7 @@ impl Mutator for MergeBlocks { if cfg.pred_iter(block).count() != 1 { return Some(( func, - format!("did nothing for {}", block), + format!("did nothing for {block}"), ProgressStatus::Skip, )); } @@ -714,7 +710,7 @@ impl Mutator for MergeBlocks { if branch_dests.len() != 1 { return Some(( func, - format!("did nothing for {}", block), + format!("did nothing for {block}"), ProgressStatus::Skip, )); } @@ -774,9 +770,6 @@ fn replace_with_const(pos: &mut FuncCursor, param: Value) -> &'static str { } else if ty == F64 { pos.ins().with_result(param).f64const(0.0); "f64const" - } else if ty.is_ref() { - pos.ins().with_result(param).null(ty); - "null" } else if ty.is_vector() { let zero_data = vec![0; ty.bytes() as usize].into(); let zero_handle = pos.func.dfg.constants.insert(zero_data); @@ -822,23 +815,24 @@ fn inst_count(func: &Function) -> usize { .sum() } -fn resolve_aliases(func: &mut Function) { - for block in func.stencil.layout.blocks() { - for inst in func.stencil.layout.block_insts(block) { - func.stencil.dfg.resolve_aliases_in_arguments(inst); - } - } -} - /// Resolve aliases only if function still crashes after this. fn try_resolve_aliases(context: &mut CrashCheckContext, func: &mut Function) { let mut func_with_resolved_aliases = func.clone(); - resolve_aliases(&mut func_with_resolved_aliases); + func_with_resolved_aliases.dfg.resolve_all_aliases(); if let CheckResult::Crash(_) = context.check_for_crash(&func_with_resolved_aliases) { *func = func_with_resolved_aliases; } } +/// Remove sourcelocs if the function still crashes after they are removed, to make the reduced clif IR easier to read. +fn try_remove_srclocs(context: &mut CrashCheckContext, func: &mut Function) { + let mut func_with_removed_sourcelocs = func.clone(); + func_with_removed_sourcelocs.srclocs.clear(); + if let CheckResult::Crash(_) = context.check_for_crash(&func_with_removed_sourcelocs) { + *func = func_with_removed_sourcelocs; + } +} + fn reduce(isa: &dyn TargetIsa, mut func: Function, verbose: bool) -> Result<(Function, String)> { let mut context = CrashCheckContext::new(isa); @@ -847,6 +841,7 @@ fn reduce(isa: &dyn TargetIsa, mut func: Function, verbose: bool) -> Result<(Fun } try_resolve_aliases(&mut context, &mut func); + try_remove_srclocs(&mut context, &mut func); let progress_bar = ProgressBar::with_draw_target(0, ProgressDrawTarget::stdout()); progress_bar.set_style( @@ -916,7 +911,7 @@ fn reduce(isa: &dyn TargetIsa, mut func: Function, verbose: bool) -> Result<(Fun ProgressStatus::Skip => unreachable!(), }; if verbose { - progress_bar.println(format!("{}: {}", msg, verb)); + progress_bar.println(format!("{msg}: {verb}")); } } } @@ -1059,7 +1054,6 @@ impl<'a> CrashCheckContext<'a> { #[cfg(test)] mod tests { use super::*; - use cranelift_reader::ParseOptions; fn run_test(test_str: &str, expected_str: &str) { let test_file = parse_test(test_str, ParseOptions::default()).unwrap(); @@ -1088,13 +1082,11 @@ mod tests { "reduction wasn't maximal for insts" ); - let actual_ir = format!("{}", reduced_func); + let actual_ir = format!("{reduced_func}"); let expected_ir = expected_str.replace("\r\n", "\n"); assert!( expected_ir == actual_ir, - "Expected:\n{}\nGot:\n{}", - expected_ir, - actual_ir, + "Expected:\n{expected_ir}\nGot:\n{actual_ir}", ); } } diff --git a/cranelift/src/cat.rs b/cranelift/src/cat.rs index 167d768aa5d4..c90f3105a06c 100644 --- a/cranelift/src/cat.rs +++ b/cranelift/src/cat.rs @@ -13,7 +13,7 @@ use std::path::{Path, PathBuf}; #[derive(Parser)] pub struct Options { /// Specify input file(s) to be used. Use '-' for stdin. - #[clap(required = true)] + #[arg(required = true)] files: Vec, } @@ -36,7 +36,7 @@ fn cat_one(path: &Path) -> Result<()> { if idx != 0 { println!(); } - print!("{}", func); + print!("{func}"); } Ok(()) diff --git a/cranelift/src/clif-util.rs b/cranelift/src/clif-util.rs index 98da1cfb4dc6..da3e164bc14e 100644 --- a/cranelift/src/clif-util.rs +++ b/cranelift/src/clif-util.rs @@ -1,6 +1,3 @@ -#![deny(trivial_numeric_casts)] -#![warn(unused_import_braces, unstable_features, unused_extern_crates)] - use clap::Parser; use std::path::PathBuf; @@ -16,9 +13,6 @@ mod utils; #[cfg(feature = "souper-harvest")] mod souper_harvest; -#[cfg(feature = "wasm")] -mod wasm; - /// Cranelift code generator utility. #[derive(Parser)] enum Commands { @@ -31,11 +25,6 @@ enum Commands { Pass(PassOptions), Bugpoint(bugpoint::Options), - #[cfg(feature = "wasm")] - Wasm(wasm::Options), - #[cfg(not(feature = "wasm"))] - Wasm(CompiledWithoutSupportOptions), - #[cfg(feature = "souper-harvest")] SouperHarvest(souper_harvest::Options), #[cfg(not(feature = "souper-harvest"))] @@ -46,15 +35,15 @@ enum Commands { #[derive(Parser)] struct TestOptions { /// Be more verbose - #[clap(short, long)] + #[arg(short, long)] verbose: bool, /// Print pass timing report for test - #[clap(short = 'T')] + #[arg(short = 'T')] time_passes: bool, /// Specify an input file to be used. Use '-' for stdin. - #[clap(required = true)] + #[arg(required = true)] files: Vec, } @@ -62,11 +51,11 @@ struct TestOptions { #[derive(Parser)] struct PassOptions { /// Be more verbose - #[clap(short, long)] + #[arg(short, long)] verbose: bool, /// Print pass timing report for test - #[clap(short = 'T')] + #[arg(short = 'T')] time_passes: bool, /// Specify an input file to be used. Use '-' for stdin. @@ -76,7 +65,7 @@ struct PassOptions { target: String, /// Specify pass(es) to be run on the input file - #[clap(required = true)] + #[arg(required = true)] passes: Vec, } @@ -95,11 +84,6 @@ fn main() -> anyhow::Result<()> { Commands::Compile(c) => compile::run(&c)?, Commands::Bugpoint(b) => bugpoint::run(&b)?, - #[cfg(feature = "wasm")] - Commands::Wasm(w) => wasm::run(&w)?, - #[cfg(not(feature = "wasm"))] - Commands::Wasm(_) => anyhow::bail!("Error: clif-util was compiled without wasm support."), - #[cfg(feature = "souper-harvest")] Commands::SouperHarvest(s) => souper_harvest::run(&s)?, #[cfg(not(feature = "souper-harvest"))] diff --git a/cranelift/src/compile.rs b/cranelift/src/compile.rs index 0df3f3fa68d4..65aa223d2056 100644 --- a/cranelift/src/compile.rs +++ b/cranelift/src/compile.rs @@ -17,30 +17,30 @@ use std::path::PathBuf; #[derive(Parser)] pub struct Options { /// Print the resulting Cranelift IR - #[clap(short)] + #[arg(short)] print: bool, /// Print pass timing report - #[clap(short = 'T')] + #[arg(short = 'T')] report_times: bool, /// Print machine code disassembly - #[clap(short = 'D', long)] + #[arg(short = 'D', long)] disasm: bool, /// Configure Cranelift settings - #[clap(long = "set")] + #[arg(long = "set")] settings: Vec, /// Specify the Cranelift target - #[clap(long = "target")] + #[arg(long = "target")] target: String, /// Specify an input file to be used. Use '-' for stdin. files: Vec, /// Output object file - #[clap(short = 'o', long = "output")] + #[arg(short = 'o', long = "output")] output: Option, } @@ -84,7 +84,7 @@ fn handle_module( ) -> Result<()> { let buffer = read_to_string(&path)?; let test_file = parse_test(&buffer, ParseOptions::default()) - .with_context(|| format!("failed to parse {}", name))?; + .with_context(|| format!("failed to parse {name}"))?; // If we have an isa from the command-line, use that. Otherwise if the // file contains a unique isa, use that. @@ -128,13 +128,12 @@ fn handle_module( let result = context.compiled_code().unwrap(); print_all( isa, - &context.func.params, + &context.func, &mem, code_info.total_size, options.print, result.buffer.relocs(), result.buffer.traps(), - result.buffer.stack_maps(), )?; } } diff --git a/cranelift/src/disasm.rs b/cranelift/src/disasm.rs index 0b0235b67959..6947a5b977a9 100644 --- a/cranelift/src/disasm.rs +++ b/cranelift/src/disasm.rs @@ -1,8 +1,9 @@ use anyhow::Result; use cfg_if::cfg_if; use cranelift_codegen::ir::function::FunctionParameters; +use cranelift_codegen::ir::Function; use cranelift_codegen::isa::TargetIsa; -use cranelift_codegen::{FinalizedMachReloc, MachStackMap, MachTrap}; +use cranelift_codegen::{FinalizedMachReloc, MachTrap}; use std::fmt::Write; fn print_relocs(func_params: &FunctionParameters, relocs: &[FinalizedMachReloc]) -> String { @@ -35,44 +36,12 @@ pub fn print_traps(traps: &[MachTrap]) -> String { text } -pub fn print_stack_maps(traps: &[MachStackMap]) -> String { - let mut text = String::new(); - for MachStackMap { - offset, - offset_end, - stack_map, - } in traps - { - writeln!( - text, - "add_stack_map at {offset:#x}-{offset_end:#x} mapped_words={}", - stack_map.mapped_words() - ) - .unwrap(); - - write!(text, " entries: ").unwrap(); - let mut first = true; - for i in 0..stack_map.mapped_words() { - if !stack_map.get_bit(i as usize) { - continue; - } - if !first { - write!(text, ", ").unwrap(); - } else { - first = false; - } - write!(text, "{i}").unwrap(); - } - } - text -} - cfg_if! { if #[cfg(feature = "disas")] { - pub fn print_disassembly(isa: &dyn TargetIsa, mem: &[u8]) -> Result<()> { + pub fn print_disassembly(func: &Function, isa: &dyn TargetIsa, mem: &[u8]) -> Result<()> { let cs = isa.to_capstone().map_err(|e| anyhow::format_err!("{}", e))?; - println!("\nDisassembly of {} bytes:", mem.len()); + println!("\nDisassembly of {} bytes <{}>:", mem.len(), func.name); let insns = cs.disasm_all(&mem, 0x0).unwrap(); for i in insns.iter() { let mut line = String::new(); @@ -86,29 +55,29 @@ cfg_if! { if !first { write!(&mut bytes_str, " ").unwrap(); } - write!(&mut bytes_str, "{:02x}", b).unwrap(); + write!(&mut bytes_str, "{b:02x}").unwrap(); len += 1; first = false; } - write!(&mut line, "{:21}\t", bytes_str).unwrap(); + write!(&mut line, "{bytes_str:21}\t").unwrap(); if len > 8 { write!(&mut line, "\n\t\t\t\t").unwrap(); } if let Some(s) = i.mnemonic() { - write!(&mut line, "{}\t", s).unwrap(); + write!(&mut line, "{s}\t").unwrap(); } if let Some(s) = i.op_str() { - write!(&mut line, "{}", s).unwrap(); + write!(&mut line, "{s}").unwrap(); } - println!("{}", line); + println!("{line}"); } Ok(()) } } else { - pub fn print_disassembly(_: &dyn TargetIsa, _: &[u8]) -> Result<()> { + pub fn print_disassembly(_: &Function, _: &dyn TargetIsa, _: &[u8]) -> Result<()> { println!("\nNo disassembly available."); Ok(()) } @@ -117,22 +86,20 @@ cfg_if! { pub fn print_all( isa: &dyn TargetIsa, - func_params: &FunctionParameters, + func: &Function, mem: &[u8], code_size: u32, print: bool, relocs: &[FinalizedMachReloc], traps: &[MachTrap], - stack_maps: &[MachStackMap], ) -> Result<()> { print_bytes(&mem); - print_disassembly(isa, &mem[0..code_size as usize])?; + print_disassembly(func, isa, &mem[0..code_size as usize])?; if print { println!( - "\n{}\n{}\n{}", - print_relocs(func_params, relocs), + "\n{}\n{}", + print_relocs(&func.params, relocs), print_traps(traps), - print_stack_maps(stack_maps) ); } Ok(()) @@ -147,7 +114,7 @@ pub fn print_bytes(mem: &[u8]) { } else { print!(", "); } - print!("{}", byte); + print!("{byte}"); } println!(); } diff --git a/cranelift/src/interpret.rs b/cranelift/src/interpret.rs index a752d692dac9..d0fce058a759 100644 --- a/cranelift/src/interpret.rs +++ b/cranelift/src/interpret.rs @@ -14,11 +14,11 @@ use thiserror::Error; #[derive(Parser)] pub struct Options { /// Specify an input file to be used. Use '-' for stdin. - #[clap(required = true)] + #[arg(required = true)] files: Vec, /// Be more verbose - #[clap(short, long)] + #[arg(short, long)] verbose: bool, } @@ -48,7 +48,7 @@ pub fn run(options: &Options) -> anyhow::Result<()> { match total { 0 => println!("0 files"), 1 => println!("1 file"), - n => println!("{} files", n), + n => println!("{n} files"), } } @@ -122,7 +122,7 @@ impl FileInterpreter { command .run(|func_name, args| { // Because we have stored function names with a leading %, we need to re-add it. - let func_name = &format!("%{}", func_name); + let func_name = &format!("%{func_name}"); let state = InterpreterState::default().with_function_store(env.clone()); match Interpreter::new(state).call_by_name(func_name, args) { Ok(ControlFlow::Return(results)) => Ok(results.to_vec()), diff --git a/cranelift/src/print_cfg.rs b/cranelift/src/print_cfg.rs index fa7027c42bcc..cadb61c760f4 100644 --- a/cranelift/src/print_cfg.rs +++ b/cranelift/src/print_cfg.rs @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; #[derive(Parser)] pub struct Options { /// Specify an input file to be used. Use '-' for stdin. - #[clap(required = true)] + #[arg(required = true)] files: Vec, } diff --git a/cranelift/src/run.rs b/cranelift/src/run.rs index 5564736e3b68..bc9048688fd0 100644 --- a/cranelift/src/run.rs +++ b/cranelift/src/run.rs @@ -8,17 +8,17 @@ use cranelift_filetests::TestFileCompiler; use cranelift_native::builder as host_isa_builder; use cranelift_reader::{parse_run_command, parse_test, Details, IsaSpec, ParseOptions}; use std::path::{Path, PathBuf}; -use target_lexicon::Triple; +use target_lexicon::{Triple, HOST}; /// Execute clif code and verify with test expressions #[derive(Parser)] pub struct Options { /// Specify an input file to be used. Use '-' for stdin. - #[clap(required = true)] + #[arg(required = true)] files: Vec, /// Be more verbose - #[clap(short, long)] + #[arg(short, long)] verbose: bool, } @@ -61,7 +61,7 @@ pub fn run(options: &Options) -> Result<()> { match total { 0 => println!("0 files"), 1 => println!("1 file"), - n => println!("{} files", n), + n => println!("{n} files"), } } @@ -106,15 +106,22 @@ fn run_file_contents(file_contents: String) -> Result<()> { /// Build an ISA based on the current machine running this code (the host) fn create_target_isa(isa_spec: &IsaSpec) -> Result { - if let IsaSpec::None(flags) = isa_spec { - // build an ISA for the current machine - let builder = host_isa_builder().map_err(|s| anyhow::anyhow!("{}", s))?; - Ok(builder.finish(flags.clone())?) - } else { - anyhow::bail!( - "A target ISA was specified in the file but should not have been--only \ - the host ISA can be used for running CLIF files" - ) + let builder = host_isa_builder().map_err(|s| anyhow::anyhow!("{}", s))?; + match *isa_spec { + IsaSpec::None(ref flags) => { + // build an ISA for the current machine + Ok(builder.finish(flags.clone())?) + } + IsaSpec::Some(ref isas) => { + for isa in isas { + if isa.triple().architecture == HOST.architecture { + return Ok(builder.finish(isa.flags().clone())?); + } + } + anyhow::bail!( + "The target ISA specified in the file is not compatible with the host ISA" + ) + } } } diff --git a/cranelift/src/souper_harvest.rs b/cranelift/src/souper_harvest.rs index a8490f842ae4..d8a559c437c7 100644 --- a/cranelift/src/souper_harvest.rs +++ b/cranelift/src/souper_harvest.rs @@ -1,16 +1,17 @@ +use crate::utils::{iterate_files, read_to_string}; use anyhow::{Context as _, Result}; use clap::Parser; +use cranelift_codegen::control::ControlPlane; +use cranelift_codegen::ir::Function; use cranelift_codegen::Context; use cranelift_reader::parse_sets_and_triple; -use cranelift_wasm::DummyEnvironment; -use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use rayon::iter::{IntoParallelIterator, ParallelBridge, ParallelIterator}; use std::collections::HashSet; +use std::hash::{BuildHasher, BuildHasherDefault}; use std::io::Write; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::{fs, io}; -static WASM_MAGIC: &[u8] = &[0x00, 0x61, 0x73, 0x6D]; - /// Harvest candidates for superoptimization from a Wasm or Clif file. /// /// Candidates are emitted in Souper's text format: @@ -18,25 +19,25 @@ static WASM_MAGIC: &[u8] = &[0x00, 0x61, 0x73, 0x6D]; #[derive(Parser)] pub struct Options { /// Specify an input file to be used. Use '-' for stdin. - input: PathBuf, + input: Vec, /// Specify the directory where harvested left-hand side files should be /// written to. - #[clap(short, long)] + #[arg(short, long)] output_dir: PathBuf, /// Configure Cranelift settings - #[clap(long = "set")] + #[arg(long = "set")] settings: Vec, /// Specify the Cranelift target - #[clap(long = "target")] + #[arg(long = "target")] target: String, /// Add a comment from which CLIF variable and function each left-hand side /// was harvested from. This prevents deduplicating harvested left-hand /// sides. - #[clap(long)] + #[arg(long)] add_harvest_source: bool, } @@ -47,16 +48,7 @@ pub fn run(options: &Options) -> Result<()> { anyhow::bail!("`souper-harvest` requires a target isa"); } - let stdin = io::stdin(); - let mut input: Box = if options.input == Path::new("-") { - Box::new(stdin.lock()) - } else { - Box::new(io::BufReader::new( - fs::File::open(&options.input).context("failed to open input file")?, - )) - }; - - match std::fs::create_dir_all(&options.output_dir) { + match fs::create_dir_all(&options.output_dir) { Ok(_) => {} Err(e) if e.kind() == io::ErrorKind::AlreadyExists @@ -76,26 +68,6 @@ pub fn run(options: &Options) -> Result<()> { } } - let mut contents = vec![]; - input - .read_to_end(&mut contents) - .context("failed to read input file")?; - - let funcs = if &contents[..WASM_MAGIC.len()] == WASM_MAGIC { - let mut dummy_environ = DummyEnvironment::new(fisa.isa.unwrap().frontend_config()); - cranelift_wasm::translate_module(&contents, &mut dummy_environ) - .context("failed to translate Wasm module to clif")?; - dummy_environ - .info - .function_bodies - .iter() - .map(|(_, f)| f.clone()) - .collect() - } else { - let contents = String::from_utf8(contents)?; - cranelift_reader::parse_functions(&contents)? - }; - let (send, recv) = std::sync::mpsc::channel::(); let writing_thread = std::thread::spawn({ @@ -111,7 +83,7 @@ pub fn run(options: &Options) -> Result<()> { let i = lhs.find('\n').unwrap(); &lhs[i + 1..] }; - let hash = fxhash::hash(lhs.as_bytes()); + let hash = hash(lhs.as_bytes()); if already_harvested.insert(hash) { let output_path = output_dir.join(hash.to_string()); let mut output = @@ -127,20 +99,31 @@ pub fn run(options: &Options) -> Result<()> { } }); - funcs - .into_par_iter() - .map_with(send, move |send, func| { - let mut ctx = Context::new(); - ctx.func = func; + iterate_files(&options.input) + .par_bridge() + .flat_map(|path| { + parse_input(path) + .unwrap_or_else(|e| { + println!("{e:?}"); + Vec::new() + }) + .into_par_iter() + }) + .map_init( + move || (send.clone(), Context::new()), + move |(send, ctx), func| { + ctx.clear(); + ctx.func = func; - ctx.optimize(fisa.isa.unwrap()) - .context("failed to run optimizations")?; + ctx.optimize(fisa.isa.unwrap(), &mut ControlPlane::default()) + .context("failed to run optimizations")?; - ctx.souper_harvest(send) - .context("failed to run souper harvester")?; + ctx.souper_harvest(send) + .context("failed to run souper harvester")?; - Ok(()) - }) + Ok(()) + }, + ) .collect::>()?; match writing_thread.join() { @@ -150,3 +133,16 @@ pub fn run(options: &Options) -> Result<()> { Ok(()) } + +fn parse_input(path: PathBuf) -> Result> { + let contents = read_to_string(&path)?; + let funcs = cranelift_reader::parse_functions(&contents) + .with_context(|| format!("parse error in {}", path.display()))?; + Ok(funcs) +} + +/// A convenience function for a quick usize hash +#[inline] +pub fn hash(v: &T) -> usize { + BuildHasherDefault::::default().hash_one(v) as usize +} diff --git a/cranelift/src/utils.rs b/cranelift/src/utils.rs index b1645534543a..5fed6e24629a 100644 --- a/cranelift/src/utils.rs +++ b/cranelift/src/utils.rs @@ -37,7 +37,7 @@ pub fn iterate_files<'a>(files: &'a [PathBuf]) -> impl Iterator && !d.file_type().is_dir() } Err(e) => { - println!("Unable to read file: {}", e); + println!("Unable to read file: {e}"); false } }) diff --git a/cranelift/src/wasm.rs b/cranelift/src/wasm.rs deleted file mode 100644 index 8a5e2102b461..000000000000 --- a/cranelift/src/wasm.rs +++ /dev/null @@ -1,321 +0,0 @@ -//! CLI tool to use the functions provided by the [cranelift-wasm](../cranelift_wasm/index.html) -//! crate. -//! -//! Reads Wasm binary/text files, translates the functions' code to Cranelift IR. - -use crate::disasm::print_all; -use anyhow::{Context as _, Result}; -use clap::Parser; -use cranelift_codegen::print_errors::{pretty_error, pretty_verifier_error}; -use cranelift_codegen::settings::FlagsOrIsa; -use cranelift_codegen::timing; -use cranelift_codegen::Context; -use cranelift_entity::EntityRef; -use cranelift_reader::parse_sets_and_triple; -use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex}; -use std::io::Read; -use std::path::Path; -use std::path::PathBuf; -use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; - -/// For verbose printing: only print if the `$x` expression is true. -macro_rules! vprintln { - ($x: expr, $($tts:tt)*) => { - if $x { - println!($($tts)*); - } - }; -} -/// For verbose printing: prints in color if the `$x` expression is true. -macro_rules! vcprintln { - ($x: expr, $use_color: expr, $term: ident, $color: expr, $($tts:tt)*) => { - if $x { - if $use_color { - $term.set_color(ColorSpec::new().set_fg(Some($color)))?; - } - println!($($tts)*); - if $use_color { - $term.reset()?; - } - } - }; -} -/// For verbose printing: prints in color (without an appended newline) if the `$x` expression is true. -macro_rules! vcprint { - ($x: expr, $use_color: expr, $term: ident, $color: expr, $($tts:tt)*) => { - if $x { - if $use_color { - $term.set_color(ColorSpec::new().set_fg(Some($color)))?; - } - print!($($tts)*); - if $use_color { - $term.reset()?; - } - } - }; -} - -/// Compiles Wasm binary/text into Cranelift IR and then into target language -#[derive(Parser)] -pub struct Options { - /// Be more verbose - #[clap(short, long)] - verbose: bool, - - /// Print the resulting Cranelift IR - #[clap(short)] - print: bool, - - /// Print pass timing report - #[clap(short = 'T')] - report_times: bool, - - /// Print machine code disassembly - #[clap(short = 'D', long)] - disasm: bool, - - /// Configure Cranelift settings - #[clap(long = "set")] - settings: Vec, - - /// Specify the Cranelift target - #[clap(long = "target")] - target: String, - - /// Specify an input file to be used. Use '-' for stdin. - files: Vec, - - /// Print bytecode size - #[clap(short = 'X')] - print_size: bool, - - /// Just decode Wasm into Cranelift IR, don't compile it to native code - #[clap(short = 't')] - just_decode: bool, - - /// Just checks the correctness of Cranelift IR translated from Wasm - #[clap(short = 'c')] - check_translation: bool, - - /// Use colors in output? [options: auto/never/always; default: auto] - #[clap(long = "color", default_value("auto"))] - color: ColorOpt, -} - -#[derive(PartialEq, Eq, Clone)] -enum ColorOpt { - Auto, - Never, - Always, -} - -impl std::str::FromStr for ColorOpt { - type Err = String; - - fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - match s.as_str() { - "auto" => Ok(ColorOpt::Auto), - "never" => Ok(ColorOpt::Never), - "always" => Ok(ColorOpt::Always), - _ => Err(format!("expected auto/never/always, found: {}", s)), - } - } -} - -pub fn run(options: &Options) -> Result<()> { - let parsed = parse_sets_and_triple(&options.settings, &options.target)?; - for path in &options.files { - let name = String::from(path.as_os_str().to_string_lossy()); - handle_module(options, path, &name, parsed.as_fisa())?; - } - Ok(()) -} - -fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -> Result<()> { - let color_choice = match options.color { - ColorOpt::Auto => ColorChoice::Auto, - ColorOpt::Always => ColorChoice::Always, - ColorOpt::Never => ColorChoice::Never, - }; - let mut terminal = StandardStream::stdout(color_choice); - let use_color = terminal.supports_color() && options.color == ColorOpt::Auto - || options.color == ColorOpt::Always; - vcprint!( - options.verbose, - use_color, - terminal, - Color::Yellow, - "Handling: " - ); - vprintln!(options.verbose, "\"{}\"", name); - vcprint!( - options.verbose, - use_color, - terminal, - Color::Magenta, - "Translating... " - ); - - let module_binary = if path.to_str() == Some("-") { - let stdin = std::io::stdin(); - let mut buf = Vec::new(); - stdin - .lock() - .read_to_end(&mut buf) - .context("failed to read stdin")?; - wat::parse_bytes(&buf)?.into() - } else { - wat::parse_file(path)? - }; - - let isa = match fisa.isa { - Some(isa) => isa, - None => { - anyhow::bail!("Error: the wasm command requires an explicit isa."); - } - }; - - let mut dummy_environ = DummyEnvironment::new(isa.frontend_config()); - translate_module(&module_binary, &mut dummy_environ)?; - - vcprintln!(options.verbose, use_color, terminal, Color::Green, "ok"); - - if options.just_decode { - if !options.print { - return Ok(()); - } - - let num_func_imports = dummy_environ.get_num_func_imports(); - for (def_index, func) in dummy_environ.info.function_bodies.iter() { - let func_index = num_func_imports + def_index.index(); - let mut context = Context::new(); - context.func = func.clone(); - if let Some(start_func) = dummy_environ.info.start_func { - if func_index == start_func.index() { - println!("; Selected as wasm start function"); - } - } - vprintln!(options.verbose, ""); - for export_name in - &dummy_environ.info.functions[FuncIndex::new(func_index)].export_names - { - println!("; Exported as \"{}\"", export_name); - } - println!("{}", context.func.display()); - vprintln!(options.verbose, ""); - } - let _ = terminal.reset(); - return Ok(()); - } - - if options.check_translation { - vcprint!( - options.verbose, - use_color, - terminal, - Color::Magenta, - "Checking... " - ); - } else { - vcprint!( - options.verbose, - use_color, - terminal, - Color::Magenta, - "Compiling... " - ); - } - - if options.print_size { - vprintln!(options.verbose, ""); - } - - let num_func_imports = dummy_environ.get_num_func_imports(); - let mut total_module_code_size = 0; - let mut context = Context::new(); - for (def_index, func) in dummy_environ.info.function_bodies.iter() { - context.func = func.clone(); - - let mut saved_size = None; - let func_index = num_func_imports + def_index.index(); - let mut mem = vec![]; - let (relocs, traps, stack_maps) = if options.check_translation { - if let Err(errors) = context.verify(fisa) { - anyhow::bail!("{}", pretty_verifier_error(&context.func, None, errors)); - } - (vec![], vec![], vec![]) - } else { - let compiled_code = context - .compile_and_emit(isa, &mut mem, &mut Default::default()) - .map_err(|err| anyhow::anyhow!("{}", pretty_error(&err.func, err.inner)))?; - let code_info = compiled_code.code_info(); - - if options.print_size { - println!( - "Function #{} code size: {} bytes", - func_index, code_info.total_size, - ); - total_module_code_size += code_info.total_size; - println!( - "Function #{} bytecode size: {} bytes", - func_index, - dummy_environ.func_bytecode_sizes[def_index.index()] - ); - } - - if options.disasm { - saved_size = Some(code_info.total_size); - } - ( - compiled_code.buffer.relocs().to_vec(), - compiled_code.buffer.traps().to_vec(), - compiled_code.buffer.stack_maps().to_vec(), - ) - }; - - if options.print { - vprintln!(options.verbose, ""); - if let Some(start_func) = dummy_environ.info.start_func { - if func_index == start_func.index() { - println!("; Selected as wasm start function"); - } - } - for export_name in - &dummy_environ.info.functions[FuncIndex::new(func_index)].export_names - { - println!("; Exported as \"{}\"", export_name); - } - println!("{}", context.func.display()); - vprintln!(options.verbose, ""); - } - - if let Some(total_size) = saved_size { - print_all( - isa, - &context.func.params, - &mem, - total_size, - options.print, - &relocs, - &traps, - &stack_maps, - )?; - } - - context.clear(); - } - - if !options.check_translation && options.print_size { - println!("Total module code size: {} bytes", total_module_code_size); - let total_bytecode_size: usize = dummy_environ.func_bytecode_sizes.iter().sum(); - println!("Total module bytecode size: {} bytes", total_bytecode_size); - } - - if options.report_times { - println!("{}", timing::take_current()); - } - - vcprintln!(options.verbose, use_color, terminal, Color::Green, "ok"); - Ok(()) -} diff --git a/cranelift/tests/bugpoint_consts.clif b/cranelift/tests/bugpoint_consts.clif index 449b53ebbe9b..2234123e4b25 100644 --- a/cranelift/tests/bugpoint_consts.clif +++ b/cranelift/tests/bugpoint_consts.clif @@ -2,13 +2,13 @@ test compile target x86_64 function u0:0() { - sig0 = (f32, f64, i8, i16, i32, i64, i128, i8, i8, i128, r32, r64, i8x16, i16x4, f32x16) + sig0 = (f32, f64, i8, i16, i32, i64, i128, i8, i8, i128, i8x16, i16x4, f32x16) fn0 = u0:1 sig0 block0: trap user0 -block1(v0: f32, v1: f64, v2: i8, v3: i16, v4: i32, v5: i64, v6: i128, v7: i8, v8: i8, v9: i128, v10: r32, v11: r64, v12: i8x16, v13: i16x4, v14: f32x16): - call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +block1(v0: f32, v1: f64, v2: i8, v3: i16, v4: i32, v5: i64, v6: i128, v7: i8, v8: i8, v9: i128, v12: i8x16, v13: i16x4, v14: f32x16): + call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v12, v13, v14) trap user0 } diff --git a/cranelift/tests/bugpoint_consts_expected.clif b/cranelift/tests/bugpoint_consts_expected.clif index c344f484f6e4..bf95789f1a20 100644 --- a/cranelift/tests/bugpoint_consts_expected.clif +++ b/cranelift/tests/bugpoint_consts_expected.clif @@ -1,5 +1,5 @@ function u0:0() fast { - sig0 = (f32, f64, i8, i16, i32, i64, i128, i8, i8, i128, r32, r64, i8x16, i16x4, f32x16) fast + sig0 = (f32, f64, i8, i16, i32, i64, i128, i8, i8, i128, i8x16, i16x4, f32x16) fast fn0 = u0:1 sig0 const0 = 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 const1 = 0x0000000000000000 @@ -18,11 +18,9 @@ block1: v8 = iconst.i8 0 v15 = iconst.i64 0 v9 = uextend.i128 v15 ; v15 = 0 - v10 = null.r32 - v11 = null.r64 v12 = vconst.i8x16 const2 v13 = vconst.i16x4 const1 v14 = vconst.f32x16 const0 - call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) ; v0 = 0.0, v1 = 0.0, v2 = 0, v3 = 0, v4 = 0, v5 = 0, v7 = 0, v8 = 0, v12 = const2, v13 = const1, v14 = const0 + call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v12, v13, v14) ; v0 = 0.0, v1 = 0.0, v2 = 0, v3 = 0, v4 = 0, v5 = 0, v7 = 0, v8 = 0, v12 = const2, v13 = const1, v14 = const0 trap user0 } diff --git a/cranelift/tests/bugpoint_test.clif b/cranelift/tests/bugpoint_test.clif index 47bcb734adba..757bdfc2b298 100644 --- a/cranelift/tests/bugpoint_test.clif +++ b/cranelift/tests/bugpoint_test.clif @@ -300,12 +300,12 @@ block0(v0: i64, v1: i64, v2: i64): v241 -> v1 v256 -> v1 v262 -> v1 - v3 = imul v0, v1 + @0001 v3 = imul v0, v1 v4 = imul v1, v2 store aligned v4, v3 v5 = load.i64 aligned v2+8 store aligned v5, v3+8 - v6 = stack_addr.i64 ss1 + @0002 v6 = stack_addr.i64 ss1 v7 = stack_addr.i64 ss2 v8 = stack_addr.i64 ss3 v9 = stack_addr.i64 ss4 @@ -366,7 +366,7 @@ block0(v0: i64, v1: i64, v2: i64): v64 = stack_addr.i64 ss59 v65 = stack_addr.i64 ss60 v66 = stack_addr.i64 ss61 - v67 = stack_addr.i64 ss62 + @0003 v67 = stack_addr.i64 ss62 v68 = stack_addr.i64 ss63 v69 = stack_addr.i64 ss64 v70 = stack_addr.i64 ss65 diff --git a/cranelift/tests/bugpoint_test_expected.clif b/cranelift/tests/bugpoint_test_expected.clif index 982fcb001188..cc83b54bf151 100644 --- a/cranelift/tests/bugpoint_test_expected.clif +++ b/cranelift/tests/bugpoint_test_expected.clif @@ -6,30 +6,11 @@ block0: v0 = iconst.i64 0 v105 = iconst.i64 0 v829 = iconst.i64 0 - v935 -> v829 - v962 -> v829 - v992 -> v829 - v1036 -> v829 - v1049 -> v829 v842 = iconst.i64 0 - v976 -> v842 - v989 -> v842 - v1038 -> v842 - v1061 -> v842 v883 = iconst.i64 0 - v934 -> v883 - v961 -> v883 - v991 -> v883 - v1005 -> v883 - v1048 -> v883 v951 = iconst.i64 0 - v988 -> v951 v987 = iconst.i64 0 v1052 = iconst.i16 0 - v960 -> v1052 - v990 -> v1052 - v1051 -> v1052 - v1055 -> v1052 call fn0(v0, v105, v1052, v883, v829, v987, v951, v842) ; v0 = 0, v105 = 0, v1052 = 0, v883 = 0, v829 = 0, v987 = 0, v951 = 0, v842 = 0 trap user0 } diff --git a/cranelift/tests/filetests.rs b/cranelift/tests/filetests.rs index 72fbe7494c83..7eb509ff86cc 100644 --- a/cranelift/tests/filetests.rs +++ b/cranelift/tests/filetests.rs @@ -1,4 +1,5 @@ -fn main() -> anyhow::Result<()> { +#[test] +fn filetests() -> anyhow::Result<()> { // Run all the filetests in the following directories. cranelift_filetests::run(false, false, &["filetests".into(), "docs".into()])?; Ok(()) diff --git a/cranelift/umbrella/Cargo.toml b/cranelift/umbrella/Cargo.toml index b3ed56f1404c..8da764de751a 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.101.0" +version = "0.112.0" description = "Umbrella for commonly-used cranelift crates" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift" @@ -10,12 +10,32 @@ categories = ["no-std"] readme = "README.md" keywords = ["compile", "compiler", "jit"] edition.workspace = true +rust-version.workspace = true [dependencies] cranelift-codegen = { workspace = true } -cranelift-frontend = { workspace = true } +cranelift-frontend = { workspace = true, optional = true } +cranelift-interpreter = { workspace = true, optional = true } +cranelift-jit = { workspace = true, optional = true } +cranelift-module = { workspace = true, optional = true } +cranelift-native = { workspace = true, optional = true } +cranelift-object = { workspace = true, optional = true } [features] -default = ["std"] -std = ["cranelift-codegen/std", "cranelift-frontend/std"] -core = ["cranelift-codegen/core", "cranelift-frontend/core"] +default = ["std", "frontend"] +frontend = ["dep:cranelift-frontend"] +interpreter = ["dep:cranelift-interpreter"] +jit = ["dep:cranelift-jit"] +module = ["dep:cranelift-module"] +native = ["dep:cranelift-native"] +object = ["dep:cranelift-object"] +std = [ + "cranelift-codegen/std", + "cranelift-frontend?/std", + "cranelift-module?/std", +] +core = [ + "cranelift-codegen/core", + "cranelift-frontend?/core", + "cranelift-module?/core", +] diff --git a/cranelift/umbrella/src/lib.rs b/cranelift/umbrella/src/lib.rs index 033b44e8ec21..cfda269c735f 100644 --- a/cranelift/umbrella/src/lib.rs +++ b/cranelift/umbrella/src/lib.rs @@ -1,17 +1,22 @@ //! Cranelift umbrella crate, providing a convenient one-line dependency. -#![deny( - missing_docs, - trivial_numeric_casts, - unused_extern_crates, - unstable_features -)] -#![warn(unused_import_braces)] +#![deny(missing_docs)] #![no_std] /// Provide these crates, renamed to reduce stutter. pub use cranelift_codegen as codegen; +#[cfg(feature = "frontend")] pub use cranelift_frontend as frontend; +#[cfg(feature = "interpreter")] +pub use cranelift_interpreter as interpreter; +#[cfg(feature = "jit")] +pub use cranelift_jit as jit; +#[cfg(feature = "module")] +pub use cranelift_module as module; +#[cfg(feature = "native")] +pub use cranelift_native as native; +#[cfg(feature = "object")] +pub use cranelift_object as object; /// A prelude providing convenient access to commonly-used cranelift features. Use /// as `use cranelift::prelude::*`. @@ -28,6 +33,7 @@ pub mod prelude { pub use crate::codegen::isa; pub use crate::codegen::settings::{self, Configurable}; + #[cfg(feature = "frontend")] pub use crate::frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; } diff --git a/cranelift/wasm/Cargo.toml b/cranelift/wasm/Cargo.toml index fcac5021bcc5..79d84ee4aaa3 100644 --- a/cranelift/wasm/Cargo.toml +++ b/cranelift/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-wasm" -version = "0.101.0" +version = "0.112.0" authors = ["The Cranelift Project Developers"] description = "Translator from WebAssembly to Cranelift IR" documentation = "https://docs.rs/cranelift-wasm" @@ -10,18 +10,22 @@ categories = ["no-std", "wasm"] readme = "README.md" keywords = ["webassembly", "wasm"] edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [dependencies] -wasmparser = { workspace = true } +wasmparser = { workspace = true, features = ['validate'] } cranelift-codegen = { workspace = true } cranelift-entity = { workspace = true } cranelift-frontend = { workspace = true } wasmtime-types = { workspace = true } hashbrown = { workspace = true, optional = true } -itertools = "0.10.0" +itertools = "0.12.0" log = { workspace = true } -serde = { version = "1.0.188", optional = true } -serde_derive = { version = "1.0.188", optional = true } +serde = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } smallvec = { workspace = true } [dev-dependencies] @@ -32,4 +36,4 @@ target-lexicon = { workspace = true } default = ["std"] std = ["cranelift-codegen/std", "cranelift-frontend/std"] core = ["hashbrown", "cranelift-codegen/core", "cranelift-frontend/core"] -enable-serde = ["serde", "serde_derive"] +enable-serde = ["serde", "serde_derive", "cranelift-codegen/enable-serde"] diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs index 6a0090c10089..5506c66728df 100644 --- a/cranelift/wasm/src/code_translator.rs +++ b/cranelift/wasm/src/code_translator.rs @@ -92,7 +92,6 @@ use cranelift_codegen::packed_option::ReservedValue; use cranelift_frontend::{FunctionBuilder, Variable}; use itertools::Itertools; use smallvec::SmallVec; -use std::convert::TryFrom; use std::vec::Vec; use wasmparser::{FuncValidator, MemArg, Operator, WasmModuleResources}; @@ -178,13 +177,11 @@ pub fn translate_operator( let addr = builder.ins().global_value(environ.pointer_type(), gv); let mut flags = ir::MemFlags::trusted(); // Put globals in the "table" abstract heap category as well. - flags.set_table(); + flags.set_alias_region(Some(ir::AliasRegion::Table)); builder.ins().load(ty, flags, addr, offset) } - GlobalVariable::Custom => environ.translate_custom_global_get( - builder.cursor(), - GlobalIndex::from_u32(*global_index), - )?, + GlobalVariable::Custom => environ + .translate_custom_global_get(builder, GlobalIndex::from_u32(*global_index))?, }; state.push1(val); } @@ -195,7 +192,7 @@ pub fn translate_operator( let addr = builder.ins().global_value(environ.pointer_type(), gv); let mut flags = ir::MemFlags::trusted(); // Put globals in the "table" abstract heap category as well. - flags.set_table(); + flags.set_alias_region(Some(ir::AliasRegion::Table)); let mut val = state.pop1(); // Ensure SIMD values are cast to their default Cranelift type, I8x16. if ty.is_vector() { @@ -208,7 +205,7 @@ pub fn translate_operator( GlobalVariable::Custom => { let val = state.pop1(); environ.translate_custom_global_set( - builder.cursor(), + builder, GlobalIndex::from_u32(*global_index), val, )?; @@ -640,13 +637,11 @@ pub fn translate_operator( Operator::CallIndirect { type_index, table_index, - table_byte: _, } => { // `type_index` is the index of the function's signature and // `table_index` is the index of the table to search the function // in. let (sigref, num_args) = state.get_indirect_sig(builder.func, *type_index, environ)?; - let table = state.get_or_create_table(builder.func, *table_index, environ)?; let callee = state.pop1(); // Bitcast any vector arguments to their default type, I8X16, before calling. @@ -656,12 +651,18 @@ pub fn translate_operator( let call = environ.translate_call_indirect( builder, TableIndex::from_u32(*table_index), - table, TypeIndex::from_u32(*type_index), sigref, callee, state.peekn(num_args), )?; + let call = match call { + Some(call) => call, + None => { + state.reachable = false; + return Ok(()); + } + }; let inst_results = builder.inst_results(call); debug_assert_eq!( inst_results.len(), @@ -708,7 +709,6 @@ pub fn translate_operator( // `table_index` is the index of the table to search the function // in. let (sigref, num_args) = state.get_indirect_sig(builder.func, *type_index, environ)?; - let table = state.get_or_create_table(builder.func, *table_index, environ)?; let callee = state.pop1(); // Bitcast any vector arguments to their default type, I8X16, before calling. @@ -718,7 +718,6 @@ pub fn translate_operator( environ.translate_return_call_indirect( builder, TableIndex::from_u32(*table_index), - table, TypeIndex::from_u32(*type_index), sigref, callee, @@ -748,7 +747,7 @@ pub fn translate_operator( * Memory management is handled by environment. It is usually translated into calls to * special functions. ************************************************************************************/ - Operator::MemoryGrow { mem, mem_byte: _ } => { + Operator::MemoryGrow { mem } => { // The WebAssembly MVP only supports one linear memory, but we expect the reserved // argument to be a memory index. let heap_index = MemoryIndex::from_u32(*mem); @@ -757,7 +756,7 @@ pub fn translate_operator( environ.before_memory_grow(builder, val, heap_index); state.push1(environ.translate_memory_grow(builder.cursor(), heap_index, heap, val)?) } - Operator::MemorySize { mem, mem_byte: _ } => { + Operator::MemorySize { mem } => { let heap_index = MemoryIndex::from_u32(*mem); let heap = state.get_heap(builder.func, *mem, environ)?; state.push1(environ.translate_memory_size(builder.cursor(), heap_index, heap)?); @@ -1543,54 +1542,43 @@ pub fn translate_operator( environ.translate_data_drop(builder.cursor(), *data_index)?; } Operator::TableSize { table: index } => { - let table = state.get_or_create_table(builder.func, *index, environ)?; - state.push1(environ.translate_table_size( - builder.cursor(), - TableIndex::from_u32(*index), - table, - )?); + state.push1( + environ.translate_table_size(builder.cursor(), TableIndex::from_u32(*index))?, + ); } Operator::TableGrow { table: index } => { let table_index = TableIndex::from_u32(*index); - let table = state.get_or_create_table(builder.func, *index, environ)?; let delta = state.pop1(); let init_value = state.pop1(); state.push1(environ.translate_table_grow( builder.cursor(), table_index, - table, delta, init_value, )?); } Operator::TableGet { table: index } => { let table_index = TableIndex::from_u32(*index); - let table = state.get_or_create_table(builder.func, *index, environ)?; let index = state.pop1(); - state.push1(environ.translate_table_get(builder, table_index, table, index)?); + state.push1(environ.translate_table_get(builder, table_index, index)?); } Operator::TableSet { table: index } => { let table_index = TableIndex::from_u32(*index); - let table = state.get_or_create_table(builder.func, *index, environ)?; let value = state.pop1(); let index = state.pop1(); - environ.translate_table_set(builder, table_index, table, value, index)?; + environ.translate_table_set(builder, table_index, value, index)?; } Operator::TableCopy { dst_table: dst_table_index, src_table: src_table_index, } => { - let dst_table = state.get_or_create_table(builder.func, *dst_table_index, environ)?; - let src_table = state.get_or_create_table(builder.func, *src_table_index, environ)?; let len = state.pop1(); let src = state.pop1(); let dest = state.pop1(); environ.translate_table_copy( builder.cursor(), TableIndex::from_u32(*dst_table_index), - dst_table, TableIndex::from_u32(*src_table_index), - src_table, dest, src, len, @@ -1607,7 +1595,6 @@ pub fn translate_operator( elem_index, table: table_index, } => { - let table = state.get_or_create_table(builder.func, *table_index, environ)?; let len = state.pop1(); let src = state.pop1(); let dest = state.pop1(); @@ -1615,7 +1602,6 @@ pub fn translate_operator( builder.cursor(), *elem_index, TableIndex::from_u32(*table_index), - table, dest, src, len, @@ -1701,17 +1687,17 @@ pub fn translate_operator( | Operator::V128Store32Lane { memarg, lane } | Operator::V128Store64Lane { memarg, lane } => { let vector = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().extractlane(vector, lane.clone())); + state.push1(builder.ins().extractlane(vector, *lane)); translate_store(memarg, ir::Opcode::Store, builder, state, environ)?; } Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => { let vector = pop1_with_bitcast(state, type_of(op), builder); - let extracted = builder.ins().extractlane(vector, lane.clone()); + let extracted = builder.ins().extractlane(vector, *lane); state.push1(builder.ins().sextend(I32, extracted)) } Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => { let vector = pop1_with_bitcast(state, type_of(op), builder); - let extracted = builder.ins().extractlane(vector, lane.clone()); + let extracted = builder.ins().extractlane(vector, *lane); state.push1(builder.ins().uextend(I32, extracted)); // On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so // uextend could be elided; for now, uextend is needed for Cranelift's type checks to @@ -1722,7 +1708,7 @@ pub fn translate_operator( | Operator::F32x4ExtractLane { lane } | Operator::F64x2ExtractLane { lane } => { let vector = pop1_with_bitcast(state, type_of(op), builder); - state.push1(builder.ins().extractlane(vector, lane.clone())) + state.push1(builder.ins().extractlane(vector, *lane)) } Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => { let (vector, replacement) = state.pop2(); @@ -2503,8 +2489,99 @@ pub fn translate_operator( state.push1(r); } - Operator::RefI31 | Operator::I31GetS | Operator::I31GetU => { - unimplemented!("GC operators not yet implemented") + Operator::RefI31 => { + let val = state.pop1(); + let i31ref = environ.translate_ref_i31(builder.cursor(), val)?; + state.push1(i31ref); + } + Operator::I31GetS => { + let i31ref = state.pop1(); + let val = environ.translate_i31_get_s(builder.cursor(), i31ref)?; + state.push1(val); + } + Operator::I31GetU => { + let i31ref = state.pop1(); + let val = environ.translate_i31_get_u(builder.cursor(), i31ref)?; + state.push1(val); + } + + Operator::TryTable { .. } | Operator::ThrowRef => { + return Err(wasm_unsupported!( + "exception operators are not yet implemented" + )); + } + + Operator::RefEq + | Operator::RefTestNonNull { .. } + | Operator::RefTestNullable { .. } + | Operator::RefCastNonNull { .. } + | Operator::RefCastNullable { .. } + | Operator::BrOnCast { .. } + | Operator::BrOnCastFail { .. } + | Operator::AnyConvertExtern + | Operator::ExternConvertAny + | Operator::ArrayNew { .. } + | Operator::ArrayNewDefault { .. } + | Operator::ArrayNewFixed { .. } + | Operator::ArrayNewData { .. } + | Operator::ArrayNewElem { .. } + | Operator::ArrayGet { .. } + | Operator::ArrayGetU { .. } + | Operator::ArrayGetS { .. } + | Operator::ArraySet { .. } + | Operator::ArrayLen { .. } + | Operator::ArrayFill { .. } + | Operator::ArrayCopy { .. } + | Operator::ArrayInitData { .. } + | Operator::ArrayInitElem { .. } + | Operator::StructNew { .. } + | Operator::StructNewDefault { .. } + | Operator::StructGetS { .. } + | Operator::StructGetU { .. } + | Operator::StructSet { .. } + | Operator::StructGet { .. } => { + return Err(wasm_unsupported!("GC operators are not yet implemented")); + } + + Operator::GlobalAtomicGet { .. } + | Operator::GlobalAtomicSet { .. } + | Operator::GlobalAtomicRmwAdd { .. } + | Operator::GlobalAtomicRmwSub { .. } + | Operator::GlobalAtomicRmwOr { .. } + | Operator::GlobalAtomicRmwXor { .. } + | Operator::GlobalAtomicRmwAnd { .. } + | Operator::GlobalAtomicRmwXchg { .. } + | Operator::GlobalAtomicRmwCmpxchg { .. } + | Operator::TableAtomicGet { .. } + | Operator::TableAtomicSet { .. } + | Operator::TableAtomicRmwXchg { .. } + | Operator::TableAtomicRmwCmpxchg { .. } + | Operator::StructAtomicGet { .. } + | Operator::StructAtomicGetS { .. } + | Operator::StructAtomicGetU { .. } + | Operator::StructAtomicSet { .. } + | Operator::StructAtomicRmwAdd { .. } + | Operator::StructAtomicRmwSub { .. } + | Operator::StructAtomicRmwOr { .. } + | Operator::StructAtomicRmwXor { .. } + | Operator::StructAtomicRmwAnd { .. } + | Operator::StructAtomicRmwXchg { .. } + | Operator::StructAtomicRmwCmpxchg { .. } + | Operator::ArrayAtomicGet { .. } + | Operator::ArrayAtomicGetS { .. } + | Operator::ArrayAtomicGetU { .. } + | Operator::ArrayAtomicSet { .. } + | Operator::ArrayAtomicRmwAdd { .. } + | Operator::ArrayAtomicRmwSub { .. } + | Operator::ArrayAtomicRmwOr { .. } + | Operator::ArrayAtomicRmwXor { .. } + | Operator::ArrayAtomicRmwAnd { .. } + | Operator::ArrayAtomicRmwXchg { .. } + | Operator::ArrayAtomicRmwCmpxchg { .. } + | Operator::RefI31Shared { .. } => { + return Err(wasm_unsupported!( + "shared-everything-threads operators are not yet implemented" + )); } }; Ok(()) @@ -2685,7 +2762,7 @@ where // `addr32 + offset` to `addr32 + offset + width` (not inclusive). In this // scenario our adjusted offset that we're checking is `memarg.offset + // access_size`. Note that we do saturating arithmetic here to avoid - // overflow. THe addition here is in the 64-bit space, which means that + // overflow. The addition here is in the 64-bit space, which means that // we'll never overflow for 32-bit wasm but for 64-bit this is an issue. If // our effective offset is u64::MAX though then it's impossible for for // that to actually be a valid offset because otherwise the wasm linear @@ -2736,7 +2813,7 @@ where // legalization of `heap_addr`, eliding the bounds check entirely. // // * For wasm64 offsets <=2gb will generate a single `heap_addr` - // instruction, but at this time all heaps are "dyanmic" which means that + // instruction, but at this time all heaps are "dynamic" which means that // a single bounds check is forced. Ideally we'd do better here, but // that's the current state of affairs. // @@ -2811,11 +2888,16 @@ where let mut flags = MemFlags::new(); flags.set_endianness(ir::Endianness::Little); + if heap.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, // vmctx, stack) accesses. - flags.set_heap(); + flags.set_alias_region(Some(ir::AliasRegion::Heap)); Ok(Reachability::Reachable((flags, index, addr))) } @@ -2943,7 +3025,7 @@ fn mem_op_size(opcode: ir::Opcode, ty: Type) -> u8 { ir::Opcode::Istore16 | ir::Opcode::Sload16 | ir::Opcode::Uload16 => 2, ir::Opcode::Istore32 | ir::Opcode::Sload32 | ir::Opcode::Uload32 => 4, ir::Opcode::Store | ir::Opcode::Load => u8::try_from(ty.bytes()).unwrap(), - _ => panic!("unknown size of mem op for {:?}", opcode), + _ => panic!("unknown size of mem op for {opcode:?}"), } } diff --git a/cranelift/wasm/src/code_translator/bounds_checks.rs b/cranelift/wasm/src/code_translator/bounds_checks.rs index 5b4d48e03cb2..c66eb8001acb 100644 --- a/cranelift/wasm/src/code_translator/bounds_checks.rs +++ b/cranelift/wasm/src/code_translator/bounds_checks.rs @@ -24,6 +24,7 @@ use crate::{FuncEnvironment, HeapData, HeapStyle}; use cranelift_codegen::{ cursor::{Cursor, FuncCursor}, ir::{self, condcodes::IntCC, InstBuilder, RelSourceLoc}, + ir::{Expr, Fact}, }; use cranelift_frontend::FunctionBuilder; use wasmtime_types::WasmResult; @@ -48,14 +49,73 @@ pub fn bounds_check_and_compute_addr( where Env: FuncEnvironment + ?Sized, { + let pointer_bit_width = u16::try_from(env.pointer_type().bits()).unwrap(); + let orig_index = index; let index = cast_index_to_pointer_ty( index, heap.index_type, env.pointer_type(), + heap.memory_type.is_some(), &mut builder.cursor(), ); let offset_and_size = offset_plus_size(offset, access_size); let spectre_mitigations_enabled = env.heap_access_spectre_mitigation(); + let pcc = env.proof_carrying_code(); + + let host_page_size_log2 = env.target_config().page_size_align_log2; + let can_use_virtual_memory = heap.page_size_log2 >= host_page_size_log2; + + 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 + }; // We need to emit code that will trap (or compute an address that will trap // when accessed) if @@ -84,17 +144,24 @@ where // index + 1 > bound // ==> index >= bound HeapStyle::Dynamic { bound_gv } if offset_and_size == 1 => { - let bound = builder.ins().global_value(env.pointer_type(), bound_gv); - let oob = builder - .ins() - .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound); + let bound = get_dynamic_heap_bound(builder, env, heap); + let oob = make_compare( + builder, + IntCC::UnsignedGreaterThanOrEqual, + index, + Some(0), + bound, + Some(0), + ); Reachable(explicit_check_oob_condition_and_compute_addr( &mut builder.cursor(), heap, env.pointer_type(), index, offset, + access_size, spectre_mitigations_enabled, + AddrPcc::dynamic(heap.memory_type, bound_gv), oob, )) } @@ -124,16 +191,27 @@ where // offset immediates -- which is a common code pattern when accessing // multiple fields in the same struct that is in linear memory -- // will all emit the same `index > bound` check, which we can GVN. - HeapStyle::Dynamic { bound_gv } if offset_and_size <= heap.offset_guard_size => { - let bound = builder.ins().global_value(env.pointer_type(), bound_gv); - let oob = builder.ins().icmp(IntCC::UnsignedGreaterThan, index, bound); + HeapStyle::Dynamic { bound_gv } + if can_use_virtual_memory && offset_and_size <= heap.offset_guard_size => + { + let bound = get_dynamic_heap_bound(builder, env, heap); + let oob = make_compare( + builder, + IntCC::UnsignedGreaterThan, + index, + Some(0), + bound, + Some(0), + ); Reachable(explicit_check_oob_condition_and_compute_addr( &mut builder.cursor(), heap, env.pointer_type(), index, offset, + access_size, spectre_mitigations_enabled, + AddrPcc::dynamic(heap.memory_type, bound_gv), oob, )) } @@ -146,18 +224,38 @@ where // index + offset + access_size > bound // ==> index > bound - (offset + access_size) HeapStyle::Dynamic { bound_gv } if offset_and_size <= heap.min_size.into() => { - let bound = builder.ins().global_value(env.pointer_type(), bound_gv); - let adjusted_bound = builder.ins().iadd_imm(bound, -(offset_and_size as i64)); - let oob = builder - .ins() - .icmp(IntCC::UnsignedGreaterThan, index, adjusted_bound); + 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), + ); Reachable(explicit_check_oob_condition_and_compute_addr( &mut builder.cursor(), heap, env.pointer_type(), index, offset, + access_size, spectre_mitigations_enabled, + AddrPcc::dynamic(heap.memory_type, bound_gv), oob, )) } @@ -170,23 +268,43 @@ where HeapStyle::Dynamic { bound_gv } => { let access_size_val = builder .ins() + // 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 = builder.ins().uadd_overflow_trap( index, access_size_val, ir::TrapCode::HeapOutOfBounds, ); - let bound = builder.ins().global_value(env.pointer_type(), bound_gv); - let oob = builder - .ins() - .icmp(IntCC::UnsignedGreaterThan, adjusted_index, bound); + 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), + ); Reachable(explicit_check_oob_condition_and_compute_addr( &mut builder.cursor(), heap, env.pointer_type(), index, offset, + access_size, spectre_mitigations_enabled, + AddrPcc::dynamic(heap.memory_type, bound_gv), oob, )) } @@ -200,6 +318,10 @@ where // bound`, since we will end up being out-of-bounds regardless of the // given `index`. HeapStyle::Static { bound } if offset_and_size > bound.into() => { + assert!( + can_use_virtual_memory, + "static memories require the ability to use virtual memory" + ); env.before_unconditionally_trapping_memory_access(builder)?; builder.ins().trap(ir::TrapCode::HeapOutOfBounds); Unreachable @@ -244,16 +366,25 @@ where // within the guard page region, neither of which require emitting an // explicit bounds check. HeapStyle::Static { bound } - if heap.index_type == ir::types::I32 + if can_use_virtual_memory + && heap.index_type == ir::types::I32 && u64::from(u32::MAX) <= u64::from(bound) + u64::from(heap.offset_guard_size) - offset_and_size => { + assert!( + can_use_virtual_memory, + "static memories require the ability to use virtual memory" + ); Reachable(compute_addr( &mut builder.cursor(), heap, env.pointer_type(), index, offset, + AddrPcc::static32( + heap.memory_type, + u64::from(bound) + u64::from(heap.offset_guard_size), + ), )) } @@ -269,30 +400,98 @@ where // precise, not rely on the virtual memory subsystem at all, and not // factor in the guard pages here. HeapStyle::Static { bound } => { + assert!( + can_use_virtual_memory, + "static memories require the ability to use virtual memory" + ); // NB: this subtraction cannot wrap because we didn't hit the first // special case. let adjusted_bound = u64::from(bound) - offset_and_size; - let oob = - builder - .ins() - .icmp_imm(IntCC::UnsignedGreaterThan, index, adjusted_bound as i64); + 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), + ); Reachable(explicit_check_oob_condition_and_compute_addr( &mut builder.cursor(), heap, env.pointer_type(), index, offset, + access_size, spectre_mitigations_enabled, + AddrPcc::static32(heap.memory_type, u64::from(bound)), oob, )) } }) } +/// Get the bound of a dynamic heap as an `ir::Value`. +fn get_dynamic_heap_bound( + builder: &mut FunctionBuilder, + env: &mut Env, + heap: &HeapData, +) -> ir::Value +where + Env: FuncEnvironment + ?Sized, +{ + let enable_pcc = heap.memory_type.is_some(); + + let (value, gv) = match (heap.max_size, &heap.style) { + // 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), HeapStyle::Dynamic { bound_gv }) + if heap.min_size == max_size && !enable_pcc => + { + ( + builder.ins().iconst(env.pointer_type(), max_size as i64), + *bound_gv, + ) + } + + // Load the heap bound from its global variable. + (_, HeapStyle::Dynamic { bound_gv }) => ( + builder.ins().global_value(env.pointer_type(), *bound_gv), + *bound_gv, + ), + + (_, HeapStyle::Static { .. }) => unreachable!("not a dynamic heap"), + }; + + // 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, + )); + } + + value +} + fn cast_index_to_pointer_ty( index: ir::Value, index_ty: ir::Type, pointer_ty: ir::Type, + pcc: bool, pos: &mut FuncCursor, ) -> ir::Value { if index_ty == pointer_ty { @@ -307,6 +506,14 @@ 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(); @@ -319,6 +526,25 @@ 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)) + } +} + /// Emit explicit checks on the given out-of-bounds condition for the Wasm /// address and return the native address. /// @@ -330,8 +556,11 @@ fn explicit_check_oob_condition_and_compute_addr( addr_ty: ir::Type, index: ir::Value, offset: u32, + access_size: u8, // Whether Spectre mitigations are enabled for heap accesses. spectre_mitigations_enabled: bool, + // 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). @@ -342,11 +571,42 @@ fn explicit_check_oob_condition_and_compute_addr( .trapnz(oob_condition, ir::TrapCode::HeapOutOfBounds); } - let mut addr = compute_addr(pos, heap, addr_ty, index, offset); + let mut addr = compute_addr(pos, heap, addr_ty, index, offset, pcc); if spectre_mitigations_enabled { let null = pos.ins().iconst(addr_ty, 0); addr = pos.ins().select_spectre_guard(oob_condition, null, addr); + + match pcc { + None => {} + Some(AddrPcc::Static32(ty, size)) => { + pos.func.dfg.facts[null] = + Some(Fact::constant(u16::try_from(addr_ty.bits()).unwrap(), 0)); + pos.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)) => { + pos.func.dfg.facts[null] = + Some(Fact::constant(u16::try_from(addr_ty.bits()).unwrap(), 0)); + pos.func.dfg.facts[addr] = Some(Fact::DynamicMem { + ty, + min: Expr::constant(0), + max: Expr::offset( + &Expr::global_value(gv), + i64::try_from(heap.offset_guard_size) + .unwrap() + .checked_sub(i64::from(access_size)) + .unwrap(), + ) + .unwrap(), + nullable: true, + }); + } + } } addr @@ -364,11 +624,54 @@ 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 { @@ -376,7 +679,48 @@ fn compute_addr( // `select_spectre_guard`, if any. If it happens after, then we // potentially are letting speculative execution read the whole first // 4GiB of memory. - pos.ins().iadd_imm(base_and_index, offset as i64) + 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 } } diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs index 95f9fecece62..cb4b09bcec44 100644 --- a/cranelift/wasm/src/environ/dummy.rs +++ b/cranelift/wasm/src/environ/dummy.rs @@ -7,16 +7,14 @@ use crate::environ::{FuncEnvironment, GlobalVariable, ModuleEnvironment, TargetEnvironment}; use crate::func_translator::FuncTranslator; -use crate::state::FuncTranslationState; -use crate::WasmType; use crate::{ DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Heap, - HeapData, HeapStyle, Memory, MemoryIndex, Table, TableIndex, TypeConvert, TypeIndex, + HeapData, HeapStyle, Memory, MemoryIndex, Table, TableIndex, TableSize, TypeConvert, TypeIndex, WasmFuncType, WasmHeapType, WasmResult, }; -use core::convert::TryFrom; +use crate::{TableData, WasmValType}; use cranelift_codegen::cursor::FuncCursor; -use cranelift_codegen::ir::immediates::{Offset32, Uimm64}; +use cranelift_codegen::ir::immediates::Offset32; use cranelift_codegen::ir::{self, InstBuilder}; use cranelift_codegen::ir::{types::*, UserFuncName}; use cranelift_codegen::isa::{CallConv, TargetFrontendConfig}; @@ -25,7 +23,8 @@ use cranelift_frontend::FunctionBuilder; use std::boxed::Box; use std::string::String; use std::vec::Vec; -use wasmparser::{FuncValidator, FunctionBody, Operator, ValidatorResources, WasmFeatures}; +use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures}; +use wasmtime_types::wasm_unsupported; /// A collection of names under which a given entity is exported. pub struct Exportable { @@ -106,31 +105,6 @@ impl DummyModuleInfo { } } -/// State for tracking and checking reachability at each operator. Used for unit testing with the -/// `DummyEnvironment`. -#[derive(Clone)] -pub struct ExpectedReachability { - /// Before- and after-reachability - reachability: Vec<(bool, bool)>, - before_idx: usize, - after_idx: usize, -} - -impl ExpectedReachability { - fn check_before(&mut self, reachable: bool) { - assert_eq!(reachable, self.reachability[self.before_idx].0); - self.before_idx += 1; - } - fn check_after(&mut self, reachable: bool) { - assert_eq!(reachable, self.reachability[self.after_idx].1); - self.after_idx += 1; - } - fn check_end(&self) { - assert_eq!(self.before_idx, self.reachability.len()); - assert_eq!(self.after_idx, self.reachability.len()); - } -} - /// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and /// emitting placeholders when forced to. Don't try to execute code translated for this /// environment, essentially here for translation debug purposes. @@ -149,10 +123,6 @@ pub struct DummyEnvironment { /// Function names. function_names: SecondaryMap, - - /// Expected reachability data (before/after for each op) to assert. This is used for testing. - #[doc(hidden)] - pub expected_reachability: Option, } impl DummyEnvironment { @@ -164,14 +134,13 @@ impl DummyEnvironment { func_bytecode_sizes: Vec::new(), module_name: None, function_names: SecondaryMap::new(), - expected_reachability: None, } } /// Return a `DummyFuncEnvironment` for translating functions within this /// `DummyEnvironment`. pub fn func_env(&self) -> DummyFuncEnvironment { - DummyFuncEnvironment::new(&self.info, self.expected_reachability.clone()) + DummyFuncEnvironment::new(&self.info) } /// Get the type for the function at the given index. @@ -189,17 +158,6 @@ impl DummyEnvironment { pub fn get_func_name(&self, func_index: FuncIndex) -> Option<&str> { self.function_names.get(func_index).map(String::as_ref) } - - /// Test reachability bits before and after every opcode during translation, as provided by the - /// `FuncTranslationState`. This is generally used only for unit tests. This is applied to - /// every function in the module (so is likely only useful for test modules with one function). - pub fn test_expected_reachability(&mut self, reachability: Vec<(bool, bool)>) { - self.expected_reachability = Some(ExpectedReachability { - reachability, - before_idx: 0, - after_idx: 0, - }); - } } /// The `FuncEnvironment` implementation for use by the `DummyEnvironment`. @@ -207,23 +165,20 @@ pub struct DummyFuncEnvironment<'dummy_environment> { /// This function environment's module info. pub mod_info: &'dummy_environment DummyModuleInfo, - /// Expected reachability data (before/after for each op) to assert. This is used for testing. - expected_reachability: Option, - /// Heaps we have created to implement Wasm linear memories. pub heaps: PrimaryMap, + + /// Cranelift tables we have created to implement Wasm tables. + tables: SecondaryMap>, } impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> { /// Construct a new `DummyFuncEnvironment`. - pub fn new( - mod_info: &'dummy_environment DummyModuleInfo, - expected_reachability: Option, - ) -> Self { + pub fn new(mod_info: &'dummy_environment DummyModuleInfo) -> Self { Self { mod_info, - expected_reachability, heaps: Default::default(), + tables: Default::default(), } } @@ -239,16 +194,51 @@ impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> { } fn reference_type(&self) -> ir::Type { - match self.pointer_type() { - ir::types::I32 => ir::types::R32, - ir::types::I64 => ir::types::R64, - _ => panic!("unsupported pointer type"), + self.pointer_type() + } + + fn ensure_table_exists(&mut self, func: &mut ir::Function, index: TableIndex) { + if self.tables[index].is_some() { + return; } + + // Create a table whose base address is stored at `vmctx+0`. + let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); + let base_gv = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: Offset32::new(0), + global_type: self.pointer_type(), + // When tables in wasm become "growable", revisit whether this can be readonly or not. + flags: ir::MemFlags::trusted().with_readonly(), + }); + + let table = &self.mod_info.tables[index].entity; + + let bound = if Some(table.minimum) == table.maximum { + TableSize::Static { + bound: table.minimum, + } + } else { + TableSize::Dynamic { + bound_gv: func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: Offset32::new(0), + global_type: I32, + flags: ir::MemFlags::trusted().with_readonly(), + }), + } + }; + + self.tables[index] = Some(TableData { + base_gv, + bound, + element_size: u32::from(self.pointer_bytes()) * 2, + }); } } impl<'dummy_environment> TypeConvert for DummyFuncEnvironment<'dummy_environment> { - fn lookup_heap_type(&self, _index: TypeIndex) -> WasmHeapType { + fn lookup_heap_type(&self, _index: wasmparser::UnpackedIndex) -> WasmHeapType { unimplemented!() } } @@ -261,6 +251,10 @@ impl<'dummy_environment> TargetEnvironment for DummyFuncEnvironment<'dummy_envir fn heap_access_spectre_mitigation(&self) -> bool { false } + + fn proof_carrying_code(&self) -> bool { + false + } } impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> { @@ -276,12 +270,12 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ gv: vmctx, offset, ty: match self.mod_info.globals[index].entity.wasm_ty { - WasmType::I32 => ir::types::I32, - WasmType::I64 => ir::types::I64, - WasmType::F32 => ir::types::F32, - WasmType::F64 => ir::types::F64, - WasmType::V128 => ir::types::I8X16, - WasmType::Ref(_) => ir::types::R64, + WasmValType::I32 => ir::types::I32, + WasmValType::I64 => ir::types::I64, + WasmValType::F32 => ir::types::F32, + WasmValType::F64 => ir::types::F64, + WasmValType::V128 => ir::types::I8X16, + WasmValType::Ref(_) => self.pointer_type(), }, }) } @@ -297,42 +291,19 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ base: addr, offset: Offset32::new(0), global_type: self.pointer_type(), - readonly: true, + flags: ir::MemFlags::trusted().with_readonly(), }); Ok(self.heaps.push(HeapData { base: gv, min_size: 0, + max_size: None, offset_guard_size: 0x8000_0000, style: HeapStyle::Static { bound: 0x1_0000_0000, }, index_type: I32, - })) - } - - fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> WasmResult { - // Create a table whose base address is stored at `vmctx+0`. - let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); - let base_gv = func.create_global_value(ir::GlobalValueData::Load { - base: vmctx, - offset: Offset32::new(0), - global_type: self.pointer_type(), - readonly: true, // when tables in wasm become "growable", revisit whether this can be readonly or not. - }); - let bound_gv = func.create_global_value(ir::GlobalValueData::Load { - base: vmctx, - offset: Offset32::new(0), - global_type: I32, - readonly: true, - }); - - Ok(func.create_table(ir::TableData { - base_gv, - min_size: Uimm64::new(0), - bound_gv, - element_size: Uimm64::from(u64::from(self.pointer_bytes()) * 2), - index_type: I32, + memory_type: None, })) } @@ -367,51 +338,15 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ })) } - fn before_translate_operator( - &mut self, - _op: &Operator, - _builder: &mut FunctionBuilder, - state: &FuncTranslationState, - ) -> WasmResult<()> { - if let Some(ref mut r) = &mut self.expected_reachability { - r.check_before(state.reachable()); - } - Ok(()) - } - - fn after_translate_operator( - &mut self, - _op: &Operator, - _builder: &mut FunctionBuilder, - state: &FuncTranslationState, - ) -> WasmResult<()> { - if let Some(ref mut r) = &mut self.expected_reachability { - r.check_after(state.reachable()); - } - Ok(()) - } - - fn after_translate_function( - &mut self, - _builder: &mut FunctionBuilder, - _state: &FuncTranslationState, - ) -> WasmResult<()> { - if let Some(ref mut r) = &mut self.expected_reachability { - r.check_end(); - } - Ok(()) - } - fn translate_call_indirect( &mut self, builder: &mut FunctionBuilder, _table_index: TableIndex, - _table: ir::Table, _sig_index: TypeIndex, sig_ref: ir::SigRef, callee: ir::Value, call_args: &[ir::Value], - ) -> WasmResult { + ) -> WasmResult> { // Pass the current function's vmctx parameter on to the callee. let vmctx = builder .func @@ -438,17 +373,18 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ args.extend(call_args.iter().cloned(), &mut builder.func.dfg.value_lists); args.push(vmctx, &mut builder.func.dfg.value_lists); - Ok(builder - .ins() - .CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args) - .0) + Ok(Some( + builder + .ins() + .CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args) + .0, + )) } fn translate_return_call_indirect( &mut self, _builder: &mut FunctionBuilder, _table_index: TableIndex, - _table: ir::Table, _sig_index: TypeIndex, _sig_ref: ir::SigRef, _callee: ir::Value, @@ -568,7 +504,6 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ &mut self, mut pos: FuncCursor, _index: TableIndex, - _table: ir::Table, ) -> WasmResult { Ok(pos.ins().iconst(I32, -1i32 as u32 as i64)) } @@ -577,7 +512,6 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ &mut self, mut pos: FuncCursor, _table_index: TableIndex, - _table: ir::Table, _delta: ir::Value, _init_value: ir::Value, ) -> WasmResult { @@ -587,21 +521,33 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ fn translate_table_get( &mut self, builder: &mut FunctionBuilder, - _table_index: TableIndex, - _table: ir::Table, - _index: ir::Value, + table_index: TableIndex, + index: ir::Value, ) -> WasmResult { - Ok(builder.ins().null(self.reference_type())) + let pointer_type = self.pointer_type(); + self.ensure_table_exists(builder.func, table_index); + let table = self.tables[table_index].as_ref().unwrap(); + let (table_entry_addr, flags) = + table.prepare_table_addr(builder, index, pointer_type, true); + let value = builder + .ins() + .load(self.reference_type(), flags, table_entry_addr, 0); + Ok(value) } fn translate_table_set( &mut self, - _builder: &mut FunctionBuilder, - _table_index: TableIndex, - _table: ir::Table, - _value: ir::Value, - _index: ir::Value, + builder: &mut FunctionBuilder, + table_index: TableIndex, + value: ir::Value, + index: ir::Value, ) -> WasmResult<()> { + let pointer_type = self.pointer_type(); + self.ensure_table_exists(builder.func, table_index); + let table = self.tables[table_index].as_ref().unwrap(); + let (table_entry_addr, flags) = + table.prepare_table_addr(builder, index, pointer_type, true); + builder.ins().store(flags, value, table_entry_addr, 0); Ok(()) } @@ -609,9 +555,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ &mut self, _pos: FuncCursor, _dst_index: TableIndex, - _dst_table: ir::Table, _src_index: TableIndex, - _src_table: ir::Table, _dst: ir::Value, _src: ir::Value, _len: ir::Value, @@ -635,7 +579,6 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ _pos: FuncCursor, _seg_index: u32, _table_index: TableIndex, - _table: ir::Table, _dst: ir::Value, _src: ir::Value, _len: ir::Value, @@ -694,10 +637,30 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ ) -> WasmResult { Ok(pos.ins().iconst(I32, 0)) } + + fn translate_ref_i31(&mut self, _pos: FuncCursor, _val: ir::Value) -> WasmResult { + Err(wasm_unsupported!("ref.i31")) + } + + fn translate_i31_get_s( + &mut self, + _pos: FuncCursor, + _i31ref: ir::Value, + ) -> WasmResult { + Err(wasm_unsupported!("i31.get_s")) + } + + fn translate_i31_get_u( + &mut self, + _pos: FuncCursor, + _i31ref: ir::Value, + ) -> WasmResult { + Err(wasm_unsupported!("i31.get_u")) + } } impl TypeConvert for DummyEnvironment { - fn lookup_heap_type(&self, _index: TypeIndex) -> WasmHeapType { + fn lookup_heap_type(&self, _index: wasmparser::UnpackedIndex) -> WasmHeapType { unimplemented!() } } @@ -710,24 +673,23 @@ impl TargetEnvironment for DummyEnvironment { fn heap_access_spectre_mitigation(&self) -> bool { false } + + fn proof_carrying_code(&self) -> bool { + false + } } impl<'data> ModuleEnvironment<'data> for DummyEnvironment { fn declare_type_func(&mut self, wasm: WasmFuncType) -> WasmResult<()> { let mut sig = ir::Signature::new(CallConv::Fast); - let mut cvt = |ty: &WasmType| { - let reference_type = match self.pointer_type() { - ir::types::I32 => ir::types::R32, - ir::types::I64 => ir::types::R64, - _ => panic!("unsupported pointer type"), - }; + let mut cvt = |ty: &WasmValType| { ir::AbiParam::new(match ty { - WasmType::I32 => ir::types::I32, - WasmType::I64 => ir::types::I64, - WasmType::F32 => ir::types::F32, - WasmType::F64 => ir::types::F64, - WasmType::V128 => ir::types::I8X16, - WasmType::Ref(_) => reference_type, + WasmValType::I32 => ir::types::I32, + WasmValType::I64 => ir::types::I64, + WasmValType::F32 => ir::types::F32, + WasmValType::F64 => ir::types::F64, + WasmValType::V128 => ir::types::I8X16, + WasmValType::Ref(_) => self.pointer_type(), }) }; sig.params.extend(wasm.params().iter().map(&mut cvt)); @@ -905,8 +867,7 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment { self.func_bytecode_sizes .push(body.get_binary_reader().bytes_remaining()); let func = { - let mut func_environ = - DummyFuncEnvironment::new(&self.info, self.expected_reachability.clone()); + let mut func_environ = DummyFuncEnvironment::new(&self.info); let func_index = FuncIndex::new(self.get_num_func_imports() + self.info.function_bodies.len()); @@ -931,12 +892,6 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment { } fn wasm_features(&self) -> WasmFeatures { - WasmFeatures { - multi_value: true, - simd: true, - reference_types: true, - bulk_memory: true, - ..WasmFeatures::default() - } + WasmFeatures::default() } } diff --git a/cranelift/wasm/src/environ/mod.rs b/cranelift/wasm/src/environ/mod.rs index 34d930ac60d8..665a17a9d5f9 100644 --- a/cranelift/wasm/src/environ/mod.rs +++ b/cranelift/wasm/src/environ/mod.rs @@ -1,12 +1,8 @@ //! Support for configurable wasm translation. -mod dummy; #[macro_use] mod spec; -pub use crate::environ::dummy::{ - DummyEnvironment, DummyFuncEnvironment, DummyModuleInfo, ExpectedReachability, -}; pub use crate::environ::spec::{ FuncEnvironment, GlobalVariable, ModuleEnvironment, TargetEnvironment, }; diff --git a/cranelift/wasm/src/environ/spec.rs b/cranelift/wasm/src/environ/spec.rs index 5d7c82ff3cdd..61c272dbcf7b 100644 --- a/cranelift/wasm/src/environ/spec.rs +++ b/cranelift/wasm/src/environ/spec.rs @@ -8,11 +8,10 @@ use crate::state::FuncTranslationState; use crate::{ - DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Heap, HeapData, Memory, - MemoryIndex, SignatureIndex, Table, TableIndex, Tag, TagIndex, TypeConvert, TypeIndex, - WasmError, WasmFuncType, WasmHeapType, WasmResult, + DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Heap, HeapData, Memory, MemoryIndex, + Table, TableIndex, Tag, TagIndex, TypeConvert, TypeIndex, WasmError, WasmFuncType, + WasmHeapType, WasmResult, }; -use core::convert::From; use cranelift_codegen::cursor::FuncCursor; use cranelift_codegen::ir::immediates::Offset32; use cranelift_codegen::ir::{self, InstBuilder, Type}; @@ -22,6 +21,7 @@ use cranelift_frontend::FunctionBuilder; use std::boxed::Box; use std::string::ToString; use wasmparser::{FuncValidator, FunctionBody, Operator, ValidatorResources, WasmFeatures}; +use wasmtime_types::{ConstExpr, ModuleInternedTypeIndex}; /// The value of a WebAssembly global variable. #[derive(Clone, Copy)] @@ -51,6 +51,9 @@ 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 integer type to use for native pointers. /// /// This returns `I64` for 64-bit architectures and `I32` for 32-bit architectures. @@ -66,18 +69,9 @@ pub trait TargetEnvironment: TypeConvert { /// Get the Cranelift reference type to use for the given Wasm reference /// type. /// - /// By default, this returns `R64` for 64-bit architectures and `R32` for - /// 32-bit architectures. If you override this, then you should also - /// override `FuncEnvironment::{translate_ref_null, translate_ref_is_null}` - /// as well. - fn reference_type(&self, ty: WasmHeapType) -> ir::Type { - let _ = ty; - match self.pointer_type() { - ir::types::I32 => ir::types::R32, - ir::types::I64 => ir::types::R64, - _ => panic!("unsupported pointer type"), - } - } + /// Returns a pair of the CLIF reference type to use and a boolean that + /// describes whether the value should be included in GC stack maps or not. + fn reference_type(&self, ty: WasmHeapType) -> (ir::Type, bool); } /// Environment affecting the translation of a single WebAssembly function. @@ -92,6 +86,20 @@ pub trait FuncEnvironment: TargetEnvironment { signature.params[index].purpose == ir::ArgumentPurpose::Normal } + /// Does the given parameter require inclusion in stack maps? + fn param_needs_stack_map(&self, signature: &ir::Signature, index: usize) -> bool; + + /// Does the given result require inclusion in stack maps? + fn sig_ref_result_needs_stack_map(&self, sig_ref: ir::SigRef, index: usize) -> bool; + + /// Does the given result require inclusion in stack maps? + fn func_ref_result_needs_stack_map( + &self, + func: &ir::Function, + func_ref: ir::FuncRef, + index: usize, + ) -> bool; + /// Is the given return of the given function a wasm-level parameter, as /// opposed to a hidden parameter added for use by the implementation? fn is_wasm_return(&self, signature: &ir::Signature, index: usize) -> bool { @@ -132,12 +140,6 @@ pub trait FuncEnvironment: TargetEnvironment { /// The index space covers both imported and locally declared memories. fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult; - /// Set up the necessary preamble definitions in `func` to access the table identified - /// by `index`. - /// - /// The index space covers both imported and locally declared tables. - fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult; - /// Set up a signature definition in the preamble of `func` that can be used for an indirect /// call with signature `index`. /// @@ -196,16 +198,17 @@ pub trait FuncEnvironment: TargetEnvironment { /// The signature `sig_ref` was previously created by `make_indirect_sig()`. /// /// Return the call instruction whose results are the WebAssembly return values. + /// Returns `None` if this statically traps instead of creating a call + /// instruction. fn translate_call_indirect( &mut self, builder: &mut FunctionBuilder, table_index: TableIndex, - table: ir::Table, sig_index: TypeIndex, sig_ref: ir::SigRef, callee: ir::Value, call_args: &[ir::Value], - ) -> WasmResult; + ) -> WasmResult>; /// Translate a `return_call` WebAssembly instruction at the builder's /// current position. @@ -240,7 +243,6 @@ pub trait FuncEnvironment: TargetEnvironment { &mut self, builder: &mut FunctionBuilder, table_index: TableIndex, - table: ir::Table, sig_index: TypeIndex, sig_ref: ir::SigRef, callee: ir::Value, @@ -362,19 +364,14 @@ pub trait FuncEnvironment: TargetEnvironment { fn translate_data_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>; /// Translate a `table.size` WebAssembly instruction. - fn translate_table_size( - &mut self, - pos: FuncCursor, - index: TableIndex, - table: ir::Table, - ) -> WasmResult; + fn translate_table_size(&mut self, pos: FuncCursor, index: TableIndex) + -> WasmResult; /// Translate a `table.grow` WebAssembly instruction. fn translate_table_grow( &mut self, pos: FuncCursor, table_index: TableIndex, - table: ir::Table, delta: ir::Value, init_value: ir::Value, ) -> WasmResult; @@ -384,7 +381,6 @@ pub trait FuncEnvironment: TargetEnvironment { &mut self, builder: &mut FunctionBuilder, table_index: TableIndex, - table: ir::Table, index: ir::Value, ) -> WasmResult; @@ -393,7 +389,6 @@ pub trait FuncEnvironment: TargetEnvironment { &mut self, builder: &mut FunctionBuilder, table_index: TableIndex, - table: ir::Table, value: ir::Value, index: ir::Value, ) -> WasmResult<()>; @@ -403,9 +398,7 @@ pub trait FuncEnvironment: TargetEnvironment { &mut self, pos: FuncCursor, dst_table_index: TableIndex, - dst_table: ir::Table, src_table_index: TableIndex, - src_table: ir::Table, dst: ir::Value, src: ir::Value, len: ir::Value, @@ -427,7 +420,6 @@ pub trait FuncEnvironment: TargetEnvironment { pos: FuncCursor, seg_index: u32, table_index: TableIndex, - table: ir::Table, dst: ir::Value, src: ir::Value, len: ir::Value, @@ -437,39 +429,11 @@ pub trait FuncEnvironment: TargetEnvironment { fn translate_elem_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>; /// Translate a `ref.null T` WebAssembly instruction. - /// - /// By default, translates into a null reference type. - /// - /// Override this if you don't use Cranelift reference types for all Wasm - /// reference types (e.g. you use a raw pointer for `funcref`s) or if the - /// null sentinel is not a null reference type pointer for your type. If you - /// override this method, then you should also override - /// `translate_ref_is_null` as well. - fn translate_ref_null( - &mut self, - mut pos: FuncCursor, - ty: WasmHeapType, - ) -> WasmResult { - let _ = ty; - Ok(pos.ins().null(self.reference_type(ty))) - } + fn translate_ref_null(&mut self, pos: FuncCursor, ty: WasmHeapType) -> WasmResult; /// Translate a `ref.is_null` WebAssembly instruction. - /// - /// By default, assumes that `value` is a Cranelift reference type, and that - /// a null Cranelift reference type is the null value for all Wasm reference - /// types. - /// - /// If you override this method, you probably also want to override - /// `translate_ref_null` as well. - fn translate_ref_is_null( - &mut self, - mut pos: FuncCursor, - value: ir::Value, - ) -> WasmResult { - let is_null = pos.ins().is_null(value); - Ok(pos.ins().uextend(ir::types::I32, is_null)) - } + fn translate_ref_is_null(&mut self, pos: FuncCursor, value: ir::Value) + -> WasmResult; /// Translate a `ref.func` WebAssembly instruction. fn translate_ref_func( @@ -482,7 +446,7 @@ pub trait FuncEnvironment: TargetEnvironment { /// that is custom. fn translate_custom_global_get( &mut self, - pos: FuncCursor, + builder: &mut FunctionBuilder, global_index: GlobalIndex, ) -> WasmResult; @@ -490,7 +454,7 @@ pub trait FuncEnvironment: TargetEnvironment { /// that is custom. fn translate_custom_global_set( &mut self, - pos: FuncCursor, + builder: &mut FunctionBuilder, global_index: GlobalIndex, val: ir::Value, ) -> WasmResult<()>; @@ -535,6 +499,15 @@ pub trait FuncEnvironment: TargetEnvironment { count: ir::Value, ) -> WasmResult; + /// Translate an `i32` value into an `i31ref`. + fn translate_ref_i31(&mut self, pos: FuncCursor, val: ir::Value) -> WasmResult; + + /// Sign-extend an `i31ref` into an `i32`. + fn translate_i31_get_s(&mut self, pos: FuncCursor, i31ref: ir::Value) -> WasmResult; + + /// Zero-extend an `i31ref` into an `i32`. + fn translate_i31_get_u(&mut self, pos: FuncCursor, i31ref: ir::Value) -> WasmResult; + /// Emit code at the beginning of every wasm loop. /// /// This can be used to insert explicit interrupt or safepoint checking at @@ -701,7 +674,7 @@ pub trait ModuleEnvironment<'data>: TypeConvert { /// Translates a type index to its signature index, only called for type /// indices which point to functions. - fn type_to_signature(&self, index: TypeIndex) -> WasmResult { + fn type_to_signature(&self, index: TypeIndex) -> WasmResult { let _ = index; Err(WasmError::Unsupported("module linking".to_string())) } @@ -806,7 +779,7 @@ pub trait ModuleEnvironment<'data>: TypeConvert { } /// Declares a global to the environment. - fn declare_global(&mut self, global: Global, init: GlobalInit) -> WasmResult<()>; + fn declare_global(&mut self, global: Global, init: ConstExpr) -> WasmResult<()>; /// Provides the number of exports up front. By default this does nothing, but /// implementations can use this to preallocate memory if desired. diff --git a/cranelift/wasm/src/func_translator.rs b/cranelift/wasm/src/func_translator.rs index ed666cdef982..d55d7fb38662 100644 --- a/cranelift/wasm/src/func_translator.rs +++ b/cranelift/wasm/src/func_translator.rs @@ -13,7 +13,7 @@ use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel}; use cranelift_codegen::timing; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; -use wasmparser::{self, BinaryReader, FuncValidator, FunctionBody, WasmModuleResources}; +use wasmparser::{BinaryReader, FuncValidator, FunctionBody, WasmModuleResources}; /// WebAssembly to Cranelift IR function translator. /// @@ -40,14 +40,7 @@ impl FuncTranslator { &mut self.func_ctx } - /// Translate a binary WebAssembly function. - /// - /// The `code` slice contains the binary WebAssembly *function code* as it appears in the code - /// section of a WebAssembly module, not including the initial size of the function code. The - /// slice is expected to contain two parts: - /// - /// - The declaration of *locals*, and - /// - The function *body* as an expression. + /// Translate a binary WebAssembly function from a `FunctionBody`. /// /// See [the WebAssembly specification][wasm]. /// @@ -57,24 +50,6 @@ impl FuncTranslator { /// and `func.name` fields. The signature may contain special-purpose arguments which are not /// regarded as WebAssembly local variables. Any signature arguments marked as /// `ArgumentPurpose::Normal` are made accessible as WebAssembly local variables. - /// - pub fn translate( - &mut self, - validator: &mut FuncValidator, - code: &[u8], - code_offset: usize, - func: &mut ir::Function, - environ: &mut FE, - ) -> WasmResult<()> { - self.translate_body( - validator, - FunctionBody::new(code_offset, code), - func, - environ, - ) - } - - /// Translate a binary WebAssembly function from a `FunctionBody`. pub fn translate_body( &mut self, validator: &mut FuncValidator, @@ -141,6 +116,10 @@ fn declare_wasm_parameters( builder.declare_var(local, param_type.value_type); next_local += 1; + if environ.param_needs_stack_map(&builder.func.signature, i) { + builder.declare_var_needs_stack_map(local); + } + let param_value = builder.block_params(entry_block)[i]; builder.def_var(local, param_value); } @@ -192,45 +171,53 @@ fn declare_locals( ) -> WasmResult<()> { // All locals are initialized to 0. use wasmparser::ValType::*; - let (ty, init) = match wasm_type { + let (ty, init, needs_stack_map) = match wasm_type { I32 => ( ir::types::I32, Some(builder.ins().iconst(ir::types::I32, 0)), + false, ), I64 => ( ir::types::I64, Some(builder.ins().iconst(ir::types::I64, 0)), + false, ), F32 => ( ir::types::F32, Some(builder.ins().f32const(ir::immediates::Ieee32::with_bits(0))), + false, ), F64 => ( ir::types::F64, Some(builder.ins().f64const(ir::immediates::Ieee64::with_bits(0))), + false, ), V128 => { let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into()); ( ir::types::I8X16, Some(builder.ins().vconst(ir::types::I8X16, constant_handle)), + false, ) } Ref(rt) => { let hty = environ.convert_heap_type(rt.heap_type()); - let ty = environ.reference_type(hty); + let (ty, needs_stack_map) = environ.reference_type(hty); let init = if rt.is_nullable() { Some(environ.translate_ref_null(builder.cursor(), hty)?) } else { None }; - (ty, init) + (ty, init, needs_stack_map) } }; for _ in 0..count { let local = Variable::new(*next_local); builder.declare_var(local, ty); + if needs_stack_map { + builder.declare_var_needs_stack_map(local); + } if let Some(init) = init { builder.def_var(local, init); builder.set_val_label(init, ValueLabel::new(*next_local)); @@ -294,139 +281,3 @@ fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc { // This will wrap around if byte code is larger than 4 GB. ir::SourceLoc::new(reader.original_position() as u32) } - -#[cfg(test)] -mod tests { - use super::FuncTranslator; - use crate::environ::DummyEnvironment; - use cranelift_codegen::ir::types::I32; - use cranelift_codegen::{ir, isa, settings, Context}; - use log::debug; - use target_lexicon::PointerWidth; - use wasmparser::{ - FuncValidator, FunctionBody, Parser, ValidPayload, Validator, ValidatorResources, - }; - - #[test] - fn small1() { - // Implicit return. - let wasm = wat::parse_str( - " - (module - (func $small2 (param i32) (result i32) - (i32.add (local.get 0) (i32.const 1)) - ) - ) - ", - ) - .unwrap(); - - let mut trans = FuncTranslator::new(); - let flags = settings::Flags::new(settings::builder()); - let runtime = DummyEnvironment::new(isa::TargetFrontendConfig { - default_call_conv: isa::CallConv::Fast, - pointer_width: PointerWidth::U64, - }); - - let mut ctx = Context::new(); - - ctx.func.name = ir::UserFuncName::testcase("small1"); - ctx.func.signature.params.push(ir::AbiParam::new(I32)); - ctx.func.signature.returns.push(ir::AbiParam::new(I32)); - - let (body, mut validator) = extract_func(&wasm); - trans - .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env()) - .unwrap(); - debug!("{}", ctx.func.display()); - ctx.verify(&flags).unwrap(); - } - - #[test] - fn small2() { - // Same as above, but with an explicit return instruction. - let wasm = wat::parse_str( - " - (module - (func $small2 (param i32) (result i32) - (return (i32.add (local.get 0) (i32.const 1))) - ) - ) - ", - ) - .unwrap(); - - let mut trans = FuncTranslator::new(); - let flags = settings::Flags::new(settings::builder()); - let runtime = DummyEnvironment::new(isa::TargetFrontendConfig { - default_call_conv: isa::CallConv::Fast, - pointer_width: PointerWidth::U64, - }); - - let mut ctx = Context::new(); - - ctx.func.name = ir::UserFuncName::testcase("small2"); - ctx.func.signature.params.push(ir::AbiParam::new(I32)); - ctx.func.signature.returns.push(ir::AbiParam::new(I32)); - - let (body, mut validator) = extract_func(&wasm); - trans - .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env()) - .unwrap(); - debug!("{}", ctx.func.display()); - ctx.verify(&flags).unwrap(); - } - - #[test] - fn infloop() { - // An infinite loop, no return instructions. - let wasm = wat::parse_str( - " - (module - (func $infloop (result i32) - (local i32) - (loop (result i32) - (i32.add (local.get 0) (i32.const 1)) - (local.set 0) - (br 0) - ) - ) - ) - ", - ) - .unwrap(); - - let mut trans = FuncTranslator::new(); - let flags = settings::Flags::new(settings::builder()); - let runtime = DummyEnvironment::new(isa::TargetFrontendConfig { - default_call_conv: isa::CallConv::Fast, - pointer_width: PointerWidth::U64, - }); - - let mut ctx = Context::new(); - - ctx.func.name = ir::UserFuncName::testcase("infloop"); - ctx.func.signature.returns.push(ir::AbiParam::new(I32)); - - let (body, mut validator) = extract_func(&wasm); - trans - .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env()) - .unwrap(); - debug!("{}", ctx.func.display()); - ctx.verify(&flags).unwrap(); - } - - fn extract_func(wat: &[u8]) -> (FunctionBody<'_>, FuncValidator) { - let mut validator = Validator::new(); - for payload in Parser::new(0).parse_all(wat) { - match validator.payload(&payload.unwrap()).unwrap() { - ValidPayload::Func(validator, body) => { - let validator = validator.into_validator(Default::default()); - return (body, validator); - } - _ => {} - } - } - panic!("failed to find function"); - } -} diff --git a/cranelift/wasm/src/heap.rs b/cranelift/wasm/src/heap.rs index b253c62dcee0..c8adc66727e3 100644 --- a/cranelift/wasm/src/heap.rs +++ b/cranelift/wasm/src/heap.rs @@ -1,6 +1,6 @@ //! Heaps to implement WebAssembly linear memories. -use cranelift_codegen::ir::{GlobalValue, Type}; +use cranelift_codegen::ir::{GlobalValue, MemoryType, Type}; use cranelift_entity::entity_impl; /// An opaque reference to a [`HeapData`][crate::HeapData]. @@ -74,6 +74,11 @@ pub struct HeapData { /// don't need bounds checking. pub min_size: u64, + /// The maximum heap size in bytes. + /// + /// Heap accesses larger than this will always trap. + pub max_size: Option, + /// Size in bytes of the offset-guard pages following the heap. pub offset_guard_size: u64, @@ -82,6 +87,12 @@ pub struct HeapData { /// The index type for the heap. pub index_type: Type, + + /// The memory type for the pointed-to memory, if using proof-carrying code. + pub memory_type: Option, + + /// The log2 of this memory's page size. + pub page_size_log2: u8, } /// Style of heap including style-specific information. diff --git a/cranelift/wasm/src/lib.rs b/cranelift/wasm/src/lib.rs index ecb9f8f391e6..bd3092fa3418 100644 --- a/cranelift/wasm/src/lib.rs +++ b/cranelift/wasm/src/lib.rs @@ -4,14 +4,9 @@ //! [`ModuleEnvironment`](trait.ModuleEnvironment.html) //! trait to deal with tables, globals and linear memory. //! -//! The crate provides a `DummyEnvironment` struct that will allow to translate the code of the -//! functions but will fail at execution. -//! //! The main function of this module is [`translate_module`](fn.translate_module.html). -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces)] -#![cfg_attr(feature = "std", deny(unstable_features))] +#![deny(missing_docs)] #![no_std] #[cfg(not(feature = "std"))] @@ -41,16 +36,15 @@ mod heap; mod module_translator; mod sections_translator; mod state; +mod table; mod translation_utils; -pub use crate::environ::{ - DummyEnvironment, DummyFuncEnvironment, DummyModuleInfo, ExpectedReachability, FuncEnvironment, - GlobalVariable, ModuleEnvironment, TargetEnvironment, -}; +pub use crate::environ::{FuncEnvironment, GlobalVariable, ModuleEnvironment, TargetEnvironment}; pub use crate::func_translator::FuncTranslator; pub use crate::heap::{Heap, HeapData, HeapStyle}; pub use crate::module_translator::translate_module; pub use crate::state::FuncTranslationState; +pub use crate::table::{TableData, TableSize}; pub use crate::translation_utils::*; pub use cranelift_frontend::FunctionBuilder; pub use wasmtime_types::*; diff --git a/cranelift/wasm/src/module_translator.rs b/cranelift/wasm/src/module_translator.rs index 6da7d0b5b0fb..e78c90d49cd0 100644 --- a/cranelift/wasm/src/module_translator.rs +++ b/cranelift/wasm/src/module_translator.rs @@ -3,13 +3,13 @@ use crate::environ::ModuleEnvironment; use crate::sections_translator::{ parse_data_section, parse_element_section, parse_export_section, parse_function_section, - parse_global_section, parse_import_section, parse_memory_section, parse_name_section, - parse_start_section, parse_table_section, parse_tag_section, parse_type_section, + parse_global_section, parse_import_section, parse_memory_section, parse_start_section, + parse_table_section, parse_tag_section, parse_type_section, }; use crate::WasmResult; use cranelift_codegen::timing; use std::prelude::v1::*; -use wasmparser::{NameSectionReader, Parser, Payload, Validator}; +use wasmparser::{Parser, Payload, Validator}; /// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR /// [`Function`](cranelift_codegen::ir::Function). @@ -107,19 +107,11 @@ pub fn translate_module<'data>( environ.reserve_passive_data(count)?; } - Payload::CustomSection(s) if s.name() == "name" => { - let result = - parse_name_section(NameSectionReader::new(s.data(), s.data_offset()), environ); - if let Err(e) = result { - log::warn!("failed to parse name section {:?}", e); - } - } - Payload::CustomSection(s) => environ.custom_section(s.name(), s.data())?, other => { validator.payload(&other)?; - panic!("unimplemented section {:?}", other); + panic!("unimplemented section {other:?}"); } } } diff --git a/cranelift/wasm/src/sections_translator.rs b/cranelift/wasm/src/sections_translator.rs index e38b1327787b..e033f13db0be 100644 --- a/cranelift/wasm/src/sections_translator.rs +++ b/cranelift/wasm/src/sections_translator.rs @@ -10,28 +10,20 @@ use crate::environ::ModuleEnvironment; use crate::wasm_unsupported; use crate::{ - DataIndex, ElemIndex, FuncIndex, GlobalIndex, GlobalInit, Memory, MemoryIndex, TableIndex, Tag, - TagIndex, TypeIndex, WasmError, WasmResult, + DataIndex, ElemIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex, Tag, TagIndex, + TypeIndex, WasmError, WasmResult, }; use cranelift_entity::packed_option::ReservedValue; use cranelift_entity::EntityRef; use std::boxed::Box; use std::vec::Vec; use wasmparser::{ - self, Data, DataKind, DataSectionReader, Element, ElementItems, ElementKind, - ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader, - GlobalSectionReader, ImportSectionReader, MemorySectionReader, MemoryType, NameSectionReader, - Naming, Operator, TableSectionReader, TagSectionReader, TagType, TypeRef, TypeSectionReader, + Data, DataKind, DataSectionReader, Element, ElementItems, ElementKind, ElementSectionReader, + Export, ExportSectionReader, ExternalKind, FunctionSectionReader, GlobalSectionReader, + ImportSectionReader, MemorySectionReader, Operator, TableSectionReader, TagSectionReader, + TagType, TypeRef, TypeSectionReader, }; - -fn memory(ty: MemoryType) -> Memory { - Memory { - minimum: ty.initial, - maximum: ty.maximum, - shared: ty.shared, - memory64: ty.memory64, - } -} +use wasmtime_types::ConstExpr; fn tag(e: TagType) -> Tag { match e.kind { @@ -74,7 +66,7 @@ pub fn parse_import_section<'data>( )?; } TypeRef::Memory(ty) => { - environ.declare_memory_import(memory(ty), import.module, import.name)?; + environ.declare_memory_import(ty.into(), import.module, import.name)?; } TypeRef::Tag(e) => { environ.declare_tag_import(tag(e), import.module, import.name)?; @@ -84,7 +76,7 @@ pub fn parse_import_section<'data>( environ.declare_global_import(ty, import.module, import.name)?; } TypeRef::Table(ty) => { - let ty = environ.convert_table_type(&ty); + let ty = environ.convert_table_type(&ty)?; environ.declare_table_import(ty, import.module, import.name)?; } } @@ -123,7 +115,7 @@ pub fn parse_table_section( environ.reserve_tables(tables.count())?; for entry in tables { - let ty = environ.convert_table_type(&entry?.ty); + let ty = environ.convert_table_type(&entry?.ty)?; environ.declare_table(ty)?; } @@ -138,8 +130,7 @@ pub fn parse_memory_section( environ.reserve_memories(memories.count())?; for entry in memories { - let memory = memory(entry?); - environ.declare_memory(memory)?; + environ.declare_memory(entry?.into())?; } Ok(()) @@ -169,29 +160,7 @@ pub fn parse_global_section( for entry in globals { let wasmparser::Global { ty, init_expr } = entry?; - let mut init_expr_reader = init_expr.get_binary_reader(); - let initializer = match init_expr_reader.read_operator()? { - Operator::I32Const { value } => GlobalInit::I32Const(value), - Operator::I64Const { value } => GlobalInit::I64Const(value), - Operator::F32Const { value } => GlobalInit::F32Const(value.bits()), - Operator::F64Const { value } => GlobalInit::F64Const(value.bits()), - Operator::V128Const { value } => { - GlobalInit::V128Const(u128::from_le_bytes(*value.bytes())) - } - Operator::RefNull { hty: _ } => GlobalInit::RefNullConst, - Operator::RefFunc { function_index } => { - GlobalInit::RefFunc(FuncIndex::from_u32(function_index)) - } - Operator::GlobalGet { global_index } => { - GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index)) - } - ref s => { - return Err(wasm_unsupported!( - "unsupported init expr in global section: {:?}", - s - )); - } - }; + let (initializer, _escaped) = ConstExpr::from_wasmparser(init_expr)?; let ty = environ.convert_global_type(&ty); environ.declare_global(ty, initializer)?; } @@ -251,8 +220,7 @@ fn read_elems(items: &ElementItems) -> WasmResult> { Operator::RefFunc { function_index } => FuncIndex::from_u32(function_index), s => { return Err(WasmError::Unsupported(format!( - "unsupported init expr in element section: {:?}", - s + "unsupported init expr in element section: {s:?}" ))); } }; @@ -362,47 +330,3 @@ pub fn parse_data_section<'data>( Ok(()) } - -/// Parses the Name section of the wasm module. -pub fn parse_name_section<'data>( - names: NameSectionReader<'data>, - environ: &mut dyn ModuleEnvironment<'data>, -) -> WasmResult<()> { - for subsection in names { - match subsection? { - wasmparser::Name::Function(names) => { - for name in names { - let Naming { index, name } = name?; - // We reserve `u32::MAX` for our own use in cranelift-entity. - if index != u32::max_value() { - environ.declare_func_name(FuncIndex::from_u32(index), name); - } - } - } - wasmparser::Name::Module { name, .. } => { - environ.declare_module_name(name); - } - wasmparser::Name::Local(reader) => { - for f in reader { - let f = f?; - if f.index == u32::max_value() { - continue; - } - for name in f.names { - let Naming { index, name } = name?; - environ.declare_local_name(FuncIndex::from_u32(f.index), index, name) - } - } - } - wasmparser::Name::Label(_) - | wasmparser::Name::Type(_) - | wasmparser::Name::Table(_) - | wasmparser::Name::Global(_) - | wasmparser::Name::Memory(_) - | wasmparser::Name::Element(_) - | wasmparser::Name::Data(_) - | wasmparser::Name::Unknown { .. } => {} - } - } - Ok(()) -} diff --git a/cranelift/wasm/src/state.rs b/cranelift/wasm/src/state.rs index 3d775e05ec1a..d788157f84c7 100644 --- a/cranelift/wasm/src/state.rs +++ b/cranelift/wasm/src/state.rs @@ -4,7 +4,7 @@ //! value and control stacks during the translation of a single function. use crate::environ::{FuncEnvironment, GlobalVariable}; -use crate::{FuncIndex, GlobalIndex, Heap, MemoryIndex, TableIndex, TypeIndex, WasmResult}; +use crate::{FuncIndex, GlobalIndex, Heap, MemoryIndex, TypeIndex, WasmResult}; use crate::{HashMap, Occupied, Vacant}; use cranelift_codegen::ir::{self, Block, Inst, Value}; use std::vec::Vec; @@ -194,7 +194,7 @@ impl ControlStackFrame { // (see also `FuncTranslationState::push_if`). // Yet, the original_stack_size member accounts for them only once, so that the else // block can see the same number of parameters as the consequent block. As a matter of - // fact, we need to substract an extra number of parameter values for if blocks. + // fact, we need to subtract an extra number of parameter values for if blocks. let num_duplicated_params = match self { &ControlStackFrame::If { num_param_values, .. @@ -229,9 +229,6 @@ pub struct FuncTranslationState { // Map of heaps that have been created by `FuncEnvironment::make_heap`. memory_to_heap: HashMap, - // Map of tables that have been created by `FuncEnvironment::make_table`. - pub(crate) tables: HashMap, - // Map of indirect call signatures that have been created by // `FuncEnvironment::make_indirect_sig()`. // Stores both the signature reference and the number of WebAssembly arguments @@ -261,7 +258,6 @@ impl FuncTranslationState { reachable: true, globals: HashMap::new(), memory_to_heap: HashMap::new(), - tables: HashMap::new(), signatures: HashMap::new(), functions: HashMap::new(), } @@ -273,7 +269,6 @@ impl FuncTranslationState { self.reachable = true; self.globals.clear(); self.memory_to_heap.clear(); - self.tables.clear(); self.signatures.clear(); self.functions.clear(); } @@ -334,7 +329,7 @@ impl FuncTranslationState { (v1, v2, v3) } - /// Helper to ensure the the stack size is at least as big as `n`; note that due to + /// Helper to ensure the stack size is at least as big as `n`; note that due to /// `debug_assert` this will not execute in non-optimized builds. #[inline] fn ensure_length_is_at_least(&self, n: usize) { @@ -472,21 +467,6 @@ impl FuncTranslationState { } } - /// Get the `Table` reference that should be used to access table `index`. - /// Create the reference if necessary. - pub(crate) fn get_or_create_table( - &mut self, - func: &mut ir::Function, - index: u32, - environ: &mut FE, - ) -> WasmResult { - let index = TableIndex::from_u32(index); - match self.tables.entry(index) { - Occupied(entry) => Ok(*entry.get()), - Vacant(entry) => Ok(*entry.insert(environ.make_table(func, index)?)), - } - } - /// Get the `SigRef` reference that should be used to make an indirect call with signature /// `index`. Also return the number of WebAssembly arguments in the signature. /// diff --git a/cranelift/wasm/src/table.rs b/cranelift/wasm/src/table.rs new file mode 100644 index 000000000000..bb5466670696 --- /dev/null +++ b/cranelift/wasm/src/table.rs @@ -0,0 +1,104 @@ +use cranelift_codegen::cursor::FuncCursor; +use cranelift_codegen::ir::{self, condcodes::IntCC, immediates::Imm64, InstBuilder}; +use cranelift_frontend::FunctionBuilder; + +/// Size of a WebAssembly table, in elements. +#[derive(Clone)] +pub enum TableSize { + /// Non-resizable table. + Static { + /// Non-resizable tables have a constant size known at compile time. + bound: u32, + }, + /// Resizable table. + Dynamic { + /// Resizable tables declare a Cranelift global value to load the + /// current size from. + bound_gv: ir::GlobalValue, + }, +} + +impl TableSize { + /// Get a CLIF value representing the current bounds of this table. + pub fn bound(&self, mut pos: FuncCursor, index_ty: ir::Type) -> ir::Value { + match *self { + TableSize::Static { bound } => pos.ins().iconst(index_ty, Imm64::new(i64::from(bound))), + TableSize::Dynamic { bound_gv } => pos.ins().global_value(index_ty, bound_gv), + } + } +} + +/// An implementation of a WebAssembly table. +#[derive(Clone)] +pub struct TableData { + /// Global value giving the address of the start of the table. + pub base_gv: ir::GlobalValue, + + /// The size of the table, in elements. + pub bound: TableSize, + + /// The size of a table element, in bytes. + pub element_size: u32, +} + +impl TableData { + /// Return a CLIF value containing a native pointer to the beginning of the + /// given index within this table. + pub fn prepare_table_addr( + &self, + pos: &mut FunctionBuilder, + mut index: ir::Value, + addr_ty: ir::Type, + enable_table_access_spectre_mitigation: bool, + ) -> (ir::Value, ir::MemFlags) { + let index_ty = pos.func.dfg.value_type(index); + + // Start with the bounds check. Trap if `index + 1 > bound`. + let bound = self.bound.bound(pos.cursor(), index_ty); + + // `index > bound - 1` is the same as `index >= bound`. + let oob = pos + .ins() + .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound); + + if !enable_table_access_spectre_mitigation { + pos.ins().trapnz(oob, ir::TrapCode::TableOutOfBounds); + } + + // Convert `index` to `addr_ty`. + if index_ty != addr_ty { + index = pos.ins().uextend(addr_ty, index); + } + + // Add the table base address base + let base = pos.ins().global_value(addr_ty, self.base_gv); + + let element_size = self.element_size; + let offset = if element_size == 1 { + index + } else if element_size.is_power_of_two() { + pos.ins() + .ishl_imm(index, i64::from(element_size.trailing_zeros())) + } else { + pos.ins().imul_imm(index, element_size as i64) + }; + + let element_addr = pos.ins().iadd(base, offset); + + let base_flags = ir::MemFlags::new() + .with_aligned() + .with_alias_region(Some(ir::AliasRegion::Table)); + if enable_table_access_spectre_mitigation { + // Short-circuit the computed table element address to a null pointer + // when out-of-bounds. The consumer of this address will trap when + // trying to access it. + let zero = pos.ins().iconst(addr_ty, 0); + ( + pos.ins().select_spectre_guard(oob, zero, element_addr), + base_flags.with_trap_code(Some(ir::TrapCode::TableOutOfBounds)), + ) + } else { + (element_addr, base_flags.with_trap_code(None)) + } + } +} diff --git a/cranelift/wasm/src/translation_utils.rs b/cranelift/wasm/src/translation_utils.rs index 8a6ceb2c73fa..588cfa5c3fce 100644 --- a/cranelift/wasm/src/translation_utils.rs +++ b/cranelift/wasm/src/translation_utils.rs @@ -4,7 +4,7 @@ use crate::WasmResult; use core::u32; use cranelift_codegen::ir; use cranelift_frontend::FunctionBuilder; -use wasmparser::{FuncValidator, WasmFuncType, WasmModuleResources}; +use wasmparser::{FuncValidator, WasmModuleResources}; /// Get the parameter and result types for the given Wasm blocktype. pub fn blocktype_params_results<'a, T>( @@ -18,30 +18,24 @@ where T: WasmModuleResources, { return Ok(match ty { - wasmparser::BlockType::Empty => { - let params: &'static [wasmparser::ValType] = &[]; - let results: std::vec::Vec = vec![]; - ( - itertools::Either::Left(params.iter().copied()), - itertools::Either::Left(results.into_iter()), - ) - } - wasmparser::BlockType::Type(ty) => { - let params: &'static [wasmparser::ValType] = &[]; - let results: std::vec::Vec = vec![ty.clone()]; - ( - itertools::Either::Left(params.iter().copied()), - itertools::Either::Left(results.into_iter()), - ) - } + wasmparser::BlockType::Empty => ( + itertools::Either::Left(std::iter::empty()), + itertools::Either::Left(None.into_iter()), + ), + wasmparser::BlockType::Type(ty) => ( + itertools::Either::Left(std::iter::empty()), + itertools::Either::Left(Some(ty).into_iter()), + ), wasmparser::BlockType::FuncType(ty_index) => { let ty = validator .resources() - .func_type_at(ty_index) - .expect("should be valid"); + .sub_type_at(ty_index) + .expect("should be valid") + .unwrap_func(); + ( - itertools::Either::Right(ty.inputs()), - itertools::Either::Right(ty.outputs()), + itertools::Either::Right(ty.params().iter().copied()), + itertools::Either::Right(ty.results().iter().copied()), ) } }); @@ -70,7 +64,11 @@ pub fn block_with_params( } wasmparser::ValType::Ref(rt) => { let hty = environ.convert_heap_type(rt.heap_type()); - builder.append_block_param(block, environ.reference_type(hty)); + let (ty, needs_stack_map) = environ.reference_type(hty); + let val = builder.append_block_param(block, ty); + if needs_stack_map { + builder.declare_value_needs_stack_map(val); + } } wasmparser::ValType::V128 => { builder.append_block_param(block, ir::types::I8X16); diff --git a/cranelift/wasm/tests/wasm_testsuite.rs b/cranelift/wasm/tests/wasm_testsuite.rs deleted file mode 100644 index 311b879b0b55..000000000000 --- a/cranelift/wasm/tests/wasm_testsuite.rs +++ /dev/null @@ -1,153 +0,0 @@ -use cranelift_codegen::isa::{CallConv, TargetFrontendConfig}; -use cranelift_codegen::print_errors::pretty_verifier_error; -use cranelift_codegen::settings::{self, Flags}; -use cranelift_codegen::verifier; -use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex}; -use std::fs; -use std::path::Path; -use target_lexicon::PointerWidth; - -#[test] -fn testsuite() { - let mut paths: Vec<_> = fs::read_dir("./wasmtests") - .unwrap() - .map(|r| r.unwrap()) - .filter(|p| { - // Ignore files starting with `.`, which could be editor temporary files - if let Some(stem) = p.path().file_stem() { - if let Some(stemstr) = stem.to_str() { - return !stemstr.starts_with('.'); - } - } - false - }) - .collect(); - paths.sort_by_key(|dir| dir.path()); - let flags = Flags::new(settings::builder()); - for path in paths { - let path = path.path(); - println!("=== {} ===", path.display()); - let data = read_module(&path); - handle_module(data, &flags); - } -} - -#[test] -fn use_name_section() { - let data = wat::parse_str( - r#" - (module $module_name - (func $func_name (local $loc_name i32) - ) - )"#, - ) - .unwrap(); - - let mut dummy_environ = DummyEnvironment::new(TargetFrontendConfig { - default_call_conv: CallConv::SystemV, - pointer_width: PointerWidth::U32, - }); - - translate_module(data.as_ref(), &mut dummy_environ).unwrap(); - - assert_eq!( - dummy_environ.get_func_name(FuncIndex::from_u32(0)).unwrap(), - "func_name" - ); -} - -fn read_module(path: &Path) -> Vec { - match path.extension() { - None => { - panic!("the file extension is not wasm or wat"); - } - Some(ext) => match ext.to_str() { - Some("wasm") => std::fs::read(path).expect("error reading wasm file"), - Some("wat") => wat::parse_file(path) - .map_err(|e| e.to_string()) - .expect("failed to parse wat"), - None | Some(&_) => panic!("the file extension for {:?} is not wasm or wat", path), - }, - } -} - -fn handle_module(data: Vec, flags: &Flags) { - let mut dummy_environ = DummyEnvironment::new(TargetFrontendConfig { - default_call_conv: CallConv::SystemV, - pointer_width: PointerWidth::U64, - }); - - translate_module(&data, &mut dummy_environ).unwrap(); - - for func in dummy_environ.info.function_bodies.values() { - verifier::verify_function(func, flags) - .map_err(|errors| panic!("{}", pretty_verifier_error(func, None, errors))) - .unwrap(); - } -} - -#[test] -fn reachability_is_correct() { - let tests = vec![ - ( - r#" - (module (func (param i32) - (loop - (block - local.get 0 - br_if 0 - br 1))))"#, - vec![ - (true, true), // Loop - (true, true), // Block - (true, true), // LocalGet - (true, true), // BrIf - (true, false), // Br - (false, true), // End - (true, true), // End - (true, true), // End - ], - ), - ( - r#" - (module (func (param i32) - (loop - (block - br 1 - nop))))"#, - vec![ - (true, true), // Loop - (true, true), // Block - (true, false), // Br - (false, false), // Nop - (false, false), // Nop - (false, false), // Nop - (false, false), // End - ], - ), - ( - r#" - (module (func (param i32) (result i32) - i32.const 1 - return - i32.const 42))"#, - vec![ - (true, true), // I32Const - (true, false), // Return - (false, false), // I32Const - (false, false), // End - ], - ), - ]; - - for (wat, expected_reachability) in tests { - println!("testing wat:\n{}", wat); - let mut env = DummyEnvironment::new(TargetFrontendConfig { - default_call_conv: CallConv::SystemV, - pointer_width: PointerWidth::U64, - }); - env.test_expected_reachability(expected_reachability); - let data = wat::parse_str(wat).unwrap(); - translate_module(data.as_ref(), &mut env).unwrap(); - } -} diff --git a/cranelift/wasm/wasmtests/arith.wat b/cranelift/wasm/wasmtests/arith.wat deleted file mode 100644 index 80005a6742de..000000000000 --- a/cranelift/wasm/wasmtests/arith.wat +++ /dev/null @@ -1,13 +0,0 @@ -(module - (memory 1) - (func $main (local i32) - (local.set 0 (i32.sub (i32.const 4) (i32.const 4))) - (if - (local.get 0) - (then unreachable) - (else (drop (i32.mul (i32.const 6) (local.get 0)))) - ) - ) - (start $main) - (data (i32.const 0) "abcdefgh") -) diff --git a/cranelift/wasm/wasmtests/br_table.wat b/cranelift/wasm/wasmtests/br_table.wat deleted file mode 100644 index 75444fa49c4c..000000000000 --- a/cranelift/wasm/wasmtests/br_table.wat +++ /dev/null @@ -1,30 +0,0 @@ -(module - (func (result i32) - (block (result i32) - (block (result i32) - (block (result i32) - (br_table 0 1 2 3 (i32.const 42) (i32.const 0)) - ) - ) - ) - ) - (func (result i32) - (block (result i32) - (block (result i32) - (block (result i32) - (br_table 3 2 1 0 (i32.const 42) (i32.const 0)) - ) - ) - ) - ) - (func (result i32) - (block (result i32) - (br_table 0 0 1 1 (i32.const 42) (i32.const 0)) - ) - ) - (func (result i32) - (block (result i32) - (br_table 1 1 0 0 (i32.const 42) (i32.const 0)) - ) - ) -) diff --git a/cranelift/wasm/wasmtests/call-simd.wat b/cranelift/wasm/wasmtests/call-simd.wat deleted file mode 100644 index 61834d86bdfc..000000000000 --- a/cranelift/wasm/wasmtests/call-simd.wat +++ /dev/null @@ -1,14 +0,0 @@ -(module - (func $main - (v128.const i32x4 1 2 3 4) - (v128.const i32x4 1 2 3 4) - (call $add) - drop - ) - (func $add (param $a v128) (param $b v128) (result v128) - (local.get $a) - (local.get $b) - (i32x4.add) - ) - (start $main) -) diff --git a/cranelift/wasm/wasmtests/call.wat b/cranelift/wasm/wasmtests/call.wat deleted file mode 100644 index d6d75cb5c9c7..000000000000 --- a/cranelift/wasm/wasmtests/call.wat +++ /dev/null @@ -1,10 +0,0 @@ -(module - (func $main (local i32) - (local.set 0 (i32.const 0)) - (drop (call $inc)) - ) - (func $inc (result i32) - (i32.const 1) - ) - (start $main) -) diff --git a/cranelift/wasm/wasmtests/embenchen_fannkuch.wat b/cranelift/wasm/wasmtests/embenchen_fannkuch.wat deleted file mode 100644 index 391fdcd55b71..000000000000 --- a/cranelift/wasm/wasmtests/embenchen_fannkuch.wat +++ /dev/null @@ -1,12180 +0,0 @@ -(module - (type $0 (;0;) (func (param i32 i32 i32) (result i32))) - (type $1 (;1;) (func (param i32) (result i32))) - (type $2 (;2;) (func (param i32))) - (type $3 (;3;) (func (result i32))) - (type $4 (;4;) (func (param i32 i32) (result i32))) - (type $5 (;5;) (func (param i32 i32))) - (type $6 (;6;) (func)) - (type $7 (;7;) (func (param i32 i32 i32 i32 i32) (result i32))) - (type $8 (;8;) (func (param i32 i32 i32))) - (type $9 (;9;) (func (param i64 i32) (result i32))) - (type $10 (;10;) (func (param i32 i32 i32 i32 i32))) - (type $11 (;11;) (func (param f64 i32) (result f64))) - (type $12 (;12;) (func (param i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory $16 (;0;) 2048 2048)) - (import "env" "table" (table $timport$17 (;0;) 8 8 funcref)) - (import "env" "DYNAMICTOP_PTR" (global $gimport$0 (;0;) i32)) - (import "env" "STACKTOP" (global $gimport$1 (;1;) i32)) - (import "env" "STACK_MAX" (global $gimport$2 (;2;) i32)) - (import "env" "memoryBase" (global $gimport$18 (;3;) i32)) - (import "env" "tableBase" (global $gimport$19 (;4;) i32)) - (import "env" "abort" (func $fimport$3 (;0;) (type $2))) - (import "env" "enlargeMemory" (func $fimport$4 (;1;) (type $3))) - (import "env" "getTotalMemory" (func $fimport$5 (;2;) (type $3))) - (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (;3;) (type $3))) - (import "env" "_pthread_cleanup_pop" (func $fimport$7 (;4;) (type $2))) - (import "env" "___syscall6" (func $fimport$8 (;5;) (type $4))) - (import "env" "_pthread_cleanup_push" (func $fimport$9 (;6;) (type $5))) - (import "env" "_abort" (func $fimport$10 (;7;) (type $6))) - (import "env" "___setErrNo" (func $fimport$11 (;8;) (type $2))) - (import "env" "_emscripten_memcpy_big" (func $fimport$12 (;9;) (type $0))) - (import "env" "___syscall54" (func $fimport$13 (;10;) (type $4))) - (import "env" "___syscall140" (func $fimport$14 (;11;) (type $4))) - (import "env" "___syscall146" (func $fimport$15 (;12;) (type $4))) - (func $0 (;13;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $1 - global.get $global$1 - local.get $0 - i32.add - global.set $global$1 - global.get $global$1 - i32.const 15 - i32.add - i32.const -16 - i32.and - global.set $global$1 - local.get $1 - end - ) - (func $1 (;14;) (type $3) (result i32) - global.get $global$1 - ) - (func $2 (;15;) (type $2) (param $0 i32) - local.get $0 - global.set $global$1 - ) - (func $3 (;16;) (type $5) (param $0 i32) (param $1 i32) - block $label$1 ;; label = @1 - local.get $0 - global.set $global$1 - local.get $1 - global.set $global$2 - end - ) - (func $4 (;17;) (type $5) (param $0 i32) (param $1 i32) - global.get $global$3 - i32.eqz - if ;; label = @1 - block ;; label = @2 - local.get $0 - global.set $global$3 - local.get $1 - global.set $global$4 - end - end - ) - (func $5 (;18;) (type $2) (param $0 i32) - local.get $0 - global.set $global$5 - ) - (func $6 (;19;) (type $3) (result i32) - global.get $global$5 - ) - (func $7 (;20;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.load offset=4 - local.tee $4 - i32.const 2 - i32.shl - local.tee $15 - call $35 - local.set $3 - local.get $15 - call $35 - local.set $6 - local.get $15 - call $35 - local.set $10 - local.get $4 - i32.const 0 - i32.gt_s - local.tee $2 - if ;; label = @2 - block ;; label = @3 - i32.const 0 - local.set $1 - loop $label$3 ;; label = @4 - local.get $3 - local.get $1 - i32.const 2 - i32.shl - i32.add - local.get $1 - i32.store - local.get $1 - i32.const 1 - i32.add - local.tee $1 - local.get $4 - i32.ne - br_if 0 (;@4;) - end - local.get $3 - local.get $0 - i32.load - local.tee $0 - i32.const 2 - i32.shl - i32.add - local.get $4 - i32.const -1 - i32.add - local.tee $11 - i32.store - local.get $3 - local.get $11 - i32.const 2 - i32.shl - i32.add - local.tee $14 - local.get $0 - i32.store - local.get $2 - if ;; label = @4 - block ;; label = @5 - i32.const 0 - local.set $0 - local.get $4 - local.set $1 - loop $label$5 ;; label = @6 - block $label$6 ;; label = @7 - local.get $1 - i32.const 1 - i32.gt_s - if ;; label = @8 - loop $label$8 ;; label = @9 - local.get $10 - local.get $1 - i32.const -1 - i32.add - local.tee $2 - i32.const 2 - i32.shl - i32.add - local.get $1 - i32.store - local.get $2 - i32.const 1 - i32.gt_s - if ;; label = @10 - block ;; label = @11 - local.get $2 - local.set $1 - br 2 (;@9;) - end - else - i32.const 1 - local.set $2 - end - end - else - local.get $1 - local.set $2 - end - local.get $3 - i32.load - local.tee $7 - if ;; label = @8 - local.get $14 - i32.load - local.get $11 - i32.ne - if ;; label = @9 - block ;; label = @10 - local.get $6 - local.get $3 - local.get $15 - call $40 - drop - i32.const 0 - local.set $8 - local.get $6 - i32.load - local.set $9 - loop $label$14 ;; label = @11 - local.get $9 - i32.const -1 - i32.add - local.tee $1 - i32.const 1 - i32.gt_s - if ;; label = @12 - block ;; label = @13 - i32.const 1 - local.set $5 - loop $label$16 ;; label = @14 - local.get $6 - local.get $5 - i32.const 2 - i32.shl - i32.add - local.tee $12 - i32.load - local.set $17 - local.get $12 - local.get $6 - local.get $1 - i32.const 2 - i32.shl - i32.add - local.tee $12 - i32.load - i32.store - local.get $12 - local.get $17 - i32.store - local.get $5 - i32.const 1 - i32.add - local.tee $5 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - i32.lt_s - br_if 0 (;@14;) - end - end - end - local.get $8 - i32.const 1 - i32.add - local.set $5 - local.get $6 - local.get $9 - i32.const 2 - i32.shl - i32.add - local.tee $12 - i32.load - local.set $1 - local.get $12 - local.get $9 - i32.store - local.get $1 - if ;; label = @12 - block ;; label = @13 - local.get $5 - local.set $8 - local.get $1 - local.set $9 - br 2 (;@11;) - end - end - end - local.get $0 - local.get $8 - i32.le_s - if ;; label = @11 - local.get $5 - local.set $0 - end - end - end - end - local.get $2 - local.get $11 - i32.lt_s - if ;; label = @8 - local.get $2 - local.set $1 - else - block ;; label = @9 - i32.const 31 - local.set $1 - br 2 (;@7;) - end - end - loop $label$21 ;; label = @8 - local.get $1 - i32.const 0 - i32.gt_s - if ;; label = @9 - block ;; label = @10 - i32.const 0 - local.set $2 - loop $label$23 ;; label = @11 - local.get $3 - local.get $2 - i32.const 2 - i32.shl - i32.add - local.get $3 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 2 - i32.shl - i32.add - i32.load - i32.store - local.get $2 - local.get $1 - i32.lt_s - br_if 0 (;@11;) - local.get $1 - local.set $2 - end - end - else - i32.const 0 - local.set $2 - end - local.get $3 - local.get $2 - i32.const 2 - i32.shl - i32.add - local.get $7 - i32.store - local.get $10 - local.get $1 - i32.const 2 - i32.shl - i32.add - local.tee $2 - i32.load - local.set $5 - local.get $2 - local.get $5 - i32.const -1 - i32.add - i32.store - local.get $5 - i32.const 1 - i32.gt_s - br_if 2 (;@6;) - local.get $1 - i32.const 1 - i32.add - local.tee $1 - local.get $11 - i32.lt_s - if ;; label = @9 - block ;; label = @10 - local.get $3 - i32.load - local.set $7 - br 2 (;@8;) - end - else - block ;; label = @10 - i32.const 31 - local.set $1 - br 3 (;@7;) - end - end - end - end - end - local.get $1 - i32.const 31 - i32.eq - if ;; label = @6 - block ;; label = @7 - local.get $3 - call $36 - local.get $6 - call $36 - local.get $10 - call $36 - local.get $0 - return - end - end - end - else - block ;; label = @5 - local.get $14 - local.set $16 - local.get $11 - local.set $13 - end - end - end - else - block ;; label = @3 - local.get $3 - local.get $0 - i32.load - local.tee $0 - i32.const 2 - i32.shl - i32.add - local.get $4 - i32.const -1 - i32.add - local.tee $13 - i32.store - local.get $3 - local.get $13 - i32.const 2 - i32.shl - i32.add - local.tee $16 - local.get $0 - i32.store - end - end - i32.const 0 - local.set $0 - local.get $4 - local.set $1 - loop $label$30 ;; label = @2 - block $label$31 ;; label = @3 - local.get $1 - i32.const 1 - i32.gt_s - if ;; label = @4 - loop $label$33 ;; label = @5 - local.get $10 - local.get $1 - i32.const -1 - i32.add - local.tee $2 - i32.const 2 - i32.shl - i32.add - local.get $1 - i32.store - local.get $2 - i32.const 1 - i32.gt_s - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.set $1 - br 2 (;@5;) - end - else - i32.const 1 - local.set $2 - end - end - else - local.get $1 - local.set $2 - end - local.get $3 - i32.load - local.tee $9 - if ;; label = @4 - local.get $16 - i32.load - local.get $13 - i32.ne - if ;; label = @5 - block ;; label = @6 - i32.const 0 - local.set $5 - local.get $6 - i32.load - local.set $8 - loop $label$39 ;; label = @7 - local.get $8 - i32.const -1 - i32.add - local.tee $1 - i32.const 1 - i32.gt_s - if ;; label = @8 - block ;; label = @9 - i32.const 1 - local.set $4 - loop $label$41 ;; label = @10 - local.get $6 - local.get $4 - i32.const 2 - i32.shl - i32.add - local.tee $7 - i32.load - local.set $14 - local.get $7 - local.get $6 - local.get $1 - i32.const 2 - i32.shl - i32.add - local.tee $7 - i32.load - i32.store - local.get $7 - local.get $14 - i32.store - local.get $4 - i32.const 1 - i32.add - local.tee $4 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - i32.lt_s - br_if 0 (;@10;) - end - end - end - local.get $5 - i32.const 1 - i32.add - local.set $4 - local.get $6 - local.get $8 - i32.const 2 - i32.shl - i32.add - local.tee $7 - i32.load - local.set $1 - local.get $7 - local.get $8 - i32.store - local.get $1 - if ;; label = @8 - block ;; label = @9 - local.get $4 - local.set $5 - local.get $1 - local.set $8 - br 2 (;@7;) - end - end - end - local.get $0 - local.get $5 - i32.le_s - if ;; label = @7 - local.get $4 - local.set $0 - end - end - end - end - local.get $2 - local.get $13 - i32.lt_s - if ;; label = @4 - local.get $2 - local.set $1 - else - block ;; label = @5 - i32.const 31 - local.set $1 - br 2 (;@3;) - end - end - loop $label$46 ;; label = @4 - local.get $1 - i32.const 0 - i32.gt_s - if ;; label = @5 - block ;; label = @6 - i32.const 0 - local.set $2 - loop $label$48 ;; label = @7 - local.get $3 - local.get $2 - i32.const 2 - i32.shl - i32.add - local.get $3 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 2 - i32.shl - i32.add - i32.load - i32.store - local.get $2 - local.get $1 - i32.lt_s - br_if 0 (;@7;) - local.get $1 - local.set $2 - end - end - else - i32.const 0 - local.set $2 - end - local.get $3 - local.get $2 - i32.const 2 - i32.shl - i32.add - local.get $9 - i32.store - local.get $10 - local.get $1 - i32.const 2 - i32.shl - i32.add - local.tee $2 - i32.load - local.set $4 - local.get $2 - local.get $4 - i32.const -1 - i32.add - i32.store - local.get $4 - i32.const 1 - i32.gt_s - br_if 2 (;@2;) - local.get $1 - i32.const 1 - i32.add - local.tee $1 - local.get $13 - i32.lt_s - if ;; label = @5 - block ;; label = @6 - local.get $3 - i32.load - local.set $9 - br 2 (;@4;) - end - else - block ;; label = @6 - i32.const 31 - local.set $1 - br 3 (;@3;) - end - end - end - end - end - local.get $1 - i32.const 31 - i32.eq - if ;; label = @2 - block ;; label = @3 - local.get $3 - call $36 - local.get $6 - call $36 - local.get $10 - call $36 - local.get $0 - return - end - end - i32.const 0 - end - ) - (func $8 (;21;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $5 - global.get $global$1 - i32.const 32 - i32.add - global.set $global$1 - local.get $5 - i32.const 16 - i32.add - local.set $7 - local.get $5 - i32.const 8 - i32.add - local.set $10 - local.get $5 - local.set $2 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $0 - i32.const 1 - i32.le_s - br_if 0 (;@3;) - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - block $label$6 ;; label = @6 - block $label$7 ;; label = @7 - block $label$8 ;; label = @8 - block $label$9 ;; label = @9 - block $label$10 ;; label = @10 - local.get $1 - i32.load offset=4 - i32.load8_s - local.tee $0 - i32.const 48 - i32.sub - br_table 5 (;@5;) 0 (;@10;) 2 (;@8;) 1 (;@9;) 3 (;@7;) 4 (;@6;) 6 (;@4;) - end - i32.const 9 - local.set $3 - br 7 (;@2;) - end - br 5 (;@3;) - end - i32.const 10 - local.set $3 - br 5 (;@2;) - end - i32.const 11 - local.set $3 - br 4 (;@2;) - end - i32.const 12 - local.set $3 - br 3 (;@2;) - end - local.get $5 - global.set $global$1 - i32.const 0 - return - end - local.get $2 - local.get $0 - i32.const -48 - i32.add - i32.store - i32.const 1140 - local.get $2 - call $33 - drop - local.get $5 - global.set $global$1 - i32.const -1 - return - end - i32.const 11 - local.set $3 - end - local.get $3 - i32.const -1 - i32.add - local.set $6 - i32.const 0 - local.set $2 - i32.const 0 - local.set $0 - loop $label$11 ;; label = @2 - i32.const 12 - call $35 - local.tee $1 - local.get $0 - i32.store - local.get $1 - local.get $3 - i32.store offset=4 - local.get $1 - local.get $2 - i32.store offset=8 - local.get $0 - i32.const 1 - i32.add - local.tee $0 - local.get $6 - i32.ne - if ;; label = @3 - block ;; label = @4 - local.get $1 - local.set $2 - br 2 (;@2;) - end - end - end - local.get $3 - i32.const 2 - i32.shl - local.tee $0 - call $35 - local.set $4 - local.get $0 - call $35 - local.set $8 - i32.const 0 - local.set $0 - loop $label$13 ;; label = @2 - local.get $4 - local.get $0 - i32.const 2 - i32.shl - i32.add - local.get $0 - i32.store - local.get $0 - i32.const 1 - i32.add - local.tee $0 - local.get $3 - i32.ne - br_if 0 (;@2;) - end - local.get $3 - local.set $0 - i32.const 30 - local.set $6 - loop $label$14 ;; label = @2 - block $label$15 ;; label = @3 - i32.const 0 - local.set $2 - loop $label$16 ;; label = @4 - local.get $10 - local.get $4 - local.get $2 - i32.const 2 - i32.shl - i32.add - i32.load - i32.const 1 - i32.add - i32.store - i32.const 1174 - local.get $10 - call $33 - drop - local.get $2 - i32.const 1 - i32.add - local.tee $2 - local.get $3 - i32.ne - br_if 0 (;@4;) - end - i32.const 10 - call $34 - drop - local.get $0 - i32.const 1 - i32.gt_s - if ;; label = @4 - loop $label$18 ;; label = @5 - local.get $8 - local.get $0 - i32.const -1 - i32.add - local.tee $2 - i32.const 2 - i32.shl - i32.add - local.get $0 - i32.store - local.get $2 - i32.const 1 - i32.gt_s - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.set $0 - br 2 (;@5;) - end - else - i32.const 1 - local.set $0 - end - end - else - local.get $0 - local.get $3 - i32.eq - br_if 1 (;@3;) - end - local.get $6 - i32.const -1 - i32.add - local.set $6 - loop $label$22 ;; label = @4 - block $label$23 ;; label = @5 - local.get $4 - i32.load - local.set $9 - local.get $0 - i32.const 0 - i32.gt_s - if ;; label = @6 - block ;; label = @7 - i32.const 0 - local.set $2 - loop $label$25 ;; label = @8 - local.get $4 - local.get $2 - i32.const 2 - i32.shl - i32.add - local.get $4 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 2 - i32.shl - i32.add - i32.load - i32.store - local.get $2 - local.get $0 - i32.lt_s - br_if 0 (;@8;) - local.get $0 - local.set $2 - end - end - else - i32.const 0 - local.set $2 - end - local.get $4 - local.get $2 - i32.const 2 - i32.shl - i32.add - local.get $9 - i32.store - local.get $8 - local.get $0 - i32.const 2 - i32.shl - i32.add - local.tee $9 - i32.load - local.set $2 - local.get $9 - local.get $2 - i32.const -1 - i32.add - i32.store - local.get $2 - i32.const 1 - i32.gt_s - br_if 0 (;@5;) - local.get $0 - i32.const 1 - i32.add - local.tee $0 - local.get $3 - i32.ne - br_if 1 (;@4;) - br 2 (;@3;) - end - end - local.get $6 - br_if 1 (;@2;) - end - end - local.get $4 - call $36 - local.get $8 - call $36 - local.get $1 - if ;; label = @2 - block ;; label = @3 - i32.const 0 - local.set $0 - loop $label$28 ;; label = @4 - local.get $0 - local.get $1 - call $7 - local.tee $2 - i32.lt_s - if ;; label = @5 - local.get $2 - local.set $0 - end - local.get $1 - i32.load offset=8 - local.set $2 - local.get $1 - call $36 - local.get $2 - if ;; label = @5 - block ;; label = @6 - local.get $2 - local.set $1 - br 2 (;@4;) - end - end - end - end - else - i32.const 0 - local.set $0 - end - local.get $7 - local.get $3 - i32.store - local.get $7 - local.get $0 - i32.store offset=4 - i32.const 1151 - local.get $7 - call $33 - drop - local.get $5 - global.set $global$1 - i32.const 0 - end - ) - (func $9 (;22;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $1 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $1 - local.tee $2 - local.get $0 - i32.load offset=60 - i32.store - i32.const 6 - local.get $2 - call $fimport$8 - call $11 - local.set $0 - local.get $1 - global.set $global$1 - local.get $0 - end - ) - (func $10 (;23;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 32 - i32.add - global.set $global$1 - local.get $4 - local.tee $3 - local.get $0 - i32.load offset=60 - i32.store - local.get $3 - i32.const 0 - i32.store offset=4 - local.get $3 - local.get $1 - i32.store offset=8 - local.get $3 - local.get $4 - i32.const 20 - i32.add - local.tee $0 - i32.store offset=12 - local.get $3 - local.get $2 - i32.store offset=16 - i32.const 140 - local.get $3 - call $fimport$14 - call $11 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $0 - i32.const -1 - i32.store - i32.const -1 - end - else - local.get $0 - i32.load - end - local.set $0 - local.get $4 - global.set $global$1 - local.get $0 - end - ) - (func $11 (;24;) (type $1) (param $0 i32) (result i32) - local.get $0 - i32.const -4096 - i32.gt_u - if (result i32) ;; label = @1 - block (result i32) ;; label = @2 - call $12 - i32.const 0 - local.get $0 - i32.sub - i32.store - i32.const -1 - end - else - local.get $0 - end - ) - (func $12 (;25;) (type $3) (result i32) - i32.const 3648 - ) - (func $13 (;26;) (type $2) (param $0 i32) - nop - ) - (func $14 (;27;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 80 - i32.add - global.set $global$1 - local.get $4 - local.set $3 - local.get $4 - i32.const 12 - i32.add - local.set $5 - local.get $0 - i32.const 3 - i32.store offset=36 - local.get $0 - i32.load - i32.const 64 - i32.and - i32.eqz - if ;; label = @2 - block ;; label = @3 - local.get $3 - local.get $0 - i32.load offset=60 - i32.store - local.get $3 - i32.const 21505 - i32.store offset=4 - local.get $3 - local.get $5 - i32.store offset=8 - i32.const 54 - local.get $3 - call $fimport$13 - if ;; label = @4 - local.get $0 - i32.const -1 - i32.store8 offset=75 - end - end - end - local.get $0 - local.get $1 - local.get $2 - call $15 - local.set $0 - local.get $4 - global.set $global$1 - local.get $0 - end - ) - (func $15 (;28;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $8 - global.get $global$1 - i32.const 48 - i32.add - global.set $global$1 - local.get $8 - i32.const 16 - i32.add - local.set $9 - local.get $8 - local.set $10 - local.get $8 - i32.const 32 - i32.add - local.tee $3 - local.get $0 - i32.const 28 - i32.add - local.tee $6 - i32.load - local.tee $4 - i32.store - local.get $3 - local.get $0 - i32.const 20 - i32.add - local.tee $11 - i32.load - local.get $4 - i32.sub - local.tee $5 - i32.store offset=4 - local.get $3 - local.get $1 - i32.store offset=8 - local.get $3 - local.get $2 - i32.store offset=12 - local.get $0 - i32.const 60 - i32.add - local.set $13 - local.get $0 - i32.const 44 - i32.add - local.set $14 - local.get $3 - local.set $1 - i32.const 2 - local.set $4 - local.get $5 - local.get $2 - i32.add - local.set $12 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - loop $label$5 ;; label = @5 - i32.const 3604 - i32.load - if ;; label = @6 - block ;; label = @7 - i32.const 1 - local.get $0 - call $fimport$9 - local.get $10 - local.get $13 - i32.load - i32.store - local.get $10 - local.get $1 - i32.store offset=4 - local.get $10 - local.get $4 - i32.store offset=8 - i32.const 146 - local.get $10 - call $fimport$15 - call $11 - local.set $3 - i32.const 0 - call $fimport$7 - end - else - block ;; label = @7 - local.get $9 - local.get $13 - i32.load - i32.store - local.get $9 - local.get $1 - i32.store offset=4 - local.get $9 - local.get $4 - i32.store offset=8 - i32.const 146 - local.get $9 - call $fimport$15 - call $11 - local.set $3 - end - end - local.get $12 - local.get $3 - i32.eq - br_if 1 (;@4;) - local.get $3 - i32.const 0 - i32.lt_s - br_if 2 (;@3;) - local.get $3 - local.get $1 - i32.load offset=4 - local.tee $5 - i32.gt_u - if (result i32) ;; label = @6 - block (result i32) ;; label = @7 - local.get $6 - local.get $14 - i32.load - local.tee $7 - i32.store - local.get $11 - local.get $7 - i32.store - local.get $1 - i32.load offset=12 - local.set $7 - local.get $1 - i32.const 8 - i32.add - local.set $1 - local.get $4 - i32.const -1 - i32.add - local.set $4 - local.get $3 - local.get $5 - i32.sub - end - else - local.get $4 - i32.const 2 - i32.eq - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - local.get $6 - local.get $6 - i32.load - local.get $3 - i32.add - i32.store - local.get $5 - local.set $7 - i32.const 2 - local.set $4 - local.get $3 - end - else - block (result i32) ;; label = @8 - local.get $5 - local.set $7 - local.get $3 - end - end - end - local.set $5 - local.get $1 - local.get $1 - i32.load - local.get $5 - i32.add - i32.store - local.get $1 - local.get $7 - local.get $5 - i32.sub - i32.store offset=4 - local.get $12 - local.get $3 - i32.sub - local.set $12 - br 0 (;@5;) - end - end - local.get $0 - local.get $14 - i32.load - local.tee $1 - local.get $0 - i32.load offset=48 - i32.add - i32.store offset=16 - local.get $6 - local.get $1 - i32.store - local.get $11 - local.get $1 - i32.store - br 1 (;@2;) - end - local.get $0 - i32.const 0 - i32.store offset=16 - local.get $6 - i32.const 0 - i32.store - local.get $11 - i32.const 0 - i32.store - local.get $0 - local.get $0 - i32.load - i32.const 32 - i32.or - i32.store - local.get $4 - i32.const 2 - i32.eq - if (result i32) ;; label = @3 - i32.const 0 - else - local.get $2 - local.get $1 - i32.load offset=4 - i32.sub - end - local.set $2 - end - local.get $8 - global.set $global$1 - local.get $2 - end - ) - (func $16 (;29;) (type $2) (param $0 i32) - local.get $0 - i32.load offset=68 - i32.eqz - if ;; label = @1 - local.get $0 - call $13 - end - ) - (func $17 (;30;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $1 - i32.const 255 - i32.and - local.set $5 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - local.get $2 - i32.const 0 - i32.ne - local.tee $4 - local.get $0 - i32.const 3 - i32.and - i32.const 0 - i32.ne - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $1 - i32.const 255 - i32.and - local.set $4 - local.get $2 - local.set $3 - local.get $0 - local.set $2 - loop $label$6 ;; label = @7 - local.get $2 - i32.load8_s - local.get $4 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $3 - local.set $0 - br 6 (;@3;) - end - end - local.get $3 - i32.const -1 - i32.add - local.tee $3 - i32.const 0 - i32.ne - local.tee $0 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 3 - i32.and - i32.const 0 - i32.ne - i32.and - br_if 0 (;@7;) - br 3 (;@4;) - end - end - else - block ;; label = @6 - local.get $2 - local.set $3 - local.get $0 - local.set $2 - local.get $4 - local.set $0 - end - end - end - local.get $0 - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $0 - br 2 (;@3;) - end - else - i32.const 0 - local.set $0 - end - br 1 (;@2;) - end - local.get $2 - i32.load8_s - local.get $1 - i32.const 255 - i32.and - local.tee $1 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.ne - if ;; label = @3 - block ;; label = @4 - local.get $5 - i32.const 16843009 - i32.mul - local.set $3 - block $label$12 ;; label = @5 - block $label$13 ;; label = @6 - local.get $0 - i32.const 3 - i32.le_u - br_if 0 (;@6;) - loop $label$14 ;; label = @7 - local.get $2 - i32.load - local.get $3 - i32.xor - local.tee $4 - i32.const -2139062144 - i32.and - i32.const -2139062144 - i32.xor - local.get $4 - i32.const -16843009 - i32.add - i32.and - i32.eqz - if ;; label = @8 - block ;; label = @9 - local.get $2 - i32.const 4 - i32.add - local.set $2 - local.get $0 - i32.const -4 - i32.add - local.tee $0 - i32.const 3 - i32.gt_u - br_if 2 (;@7;) - br 3 (;@6;) - end - end - end - br 1 (;@5;) - end - local.get $0 - i32.eqz - if ;; label = @6 - block ;; label = @7 - i32.const 0 - local.set $0 - br 5 (;@2;) - end - end - end - loop $label$17 ;; label = @5 - local.get $2 - i32.load8_s - local.get $1 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eq - br_if 3 (;@2;) - local.get $2 - i32.const 1 - i32.add - local.set $2 - local.get $0 - i32.const -1 - i32.add - local.tee $0 - br_if 0 (;@5;) - i32.const 0 - local.set $0 - end - end - end - end - local.get $0 - if (result i32) ;; label = @2 - local.get $2 - else - i32.const 0 - end - end - ) - (func $18 (;31;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 224 - i32.add - global.set $global$1 - local.get $4 - i32.const 136 - i32.add - local.set $5 - local.get $4 - i32.const 80 - i32.add - local.tee $3 - i64.const 0 - i64.store align=4 - local.get $3 - i64.const 0 - i64.store offset=8 align=4 - local.get $3 - i64.const 0 - i64.store offset=16 align=4 - local.get $3 - i64.const 0 - i64.store offset=24 align=4 - local.get $3 - i64.const 0 - i64.store offset=32 align=4 - local.get $4 - i32.const 120 - i32.add - local.tee $6 - local.get $2 - i32.load - i32.store - i32.const 0 - local.get $1 - local.get $6 - local.get $4 - local.tee $2 - local.get $3 - call $19 - i32.const 0 - i32.lt_s - if ;; label = @2 - i32.const -1 - local.set $1 - else - block ;; label = @3 - local.get $0 - i32.load offset=76 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @4 - local.get $0 - call $20 - else - i32.const 0 - end - local.set $12 - local.get $0 - i32.load - local.set $7 - local.get $0 - i32.load8_s offset=74 - i32.const 1 - i32.lt_s - if ;; label = @4 - local.get $0 - local.get $7 - i32.const -33 - i32.and - i32.store - end - local.get $0 - i32.const 48 - i32.add - local.tee $8 - i32.load - if ;; label = @4 - local.get $0 - local.get $1 - local.get $6 - local.get $2 - local.get $3 - call $19 - local.set $1 - else - block ;; label = @5 - local.get $0 - i32.const 44 - i32.add - local.tee $9 - i32.load - local.set $10 - local.get $9 - local.get $5 - i32.store - local.get $0 - i32.const 28 - i32.add - local.tee $13 - local.get $5 - i32.store - local.get $0 - i32.const 20 - i32.add - local.tee $11 - local.get $5 - i32.store - local.get $8 - i32.const 80 - i32.store - local.get $0 - i32.const 16 - i32.add - local.tee $14 - local.get $5 - i32.const 80 - i32.add - i32.store - local.get $0 - local.get $1 - local.get $6 - local.get $2 - local.get $3 - call $19 - local.set $1 - local.get $10 - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 0 - i32.const 0 - local.get $0 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - drop - local.get $11 - i32.load - i32.eqz - if ;; label = @8 - i32.const -1 - local.set $1 - end - local.get $9 - local.get $10 - i32.store - local.get $8 - i32.const 0 - i32.store - local.get $14 - i32.const 0 - i32.store - local.get $13 - i32.const 0 - i32.store - local.get $11 - i32.const 0 - i32.store - end - end - end - end - local.get $0 - local.get $0 - i32.load - local.tee $2 - local.get $7 - i32.const 32 - i32.and - i32.or - i32.store - local.get $12 - if ;; label = @4 - local.get $0 - call $13 - end - local.get $2 - i32.const 32 - i32.and - if ;; label = @4 - i32.const -1 - local.set $1 - end - end - end - local.get $4 - global.set $global$1 - local.get $1 - end - ) - (func $19 (;32;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) - (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) (local $47 i32) (local $48 i32) (local $49 i32) (local $50 i64) (local $51 i64) (local $52 f64) (local $53 f64) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $23 - global.get $global$1 - i32.const 624 - i32.add - global.set $global$1 - local.get $23 - i32.const 16 - i32.add - local.set $20 - local.get $23 - local.set $16 - local.get $23 - i32.const 528 - i32.add - local.set $36 - local.get $0 - i32.const 0 - i32.ne - local.set $30 - local.get $23 - i32.const 536 - i32.add - local.tee $17 - i32.const 40 - i32.add - local.tee $21 - local.set $38 - local.get $17 - i32.const 39 - i32.add - local.set $39 - local.get $23 - i32.const 8 - i32.add - local.tee $37 - i32.const 4 - i32.add - local.set $42 - i32.const 0 - local.get $23 - i32.const 588 - i32.add - local.tee $19 - local.tee $27 - i32.sub - local.set $43 - local.get $23 - i32.const 576 - i32.add - local.tee $17 - i32.const 12 - i32.add - local.set $33 - local.get $17 - i32.const 11 - i32.add - local.set $40 - local.get $33 - local.tee $28 - local.get $27 - i32.sub - local.set $44 - i32.const -2 - local.get $27 - i32.sub - local.set $45 - local.get $28 - i32.const 2 - i32.add - local.set $46 - local.get $23 - i32.const 24 - i32.add - local.tee $47 - i32.const 288 - i32.add - local.set $48 - local.get $19 - i32.const 9 - i32.add - local.tee $31 - local.set $41 - local.get $19 - i32.const 8 - i32.add - local.set $34 - i32.const 0 - local.set $15 - i32.const 0 - local.set $10 - i32.const 0 - local.set $17 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - loop $label$4 ;; label = @4 - block $label$5 ;; label = @5 - local.get $15 - i32.const -1 - i32.gt_s - if ;; label = @6 - local.get $10 - i32.const 2147483647 - local.get $15 - i32.sub - i32.gt_s - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - call $12 - i32.const 75 - i32.store - i32.const -1 - end - else - local.get $10 - local.get $15 - i32.add - end - local.set $15 - end - local.get $1 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eqz - br_if 2 (;@3;) - local.get $1 - local.set $11 - block $label$9 ;; label = @6 - block $label$10 ;; label = @7 - loop $label$11 ;; label = @8 - block $label$12 ;; label = @9 - block $label$13 ;; label = @10 - block $label$14 ;; label = @11 - block $label$15 ;; label = @12 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 0 - i32.sub - br_table 1 (;@11;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 0 (;@12;) 2 (;@10;) - end - local.get $11 - local.set $5 - br 4 (;@7;) - end - local.get $11 - local.set $5 - br 1 (;@9;) - end - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.set $5 - br 1 (;@8;) - end - end - br 1 (;@6;) - end - loop $label$16 ;; label = @7 - local.get $5 - i32.load8_s offset=1 - i32.const 37 - i32.ne - br_if 1 (;@6;) - local.get $11 - i32.const 1 - i32.add - local.set $11 - local.get $5 - i32.const 2 - i32.add - local.tee $5 - i32.load8_s - i32.const 37 - i32.eq - br_if 0 (;@7;) - end - end - local.get $11 - local.get $1 - i32.sub - local.set $10 - local.get $30 - if ;; label = @6 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @7 - local.get $1 - local.get $10 - local.get $0 - call $21 - drop - end - end - local.get $10 - if ;; label = @6 - block ;; label = @7 - local.get $5 - local.set $1 - br 3 (;@4;) - end - end - local.get $5 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $10 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $9 - i32.const 10 - i32.lt_u - if (result i32) ;; label = @6 - block (result i32) ;; label = @7 - local.get $5 - i32.const 3 - i32.add - local.set $10 - local.get $5 - i32.load8_s offset=2 - i32.const 36 - i32.eq - local.tee $12 - if ;; label = @8 - local.get $10 - local.set $11 - end - local.get $12 - if ;; label = @8 - i32.const 1 - local.set $17 - end - local.get $11 - i32.load8_s - local.set $5 - local.get $12 - i32.eqz - if ;; label = @8 - i32.const -1 - local.set $9 - end - local.get $17 - end - else - block (result i32) ;; label = @7 - local.get $10 - local.set $5 - i32.const -1 - local.set $9 - local.get $17 - end - end - local.set $10 - block $label$25 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - local.tee $12 - i32.const 32 - i32.lt_u - if ;; label = @7 - block ;; label = @8 - i32.const 0 - local.set $17 - loop $label$27 ;; label = @9 - i32.const 1 - local.get $12 - i32.shl - i32.const 75913 - i32.and - i32.eqz - br_if 3 (;@6;) - i32.const 1 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - i32.shl - local.get $17 - i32.or - local.set $17 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - local.tee $12 - i32.const 32 - i32.lt_u - br_if 0 (;@9;) - end - end - else - i32.const 0 - local.set $17 - end - end - block $label$29 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 42 - i32.eq - if ;; label = @7 - block ;; label = @8 - block $label$31 (result i32) ;; label = @9 - block $label$32 ;; label = @10 - local.get $11 - i32.const 1 - i32.add - local.tee $7 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $12 - i32.const 10 - i32.ge_u - br_if 0 (;@10;) - local.get $11 - i32.load8_s offset=2 - i32.const 36 - i32.ne - br_if 0 (;@10;) - local.get $4 - local.get $12 - i32.const 2 - i32.shl - i32.add - i32.const 10 - i32.store - i32.const 1 - local.set $8 - local.get $3 - local.get $7 - i32.load8_s - i32.const -48 - i32.add - i32.const 3 - i32.shl - i32.add - i64.load - i32.wrap_i64 - local.set $10 - local.get $11 - i32.const 3 - i32.add - br 1 (;@9;) - end - local.get $10 - if ;; label = @10 - block ;; label = @11 - i32.const -1 - local.set $15 - br 6 (;@5;) - end - end - local.get $30 - i32.eqz - if ;; label = @10 - block ;; label = @11 - local.get $17 - local.set $12 - i32.const 0 - local.set $17 - local.get $7 - local.set $11 - i32.const 0 - local.set $10 - br 5 (;@6;) - end - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $11 - i32.load - local.set $10 - local.get $2 - local.get $11 - i32.const 4 - i32.add - i32.store - i32.const 0 - local.set $8 - local.get $7 - end - local.set $11 - local.get $17 - i32.const 8192 - i32.or - local.set $12 - i32.const 0 - local.get $10 - i32.sub - local.set $7 - local.get $11 - i32.load8_s - local.set $5 - local.get $10 - i32.const 0 - i32.lt_s - local.tee $6 - i32.eqz - if ;; label = @9 - local.get $17 - local.set $12 - end - local.get $8 - local.set $17 - local.get $6 - if ;; label = @9 - local.get $7 - local.set $10 - end - end - else - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $12 - i32.const 10 - i32.lt_u - if ;; label = @8 - block ;; label = @9 - i32.const 0 - local.set $7 - local.get $12 - local.set $5 - loop $label$39 ;; label = @10 - local.get $7 - i32.const 10 - i32.mul - local.get $5 - i32.add - local.set $7 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $12 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - br_if 0 (;@10;) - end - local.get $7 - i32.const 0 - i32.lt_s - if ;; label = @10 - block ;; label = @11 - i32.const -1 - local.set $15 - br 6 (;@5;) - end - else - block ;; label = @11 - local.get $12 - local.set $5 - local.get $17 - local.set $12 - local.get $10 - local.set $17 - local.get $7 - local.set $10 - end - end - end - else - block ;; label = @9 - local.get $17 - local.set $12 - local.get $10 - local.set $17 - i32.const 0 - local.set $10 - end - end - end - end - block $label$43 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 46 - i32.eq - if ;; label = @7 - block ;; label = @8 - local.get $11 - i32.const 1 - i32.add - local.tee $7 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 42 - i32.ne - if ;; label = @9 - block ;; label = @10 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - if ;; label = @11 - block ;; label = @12 - local.get $7 - local.set $11 - i32.const 0 - local.set $7 - end - else - block ;; label = @12 - i32.const 0 - local.set $5 - local.get $7 - local.set $11 - br 6 (;@6;) - end - end - loop $label$48 ;; label = @11 - local.get $7 - i32.const 10 - i32.mul - local.get $5 - i32.add - local.set $5 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - i32.const -48 - i32.add - local.tee $8 - i32.const 10 - i32.ge_u - br_if 5 (;@6;) - local.get $5 - local.set $7 - local.get $8 - local.set $5 - br 0 (;@11;) - end - end - end - local.get $11 - i32.const 2 - i32.add - local.tee $7 - i32.load8_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - if ;; label = @9 - local.get $11 - i32.load8_s offset=3 - i32.const 36 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.get $5 - i32.const 2 - i32.shl - i32.add - i32.const 10 - i32.store - local.get $3 - local.get $7 - i32.load8_s - i32.const -48 - i32.add - i32.const 3 - i32.shl - i32.add - i64.load - i32.wrap_i64 - local.set $5 - local.get $11 - i32.const 4 - i32.add - local.set $11 - br 5 (;@6;) - end - end - end - local.get $17 - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - end - local.get $30 - if (result i32) ;; label = @9 - block (result i32) ;; label = @10 - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $11 - i32.load - local.set $5 - local.get $2 - local.get $11 - i32.const 4 - i32.add - i32.store - local.get $7 - end - else - block (result i32) ;; label = @10 - i32.const 0 - local.set $5 - local.get $7 - end - end - local.set $11 - end - else - i32.const -1 - local.set $5 - end - end - local.get $11 - local.set $7 - i32.const 0 - local.set $8 - loop $label$55 ;; label = @6 - local.get $7 - i32.load8_s - i32.const -65 - i32.add - local.tee $6 - i32.const 57 - i32.gt_u - if ;; label = @7 - block ;; label = @8 - i32.const -1 - local.set $15 - br 3 (;@5;) - end - end - local.get $7 - i32.const 1 - i32.add - local.set $11 - local.get $8 - i32.const 58 - i32.mul - i32.const 1177 - i32.add - local.get $6 - i32.add - i32.load8_s - local.tee $13 - i32.const 255 - i32.and - local.tee $6 - i32.const -1 - i32.add - i32.const 8 - i32.lt_u - if ;; label = @7 - block ;; label = @8 - local.get $11 - local.set $7 - local.get $6 - local.set $8 - br 2 (;@6;) - end - end - end - local.get $13 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eqz - if ;; label = @6 - block ;; label = @7 - i32.const -1 - local.set $15 - br 2 (;@5;) - end - end - local.get $9 - i32.const -1 - i32.gt_s - local.set $14 - block $label$59 ;; label = @6 - block $label$60 ;; label = @7 - local.get $13 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 19 - i32.eq - if ;; label = @8 - local.get $14 - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - else - br 2 (;@7;) - end - else - block ;; label = @9 - local.get $14 - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.get $9 - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store - local.get $16 - local.get $3 - local.get $9 - i32.const 3 - i32.shl - i32.add - i64.load - i64.store - br 4 (;@7;) - end - end - local.get $30 - i32.eqz - if ;; label = @10 - block ;; label = @11 - i32.const 0 - local.set $15 - br 6 (;@5;) - end - end - local.get $16 - local.get $6 - local.get $2 - call $22 - end - end - br 1 (;@6;) - end - local.get $30 - i32.eqz - if ;; label = @7 - block ;; label = @8 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 4 (;@4;) - end - end - end - local.get $7 - i32.load8_s - local.tee $7 - i32.const -33 - i32.and - local.set $9 - local.get $8 - i32.const 0 - i32.ne - local.get $7 - i32.const 15 - i32.and - i32.const 3 - i32.eq - i32.and - i32.eqz - if ;; label = @6 - local.get $7 - local.set $9 - end - local.get $12 - i32.const -65537 - i32.and - local.set $7 - local.get $12 - i32.const 8192 - i32.and - if ;; label = @6 - local.get $7 - local.set $12 - end - block $label$70 ;; label = @6 - block $label$71 ;; label = @7 - block $label$72 ;; label = @8 - block $label$73 ;; label = @9 - block $label$74 ;; label = @10 - block $label$75 ;; label = @11 - block $label$76 ;; label = @12 - block $label$77 ;; label = @13 - block $label$78 ;; label = @14 - block $label$79 ;; label = @15 - block $label$80 ;; label = @16 - block $label$81 ;; label = @17 - block $label$82 ;; label = @18 - block $label$83 ;; label = @19 - block $label$84 ;; label = @20 - block $label$85 ;; label = @21 - block $label$86 ;; label = @22 - block $label$87 ;; label = @23 - block $label$88 ;; label = @24 - block $label$89 ;; label = @25 - local.get $9 - i32.const 65 - i32.sub - br_table 11 (;@14;) 12 (;@13;) 9 (;@16;) 12 (;@13;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 10 (;@15;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 11 (;@14;) 12 (;@13;) 6 (;@19;) 4 (;@21;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 4 (;@21;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 7 (;@18;) 0 (;@25;) 3 (;@22;) 1 (;@24;) 12 (;@13;) 12 (;@13;) 8 (;@17;) 12 (;@13;) 5 (;@20;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) - end - block $label$90 ;; label = @25 - block $label$91 ;; label = @26 - block $label$92 ;; label = @27 - block $label$93 ;; label = @28 - block $label$94 ;; label = @29 - block $label$95 ;; label = @30 - block $label$96 ;; label = @31 - block $label$97 ;; label = @32 - local.get $8 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 0 - i32.sub - br_table 0 (;@32;) 1 (;@31;) 2 (;@30;) 3 (;@29;) 4 (;@28;) 7 (;@25;) 5 (;@27;) 6 (;@26;) 7 (;@25;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 27 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 26 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i64.extend_i32_s - i64.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 25 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store16 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 24 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store8 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 23 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 22 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i64.extend_i32_s - i64.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 21 (;@4;) - end - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 20 (;@4;) - end - local.get $12 - i32.const 8 - i32.or - local.set $12 - local.get $5 - i32.const 8 - i32.le_u - if ;; label = @24 - i32.const 8 - local.set $5 - end - i32.const 120 - local.set $9 - br 11 (;@12;) - end - br 10 (;@12;) - end - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.eq - if ;; label = @22 - local.get $21 - local.set $7 - else - block ;; label = @23 - local.get $21 - local.set $1 - loop $label$101 ;; label = @24 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $50 - i64.const 7 - i64.and - i64.const 48 - i64.or - i64.store8 - local.get $50 - i64.const 3 - i64.shr_u - local.tee $50 - i64.const 0 - i64.ne - br_if 0 (;@24;) - local.get $1 - local.set $7 - end - end - end - local.get $12 - i32.const 8 - i32.and - if ;; label = @22 - block ;; label = @23 - local.get $38 - local.get $7 - i32.sub - local.tee $1 - i32.const 1 - i32.add - local.set $8 - local.get $5 - local.get $1 - i32.le_s - if ;; label = @24 - local.get $8 - local.set $5 - end - i32.const 0 - local.set $6 - i32.const 1657 - local.set $8 - br 16 (;@7;) - end - else - block ;; label = @23 - i32.const 0 - local.set $6 - i32.const 1657 - local.set $8 - br 16 (;@7;) - end - end - end - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.lt_s - if ;; label = @21 - block ;; label = @22 - local.get $16 - i64.const 0 - local.get $50 - i64.sub - local.tee $50 - i64.store - i32.const 1 - local.set $6 - i32.const 1657 - local.set $8 - br 11 (;@11;) - end - end - local.get $12 - i32.const 2048 - i32.and - if ;; label = @21 - block ;; label = @22 - i32.const 1 - local.set $6 - i32.const 1658 - local.set $8 - br 11 (;@11;) - end - else - block ;; label = @22 - local.get $12 - i32.const 1 - i32.and - local.tee $1 - local.set $6 - local.get $1 - if (result i32) ;; label = @23 - i32.const 1659 - else - i32.const 1657 - end - local.set $8 - br 11 (;@11;) - end - end - end - local.get $16 - i64.load - local.set $50 - i32.const 0 - local.set $6 - i32.const 1657 - local.set $8 - br 8 (;@11;) - end - local.get $39 - local.get $16 - i64.load - i64.store8 - local.get $39 - local.set $1 - local.get $7 - local.set $12 - i32.const 1 - local.set $7 - i32.const 0 - local.set $6 - i32.const 1657 - local.set $8 - local.get $21 - local.set $5 - br 12 (;@6;) - end - call $12 - i32.load - call $24 - local.set $1 - br 7 (;@10;) - end - local.get $16 - i32.load - local.tee $1 - i32.eqz - if ;; label = @17 - i32.const 1667 - local.set $1 - end - br 6 (;@10;) - end - local.get $37 - local.get $16 - i64.load - i64.store32 - local.get $42 - i32.const 0 - i32.store - local.get $16 - local.get $37 - i32.store - local.get $37 - local.set $7 - i32.const -1 - local.set $6 - br 6 (;@9;) - end - local.get $16 - i32.load - local.set $7 - local.get $5 - if ;; label = @15 - block ;; label = @16 - local.get $5 - local.set $6 - br 7 (;@9;) - end - else - block ;; label = @16 - local.get $0 - i32.const 32 - local.get $10 - i32.const 0 - local.get $12 - call $25 - i32.const 0 - local.set $1 - br 8 (;@8;) - end - end - end - local.get $16 - f64.load - local.set $52 - local.get $20 - i32.const 0 - i32.store - local.get $52 - i64.reinterpret_f64 - i64.const 0 - i64.lt_s - if (result i32) ;; label = @14 - block (result i32) ;; label = @15 - i32.const 1 - local.set $24 - local.get $52 - f64.neg - local.set $52 - i32.const 1674 - end - else - block (result i32) ;; label = @15 - local.get $12 - i32.const 1 - i32.and - local.set $1 - local.get $12 - i32.const 2048 - i32.and - if (result i32) ;; label = @16 - block (result i32) ;; label = @17 - i32.const 1 - local.set $24 - i32.const 1677 - end - else - block (result i32) ;; label = @17 - local.get $1 - local.set $24 - local.get $1 - if (result i32) ;; label = @18 - i32.const 1680 - else - i32.const 1675 - end - end - end - end - end - local.set $26 - block $label$119 ;; label = @14 - local.get $52 - i64.reinterpret_f64 - i64.const 9218868437227405312 - i64.and - i64.const 9218868437227405312 - i64.lt_u - if ;; label = @15 - block ;; label = @16 - local.get $52 - local.get $20 - call $27 - f64.const 0x1p+1 (;=2;) - f64.mul - local.tee $52 - f64.const 0x0p+0 (;=0;) - f64.ne - local.tee $1 - if ;; label = @17 - local.get $20 - local.get $20 - i32.load - i32.const -1 - i32.add - i32.store - end - local.get $9 - i32.const 32 - i32.or - local.tee $22 - i32.const 97 - i32.eq - if ;; label = @17 - block ;; label = @18 - local.get $26 - i32.const 9 - i32.add - local.set $1 - local.get $9 - i32.const 32 - i32.and - local.tee $6 - if ;; label = @19 - local.get $1 - local.set $26 - end - local.get $5 - i32.const 11 - i32.gt_u - i32.const 12 - local.get $5 - i32.sub - local.tee $1 - i32.eqz - i32.or - i32.eqz - if ;; label = @19 - block ;; label = @20 - f64.const 0x1p+3 (;=8;) - local.set $53 - loop $label$125 ;; label = @21 - local.get $53 - f64.const 0x1p+4 (;=16;) - f64.mul - local.set $53 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - br_if 0 (;@21;) - end - local.get $26 - i32.load8_s - i32.const 45 - i32.eq - if (result f64) ;; label = @21 - local.get $53 - local.get $52 - f64.neg - local.get $53 - f64.sub - f64.add - f64.neg - else - local.get $52 - local.get $53 - f64.add - local.get $53 - f64.sub - end - local.set $52 - end - end - i32.const 0 - local.get $20 - i32.load - local.tee $7 - i32.sub - local.set $1 - local.get $7 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @19 - local.get $1 - else - local.get $7 - end - i64.extend_i32_s - local.get $33 - call $23 - local.tee $1 - local.get $33 - i32.eq - if ;; label = @19 - block ;; label = @20 - local.get $40 - i32.const 48 - i32.store8 - local.get $40 - local.set $1 - end - end - local.get $24 - i32.const 2 - i32.or - local.set $13 - local.get $1 - i32.const -1 - i32.add - local.get $7 - i32.const 31 - i32.shr_s - i32.const 2 - i32.and - i32.const 43 - i32.add - i32.store8 - local.get $1 - i32.const -2 - i32.add - local.tee $8 - local.get $9 - i32.const 15 - i32.add - i32.store8 - local.get $5 - i32.const 1 - i32.lt_s - local.set $9 - local.get $12 - i32.const 8 - i32.and - i32.eqz - local.set $14 - local.get $19 - local.set $1 - loop $label$131 ;; label = @19 - local.get $1 - local.get $52 - i32.trunc_f64_s - local.tee $7 - i32.const 1641 - i32.add - i32.load8_u - local.get $6 - i32.or - i32.store8 - local.get $52 - local.get $7 - f64.convert_i32_s - f64.sub - f64.const 0x1p+4 (;=16;) - f64.mul - local.set $52 - block $label$132 (result i32) ;; label = @20 - local.get $1 - i32.const 1 - i32.add - local.tee $7 - local.get $27 - i32.sub - i32.const 1 - i32.eq - if (result i32) ;; label = @21 - block (result i32) ;; label = @22 - local.get $7 - local.get $14 - local.get $9 - local.get $52 - f64.const 0x0p+0 (;=0;) - f64.eq - i32.and - i32.and - br_if 2 (;@20;) - drop - local.get $7 - i32.const 46 - i32.store8 - local.get $1 - i32.const 2 - i32.add - end - else - local.get $7 - end - end - local.set $1 - local.get $52 - f64.const 0x0p+0 (;=0;) - f64.ne - br_if 0 (;@19;) - end - local.get $46 - local.get $5 - i32.add - local.get $8 - local.tee $7 - i32.sub - local.set $6 - local.get $44 - local.get $7 - i32.sub - local.get $1 - i32.add - local.set $9 - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - i32.const 0 - i32.ne - local.get $45 - local.get $1 - i32.add - local.get $5 - i32.lt_s - i32.and - if (result i32) ;; label = @19 - local.get $6 - else - local.get $9 - local.tee $6 - end - local.get $13 - i32.add - local.tee $5 - local.get $12 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $26 - local.get $13 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $5 - local.get $12 - i32.const 65536 - i32.xor - call $25 - local.get $1 - local.get $27 - i32.sub - local.set $1 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $19 - local.get $1 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $6 - local.get $1 - local.get $28 - local.get $7 - i32.sub - local.tee $1 - i32.add - i32.sub - i32.const 0 - i32.const 0 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $8 - local.get $1 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $5 - local.get $10 - i32.ge_s - if ;; label = @19 - local.get $5 - local.set $10 - end - br 4 (;@14;) - end - end - local.get $1 - if ;; label = @17 - block ;; label = @18 - local.get $20 - local.get $20 - i32.load - i32.const -28 - i32.add - local.tee $6 - i32.store - local.get $52 - f64.const 0x1p+28 (;=268435456;) - f64.mul - local.set $52 - end - else - local.get $20 - i32.load - local.set $6 - end - local.get $6 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @17 - local.get $47 - else - local.get $48 - end - local.tee $7 - local.set $8 - loop $label$145 ;; label = @17 - local.get $8 - local.get $52 - i32.trunc_f64_s - local.tee $1 - i32.store - local.get $8 - i32.const 4 - i32.add - local.set $8 - local.get $52 - local.get $1 - f64.convert_i32_u - f64.sub - f64.const 0x1.dcd65p+29 (;=1000000000;) - f64.mul - local.tee $52 - f64.const 0x0p+0 (;=0;) - f64.ne - br_if 0 (;@17;) - end - local.get $6 - i32.const 0 - i32.gt_s - if ;; label = @17 - block ;; label = @18 - local.get $7 - local.set $1 - loop $label$147 ;; label = @19 - local.get $6 - i32.const 29 - i32.gt_s - if (result i32) ;; label = @20 - i32.const 29 - else - local.get $6 - end - local.set $14 - block $label$150 ;; label = @20 - local.get $8 - i32.const -4 - i32.add - local.tee $6 - local.get $1 - i32.ge_u - if ;; label = @21 - block ;; label = @22 - local.get $14 - i64.extend_i32_u - local.set $50 - i32.const 0 - local.set $13 - loop $label$152 ;; label = @23 - local.get $6 - local.get $6 - i32.load - i64.extend_i32_u - local.get $50 - i64.shl - local.get $13 - i64.extend_i32_u - i64.add - local.tee $51 - i64.const 1000000000 - i64.rem_u - i64.store32 - local.get $51 - i64.const 1000000000 - i64.div_u - i32.wrap_i64 - local.set $13 - local.get $6 - i32.const -4 - i32.add - local.tee $6 - local.get $1 - i32.ge_u - br_if 0 (;@23;) - end - local.get $13 - i32.eqz - br_if 2 (;@20;) - local.get $1 - i32.const -4 - i32.add - local.tee $1 - local.get $13 - i32.store - end - end - end - loop $label$153 ;; label = @20 - local.get $8 - local.get $1 - i32.gt_u - if ;; label = @21 - local.get $8 - i32.const -4 - i32.add - local.tee $6 - i32.load - i32.eqz - if ;; label = @22 - block ;; label = @23 - local.get $6 - local.set $8 - br 3 (;@20;) - end - end - end - end - local.get $20 - local.get $20 - i32.load - local.get $14 - i32.sub - local.tee $6 - i32.store - local.get $6 - i32.const 0 - i32.gt_s - br_if 0 (;@19;) - end - end - else - local.get $7 - local.set $1 - end - local.get $5 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @17 - i32.const 6 - else - local.get $5 - end - local.set $18 - local.get $6 - i32.const 0 - i32.lt_s - if ;; label = @17 - block ;; label = @18 - local.get $18 - i32.const 25 - i32.add - i32.const 9 - i32.div_s - i32.const 1 - i32.add - local.set $14 - local.get $22 - i32.const 102 - i32.eq - local.set $25 - local.get $8 - local.set $5 - loop $label$160 ;; label = @19 - i32.const 0 - local.get $6 - i32.sub - local.tee $13 - i32.const 9 - i32.gt_s - if ;; label = @20 - i32.const 9 - local.set $13 - end - block $label$162 ;; label = @20 - local.get $1 - local.get $5 - i32.lt_u - if ;; label = @21 - block ;; label = @22 - i32.const 1 - local.get $13 - i32.shl - i32.const -1 - i32.add - local.set $29 - i32.const 1000000000 - local.get $13 - i32.shr_u - local.set $35 - i32.const 0 - local.set $6 - local.get $1 - local.set $8 - loop $label$164 ;; label = @23 - local.get $8 - local.get $8 - i32.load - local.tee $32 - local.get $13 - i32.shr_u - local.get $6 - i32.add - i32.store - local.get $32 - local.get $29 - i32.and - local.get $35 - i32.mul - local.set $6 - local.get $8 - i32.const 4 - i32.add - local.tee $8 - local.get $5 - i32.lt_u - br_if 0 (;@23;) - end - local.get $1 - i32.const 4 - i32.add - local.set $8 - local.get $1 - i32.load - i32.eqz - if ;; label = @23 - local.get $8 - local.set $1 - end - local.get $6 - i32.eqz - br_if 2 (;@20;) - local.get $5 - local.get $6 - i32.store - local.get $5 - i32.const 4 - i32.add - local.set $5 - end - else - block ;; label = @22 - local.get $1 - i32.const 4 - i32.add - local.set $8 - local.get $1 - i32.load - i32.eqz - if ;; label = @23 - local.get $8 - local.set $1 - end - end - end - end - local.get $25 - if (result i32) ;; label = @20 - local.get $7 - else - local.get $1 - end - local.tee $8 - local.get $14 - i32.const 2 - i32.shl - i32.add - local.set $6 - local.get $5 - local.get $8 - i32.sub - i32.const 2 - i32.shr_s - local.get $14 - i32.gt_s - if ;; label = @20 - local.get $6 - local.set $5 - end - local.get $20 - local.get $20 - i32.load - local.get $13 - i32.add - local.tee $6 - i32.store - local.get $6 - i32.const 0 - i32.lt_s - br_if 0 (;@19;) - local.get $5 - local.set $13 - end - end - else - local.get $8 - local.set $13 - end - local.get $7 - local.set $25 - block $label$172 ;; label = @17 - local.get $1 - local.get $13 - i32.lt_u - if ;; label = @18 - block ;; label = @19 - local.get $25 - local.get $1 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - local.set $5 - local.get $1 - i32.load - local.tee $6 - i32.const 10 - i32.lt_u - br_if 2 (;@17;) - i32.const 10 - local.set $8 - loop $label$174 ;; label = @20 - local.get $5 - i32.const 1 - i32.add - local.set $5 - local.get $6 - local.get $8 - i32.const 10 - i32.mul - local.tee $8 - i32.ge_u - br_if 0 (;@20;) - end - end - else - i32.const 0 - local.set $5 - end - end - local.get $22 - i32.const 103 - i32.eq - local.set $29 - local.get $18 - i32.const 0 - i32.ne - local.set $35 - local.get $18 - local.get $22 - i32.const 102 - i32.ne - if (result i32) ;; label = @17 - local.get $5 - else - i32.const 0 - end - i32.sub - local.get $35 - local.get $29 - i32.and - i32.const 31 - i32.shl - i32.const 31 - i32.shr_s - i32.add - local.tee $8 - local.get $13 - local.get $25 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - i32.const -9 - i32.add - i32.lt_s - if ;; label = @17 - block ;; label = @18 - local.get $8 - i32.const 9216 - i32.add - local.tee $14 - i32.const 9 - i32.rem_s - i32.const 1 - i32.add - local.tee $8 - i32.const 9 - i32.lt_s - if ;; label = @19 - block ;; label = @20 - i32.const 10 - local.set $6 - loop $label$180 ;; label = @21 - local.get $6 - i32.const 10 - i32.mul - local.set $6 - local.get $8 - i32.const 1 - i32.add - local.tee $8 - i32.const 9 - i32.ne - br_if 0 (;@21;) - end - end - else - i32.const 10 - local.set $6 - end - local.get $7 - i32.const 4 - i32.add - local.get $14 - i32.const 9 - i32.div_s - i32.const -1024 - i32.add - i32.const 2 - i32.shl - i32.add - local.tee $8 - i32.load - local.tee $22 - local.get $6 - i32.rem_u - local.set $14 - block $label$182 ;; label = @19 - local.get $8 - i32.const 4 - i32.add - local.get $13 - i32.eq - local.tee $32 - local.get $14 - i32.eqz - i32.and - i32.eqz - if ;; label = @20 - block ;; label = @21 - local.get $14 - local.get $6 - i32.const 2 - i32.div_s - local.tee $49 - i32.lt_u - if (result f64) ;; label = @22 - f64.const 0x1p-1 (;=0.5;) - else - local.get $32 - local.get $14 - local.get $49 - i32.eq - i32.and - if (result f64) ;; label = @23 - f64.const 0x1p+0 (;=1;) - else - f64.const 0x1.8p+0 (;=1.5;) - end - end - local.set $52 - local.get $22 - local.get $6 - i32.div_u - i32.const 1 - i32.and - if (result f64) ;; label = @22 - f64.const 0x1.0000000000001p+53 (;=9007199254740994;) - else - f64.const 0x1p+53 (;=9007199254740992;) - end - local.set $53 - block $label$190 ;; label = @22 - local.get $24 - if ;; label = @23 - block ;; label = @24 - local.get $26 - i32.load8_s - i32.const 45 - i32.ne - br_if 2 (;@22;) - local.get $53 - f64.neg - local.set $53 - local.get $52 - f64.neg - local.set $52 - end - end - end - local.get $8 - local.get $22 - local.get $14 - i32.sub - local.tee $14 - i32.store - local.get $53 - local.get $52 - f64.add - local.get $53 - f64.eq - br_if 2 (;@19;) - local.get $8 - local.get $14 - local.get $6 - i32.add - local.tee $5 - i32.store - local.get $5 - i32.const 999999999 - i32.gt_u - if ;; label = @22 - loop $label$193 ;; label = @23 - local.get $8 - i32.const 0 - i32.store - local.get $8 - i32.const -4 - i32.add - local.tee $8 - local.get $1 - i32.lt_u - if ;; label = @24 - local.get $1 - i32.const -4 - i32.add - local.tee $1 - i32.const 0 - i32.store - end - local.get $8 - local.get $8 - i32.load - i32.const 1 - i32.add - local.tee $5 - i32.store - local.get $5 - i32.const 999999999 - i32.gt_u - br_if 0 (;@23;) - end - end - local.get $25 - local.get $1 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - local.set $5 - local.get $1 - i32.load - local.tee $14 - i32.const 10 - i32.lt_u - br_if 2 (;@19;) - i32.const 10 - local.set $6 - loop $label$195 ;; label = @22 - local.get $5 - i32.const 1 - i32.add - local.set $5 - local.get $14 - local.get $6 - i32.const 10 - i32.mul - local.tee $6 - i32.ge_u - br_if 0 (;@22;) - end - end - end - end - local.get $1 - local.set $14 - local.get $5 - local.set $6 - local.get $13 - local.get $8 - i32.const 4 - i32.add - local.tee $8 - i32.le_u - if ;; label = @19 - local.get $13 - local.set $8 - end - end - else - block ;; label = @18 - local.get $1 - local.set $14 - local.get $5 - local.set $6 - local.get $13 - local.set $8 - end - end - i32.const 0 - local.get $6 - i32.sub - local.set $32 - loop $label$198 ;; label = @17 - block $label$199 ;; label = @18 - local.get $8 - local.get $14 - i32.le_u - if ;; label = @19 - block ;; label = @20 - i32.const 0 - local.set $22 - br 2 (;@18;) - end - end - local.get $8 - i32.const -4 - i32.add - local.tee $1 - i32.load - if ;; label = @19 - i32.const 1 - local.set $22 - else - block ;; label = @20 - local.get $1 - local.set $8 - br 3 (;@17;) - end - end - end - end - block $label$203 ;; label = @17 - local.get $29 - if ;; label = @18 - block ;; label = @19 - local.get $35 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $18 - i32.add - local.tee $1 - local.get $6 - i32.gt_s - local.get $6 - i32.const -5 - i32.gt_s - i32.and - if (result i32) ;; label = @20 - block (result i32) ;; label = @21 - local.get $9 - i32.const -1 - i32.add - local.set $5 - local.get $1 - i32.const -1 - i32.add - local.get $6 - i32.sub - end - else - block (result i32) ;; label = @21 - local.get $9 - i32.const -2 - i32.add - local.set $5 - local.get $1 - i32.const -1 - i32.add - end - end - local.set $1 - local.get $12 - i32.const 8 - i32.and - local.tee $13 - br_if 2 (;@17;) - block $label$207 ;; label = @20 - local.get $22 - if ;; label = @21 - block ;; label = @22 - local.get $8 - i32.const -4 - i32.add - i32.load - local.tee $18 - i32.eqz - if ;; label = @23 - block ;; label = @24 - i32.const 9 - local.set $9 - br 4 (;@20;) - end - end - local.get $18 - i32.const 10 - i32.rem_u - if ;; label = @23 - block ;; label = @24 - i32.const 0 - local.set $9 - br 4 (;@20;) - end - else - block ;; label = @24 - i32.const 10 - local.set $13 - i32.const 0 - local.set $9 - end - end - loop $label$212 ;; label = @23 - local.get $9 - i32.const 1 - i32.add - local.set $9 - local.get $18 - local.get $13 - i32.const 10 - i32.mul - local.tee $13 - i32.rem_u - i32.eqz - br_if 0 (;@23;) - end - end - else - i32.const 9 - local.set $9 - end - end - local.get $8 - local.get $25 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - i32.const -9 - i32.add - local.set $18 - local.get $5 - i32.const 32 - i32.or - i32.const 102 - i32.eq - if ;; label = @20 - block ;; label = @21 - i32.const 0 - local.set $13 - local.get $1 - local.get $18 - local.get $9 - i32.sub - local.tee $9 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @22 - i32.const 0 - local.tee $9 - else - local.get $9 - end - i32.ge_s - if ;; label = @22 - local.get $9 - local.set $1 - end - end - else - block ;; label = @21 - i32.const 0 - local.set $13 - local.get $1 - local.get $18 - local.get $6 - i32.add - local.get $9 - i32.sub - local.tee $9 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @22 - i32.const 0 - local.tee $9 - else - local.get $9 - end - i32.ge_s - if ;; label = @22 - local.get $9 - local.set $1 - end - end - end - end - else - block ;; label = @19 - local.get $12 - i32.const 8 - i32.and - local.set $13 - local.get $18 - local.set $1 - local.get $9 - local.set $5 - end - end - end - local.get $5 - i32.const 32 - i32.or - i32.const 102 - i32.eq - local.tee $25 - if ;; label = @17 - block ;; label = @18 - i32.const 0 - local.set $9 - local.get $6 - i32.const 0 - i32.le_s - if ;; label = @19 - i32.const 0 - local.set $6 - end - end - else - block ;; label = @18 - local.get $28 - local.get $6 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @19 - local.get $32 - else - local.get $6 - end - i64.extend_i32_s - local.get $33 - call $23 - local.tee $9 - i32.sub - i32.const 2 - i32.lt_s - if ;; label = @19 - loop $label$229 ;; label = @20 - local.get $9 - i32.const -1 - i32.add - local.tee $9 - i32.const 48 - i32.store8 - local.get $28 - local.get $9 - i32.sub - i32.const 2 - i32.lt_s - br_if 0 (;@20;) - end - end - local.get $9 - i32.const -1 - i32.add - local.get $6 - i32.const 31 - i32.shr_s - i32.const 2 - i32.and - i32.const 43 - i32.add - i32.store8 - local.get $9 - i32.const -2 - i32.add - local.tee $6 - local.get $5 - i32.store8 - local.get $6 - local.set $9 - local.get $28 - local.get $6 - i32.sub - local.set $6 - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $24 - i32.const 1 - i32.add - local.get $1 - i32.add - local.get $1 - local.get $13 - i32.or - local.tee $29 - i32.const 0 - i32.ne - i32.add - local.get $6 - i32.add - local.tee $18 - local.get $12 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - local.get $26 - local.get $24 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $18 - local.get $12 - i32.const 65536 - i32.xor - call $25 - block $label$231 ;; label = @17 - local.get $25 - if ;; label = @18 - block ;; label = @19 - local.get $14 - local.get $7 - i32.gt_u - if (result i32) ;; label = @20 - local.get $7 - else - local.get $14 - end - local.tee $9 - local.set $6 - loop $label$235 ;; label = @20 - local.get $6 - i32.load - i64.extend_i32_u - local.get $31 - call $23 - local.set $5 - block $label$236 ;; label = @21 - local.get $6 - local.get $9 - i32.eq - if ;; label = @22 - block ;; label = @23 - local.get $5 - local.get $31 - i32.ne - br_if 2 (;@21;) - local.get $34 - i32.const 48 - i32.store8 - local.get $34 - local.set $5 - end - else - block ;; label = @23 - local.get $5 - local.get $19 - i32.le_u - br_if 2 (;@21;) - local.get $19 - i32.const 48 - local.get $5 - local.get $27 - i32.sub - call $39 - drop - loop $label$239 ;; label = @24 - local.get $5 - i32.const -1 - i32.add - local.tee $5 - local.get $19 - i32.gt_u - br_if 0 (;@24;) - end - end - end - end - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @21 - local.get $5 - local.get $41 - local.get $5 - i32.sub - local.get $0 - call $21 - drop - end - local.get $6 - i32.const 4 - i32.add - local.tee $5 - local.get $7 - i32.le_u - if ;; label = @21 - block ;; label = @22 - local.get $5 - local.set $6 - br 2 (;@20;) - end - end - end - block $label$242 ;; label = @20 - local.get $29 - if ;; label = @21 - block ;; label = @22 - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@20;) - i32.const 1709 - i32.const 1 - local.get $0 - call $21 - drop - end - end - end - local.get $1 - i32.const 0 - i32.gt_s - local.get $5 - local.get $8 - i32.lt_u - i32.and - if ;; label = @20 - loop $label$245 ;; label = @21 - local.get $5 - i32.load - i64.extend_i32_u - local.get $31 - call $23 - local.tee $7 - local.get $19 - i32.gt_u - if ;; label = @22 - block ;; label = @23 - local.get $19 - i32.const 48 - local.get $7 - local.get $27 - i32.sub - call $39 - drop - loop $label$247 ;; label = @24 - local.get $7 - i32.const -1 - i32.add - local.tee $7 - local.get $19 - i32.gt_u - br_if 0 (;@24;) - end - end - end - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @22 - local.get $7 - local.get $1 - i32.const 9 - i32.gt_s - if (result i32) ;; label = @23 - i32.const 9 - else - local.get $1 - end - local.get $0 - call $21 - drop - end - local.get $1 - i32.const -9 - i32.add - local.set $7 - local.get $1 - i32.const 9 - i32.gt_s - local.get $5 - i32.const 4 - i32.add - local.tee $5 - local.get $8 - i32.lt_u - i32.and - if ;; label = @22 - block ;; label = @23 - local.get $7 - local.set $1 - br 2 (;@21;) - end - else - local.get $7 - local.set $1 - end - end - end - local.get $0 - i32.const 48 - local.get $1 - i32.const 9 - i32.add - i32.const 9 - i32.const 0 - call $25 - end - else - block ;; label = @19 - local.get $14 - i32.const 4 - i32.add - local.set $5 - local.get $22 - i32.eqz - if ;; label = @20 - local.get $5 - local.set $8 - end - local.get $1 - i32.const -1 - i32.gt_s - if ;; label = @20 - block ;; label = @21 - local.get $13 - i32.eqz - local.set $13 - local.get $14 - local.set $7 - local.get $1 - local.set $5 - loop $label$256 ;; label = @22 - local.get $7 - i32.load - i64.extend_i32_u - local.get $31 - call $23 - local.tee $1 - local.get $31 - i32.eq - if ;; label = @23 - block ;; label = @24 - local.get $34 - i32.const 48 - i32.store8 - local.get $34 - local.set $1 - end - end - block $label$258 ;; label = @23 - local.get $7 - local.get $14 - i32.eq - if ;; label = @24 - block ;; label = @25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @26 - local.get $1 - i32.const 1 - local.get $0 - call $21 - drop - end - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $13 - local.get $5 - i32.const 1 - i32.lt_s - i32.and - br_if 2 (;@23;) - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@23;) - i32.const 1709 - i32.const 1 - local.get $0 - call $21 - drop - end - else - block ;; label = @25 - local.get $1 - local.get $19 - i32.le_u - br_if 2 (;@23;) - local.get $19 - i32.const 48 - local.get $1 - local.get $43 - i32.add - call $39 - drop - loop $label$262 ;; label = @26 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $19 - i32.gt_u - br_if 0 (;@26;) - end - end - end - end - local.get $41 - local.get $1 - i32.sub - local.set $6 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @23 - local.get $1 - local.get $5 - local.get $6 - i32.gt_s - if (result i32) ;; label = @24 - local.get $6 - else - local.get $5 - end - local.get $0 - call $21 - drop - end - local.get $7 - i32.const 4 - i32.add - local.tee $7 - local.get $8 - i32.lt_u - local.get $5 - local.get $6 - i32.sub - local.tee $5 - i32.const -1 - i32.gt_s - i32.and - br_if 0 (;@22;) - local.get $5 - local.set $1 - end - end - end - local.get $0 - i32.const 48 - local.get $1 - i32.const 18 - i32.add - i32.const 18 - i32.const 0 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@17;) - local.get $9 - local.get $28 - local.get $9 - i32.sub - local.get $0 - call $21 - drop - end - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $18 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $18 - local.get $10 - i32.ge_s - if ;; label = @17 - local.get $18 - local.set $10 - end - end - else - block ;; label = @16 - local.get $0 - i32.const 32 - local.get $10 - local.get $52 - local.get $52 - f64.ne - i32.const 0 - i32.or - local.tee $6 - if (result i32) ;; label = @17 - i32.const 0 - local.tee $24 - else - local.get $24 - end - i32.const 3 - i32.add - local.tee $8 - local.get $7 - call $25 - local.get $0 - i32.load - local.tee $1 - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - block ;; label = @18 - local.get $26 - local.get $24 - local.get $0 - call $21 - drop - local.get $0 - i32.load - local.set $1 - end - end - local.get $9 - i32.const 32 - i32.and - i32.const 0 - i32.ne - local.tee $5 - if (result i32) ;; label = @17 - i32.const 1693 - else - i32.const 1697 - end - local.set $7 - local.get $5 - if (result i32) ;; label = @17 - i32.const 1701 - else - i32.const 1705 - end - local.set $5 - local.get $6 - i32.eqz - if ;; label = @17 - local.get $7 - local.set $5 - end - local.get $1 - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - local.get $5 - i32.const 3 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $8 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $8 - local.get $10 - i32.ge_s - if ;; label = @17 - local.get $8 - local.set $10 - end - end - end - end - local.get $11 - local.set $1 - br 9 (;@4;) - end - local.get $5 - local.set $7 - i32.const 0 - local.set $6 - i32.const 1657 - local.set $8 - local.get $21 - local.set $5 - br 6 (;@6;) - end - local.get $9 - i32.const 32 - i32.and - local.set $7 - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.eq - if (result i32) ;; label = @12 - block (result i32) ;; label = @13 - i64.const 0 - local.set $50 - local.get $21 - end - else - block (result i32) ;; label = @13 - local.get $21 - local.set $1 - loop $label$280 ;; label = @14 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $50 - i32.wrap_i64 - i32.const 15 - i32.and - i32.const 1641 - i32.add - i32.load8_u - local.get $7 - i32.or - i32.store8 - local.get $50 - i64.const 4 - i64.shr_u - local.tee $50 - i64.const 0 - i64.ne - br_if 0 (;@14;) - end - local.get $16 - i64.load - local.set $50 - local.get $1 - end - end - local.set $7 - local.get $9 - i32.const 4 - i32.shr_s - i32.const 1657 - i32.add - local.set $8 - local.get $12 - i32.const 8 - i32.and - i32.eqz - local.get $50 - i64.const 0 - i64.eq - i32.or - local.tee $1 - if ;; label = @12 - i32.const 1657 - local.set $8 - end - local.get $1 - if (result i32) ;; label = @12 - i32.const 0 - else - i32.const 2 - end - local.set $6 - br 4 (;@7;) - end - local.get $50 - local.get $21 - call $23 - local.set $7 - br 3 (;@7;) - end - local.get $1 - i32.const 0 - local.get $5 - call $17 - local.tee $13 - i32.eqz - local.set $14 - local.get $13 - local.get $1 - i32.sub - local.set $8 - local.get $1 - local.get $5 - i32.add - local.set $9 - local.get $7 - local.set $12 - local.get $14 - if (result i32) ;; label = @10 - local.get $5 - else - local.get $8 - end - local.set $7 - i32.const 0 - local.set $6 - i32.const 1657 - local.set $8 - local.get $14 - if (result i32) ;; label = @10 - local.get $9 - else - local.get $13 - end - local.set $5 - br 3 (;@6;) - end - i32.const 0 - local.set $1 - i32.const 0 - local.set $5 - local.get $7 - local.set $8 - loop $label$288 ;; label = @9 - block $label$289 ;; label = @10 - local.get $8 - i32.load - local.tee $9 - i32.eqz - br_if 0 (;@10;) - local.get $36 - local.get $9 - call $26 - local.tee $5 - i32.const 0 - i32.lt_s - local.get $5 - local.get $6 - local.get $1 - i32.sub - i32.gt_u - i32.or - br_if 0 (;@10;) - local.get $8 - i32.const 4 - i32.add - local.set $8 - local.get $6 - local.get $5 - local.get $1 - i32.add - local.tee $1 - i32.gt_u - br_if 1 (;@9;) - end - end - local.get $5 - i32.const 0 - i32.lt_s - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $1 - local.get $12 - call $25 - local.get $1 - if ;; label = @9 - block ;; label = @10 - i32.const 0 - local.set $5 - loop $label$292 ;; label = @11 - local.get $7 - i32.load - local.tee $8 - i32.eqz - br_if 3 (;@8;) - local.get $36 - local.get $8 - call $26 - local.tee $8 - local.get $5 - i32.add - local.tee $5 - local.get $1 - i32.gt_s - br_if 3 (;@8;) - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @12 - local.get $36 - local.get $8 - local.get $0 - call $21 - drop - end - local.get $7 - i32.const 4 - i32.add - local.set $7 - local.get $5 - local.get $1 - i32.lt_u - br_if 0 (;@11;) - br 3 (;@8;) - end - end - else - block ;; label = @10 - i32.const 0 - local.set $1 - br 2 (;@8;) - end - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $1 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $10 - local.get $1 - i32.le_s - if ;; label = @8 - local.get $1 - local.set $10 - end - local.get $11 - local.set $1 - br 3 (;@4;) - end - local.get $12 - i32.const -65537 - i32.and - local.set $1 - local.get $5 - i32.const -1 - i32.gt_s - if ;; label = @7 - local.get $1 - local.set $12 - end - local.get $5 - local.get $16 - i64.load - i64.const 0 - i64.ne - local.tee $9 - i32.or - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - local.get $7 - local.set $1 - local.get $5 - local.get $9 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $38 - local.get $7 - i32.sub - i32.add - local.tee $7 - i32.gt_s - if ;; label = @9 - local.get $5 - local.set $7 - end - local.get $21 - end - else - block (result i32) ;; label = @8 - local.get $21 - local.set $1 - i32.const 0 - local.set $7 - local.get $21 - end - end - local.set $5 - end - local.get $0 - i32.const 32 - local.get $10 - local.get $7 - local.get $5 - local.get $1 - i32.sub - local.tee $9 - i32.lt_s - if (result i32) ;; label = @6 - local.get $9 - local.tee $7 - else - local.get $7 - end - local.get $6 - i32.add - local.tee $5 - i32.lt_s - if (result i32) ;; label = @6 - local.get $5 - local.tee $10 - else - local.get $10 - end - local.get $5 - local.get $12 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @6 - local.get $8 - local.get $6 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $5 - local.get $12 - i32.const 65536 - i32.xor - call $25 - local.get $0 - i32.const 48 - local.get $7 - local.get $9 - i32.const 0 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @6 - local.get $1 - local.get $9 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $11 - local.set $1 - br 1 (;@4;) - end - end - br 1 (;@2;) - end - local.get $0 - i32.eqz - if ;; label = @3 - local.get $17 - if ;; label = @4 - block ;; label = @5 - i32.const 1 - local.set $0 - loop $label$308 ;; label = @6 - local.get $4 - local.get $0 - i32.const 2 - i32.shl - i32.add - i32.load - local.tee $1 - if ;; label = @7 - block ;; label = @8 - local.get $3 - local.get $0 - i32.const 3 - i32.shl - i32.add - local.get $1 - local.get $2 - call $22 - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.const 10 - i32.lt_s - br_if 2 (;@6;) - i32.const 1 - local.set $15 - br 6 (;@2;) - end - end - end - loop $label$310 ;; label = @6 - local.get $4 - local.get $0 - i32.const 2 - i32.shl - i32.add - i32.load - if ;; label = @7 - block ;; label = @8 - i32.const -1 - local.set $15 - br 6 (;@2;) - end - end - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.const 10 - i32.lt_s - br_if 0 (;@6;) - i32.const 1 - local.set $15 - end - end - else - i32.const 0 - local.set $15 - end - end - end - local.get $23 - global.set $global$1 - local.get $15 - end - ) - (func $20 (;33;) (type $1) (param $0 i32) (result i32) - i32.const 0 - ) - (func $21 (;34;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) - block $label$1 (result i32) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $2 - i32.const 16 - i32.add - local.tee $4 - i32.load - local.tee $3 - br_if 0 (;@3;) - local.get $2 - call $30 - if ;; label = @4 - i32.const 0 - local.set $3 - else - block ;; label = @5 - local.get $4 - i32.load - local.set $3 - br 2 (;@3;) - end - end - br 1 (;@2;) - end - local.get $3 - local.get $2 - i32.const 20 - i32.add - local.tee $5 - i32.load - local.tee $4 - i32.sub - local.get $1 - i32.lt_u - if ;; label = @3 - block ;; label = @4 - local.get $2 - local.get $0 - local.get $1 - local.get $2 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - local.set $3 - br 2 (;@2;) - end - end - block $label$7 (result i32) ;; label = @3 - local.get $2 - i32.load8_s offset=75 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @4 - block (result i32) ;; label = @5 - local.get $1 - local.set $3 - loop $label$9 ;; label = @6 - i32.const 0 - local.get $3 - i32.eqz - br_if 3 (;@3;) - drop - local.get $0 - local.get $3 - i32.const -1 - i32.add - local.tee $6 - i32.add - i32.load8_s - i32.const 10 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $6 - local.set $3 - br 2 (;@6;) - end - end - end - local.get $2 - local.get $0 - local.get $3 - local.get $2 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - local.get $3 - i32.lt_u - br_if 3 (;@2;) - local.get $5 - i32.load - local.set $4 - local.get $1 - local.get $3 - i32.sub - local.set $1 - local.get $0 - local.get $3 - i32.add - local.set $0 - local.get $3 - end - else - i32.const 0 - end - end - local.set $2 - local.get $4 - local.get $0 - local.get $1 - call $40 - drop - local.get $5 - local.get $5 - i32.load - local.get $1 - i32.add - i32.store - local.get $2 - local.get $1 - i32.add - local.set $3 - end - local.get $3 - end - ) - (func $22 (;35;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) (local $4 i64) (local $5 f64) - block $label$1 ;; label = @1 - local.get $1 - i32.const 20 - i32.le_u - if ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - block $label$6 ;; label = @6 - block $label$7 ;; label = @7 - block $label$8 ;; label = @8 - block $label$9 ;; label = @9 - block $label$10 ;; label = @10 - block $label$11 ;; label = @11 - block $label$12 ;; label = @12 - block $label$13 ;; label = @13 - local.get $1 - i32.const 9 - i32.sub - br_table 0 (;@13;) 1 (;@12;) 2 (;@11;) 3 (;@10;) 4 (;@9;) 5 (;@8;) 6 (;@7;) 7 (;@6;) 8 (;@5;) 9 (;@4;) 10 (;@3;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.store - br 11 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i64.extend_i32_s - i64.store - br 10 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i64.extend_i32_u - i64.store - br 9 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - i64.load - local.set $4 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $4 - i64.store - br 8 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 65535 - i32.and - i32.const 16 - i32.shl - i32.const 16 - i32.shr_s - i64.extend_i32_s - i64.store - br 7 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 65535 - i32.and - i64.extend_i32_u - i64.store - br 6 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i64.extend_i32_s - i64.store - br 5 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 255 - i32.and - i64.extend_i32_u - i64.store - br 4 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - f64.load - local.set $5 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $5 - f64.store - br 3 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - f64.load - local.set $5 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $5 - f64.store - end - end - end - ) - (func $23 (;36;) (type $9) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i64) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.wrap_i64 - local.set $2 - local.get $0 - i64.const 4294967295 - i64.gt_u - if ;; label = @2 - block ;; label = @3 - loop $label$3 ;; label = @4 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $0 - i64.const 10 - i64.rem_u - i64.const 48 - i64.or - i64.store8 - local.get $0 - i64.const 10 - i64.div_u - local.set $4 - local.get $0 - i64.const 42949672959 - i64.gt_u - if ;; label = @5 - block ;; label = @6 - local.get $4 - local.set $0 - br 2 (;@4;) - end - end - end - local.get $4 - i32.wrap_i64 - local.set $2 - end - end - local.get $2 - if ;; label = @2 - loop $label$6 ;; label = @3 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $2 - i32.const 10 - i32.rem_u - i32.const 48 - i32.or - i32.store8 - local.get $2 - i32.const 10 - i32.div_u - local.set $3 - local.get $2 - i32.const 10 - i32.ge_u - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $2 - br 2 (;@3;) - end - end - end - end - local.get $1 - end - ) - (func $24 (;37;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - i32.const 0 - local.set $1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - loop $label$5 ;; label = @5 - local.get $1 - i32.const 1711 - i32.add - i32.load8_u - local.get $0 - i32.eq - br_if 1 (;@4;) - local.get $1 - i32.const 1 - i32.add - local.tee $1 - i32.const 87 - i32.ne - br_if 0 (;@5;) - i32.const 87 - local.set $1 - i32.const 1799 - local.set $0 - br 2 (;@3;) - end - end - local.get $1 - if ;; label = @4 - block ;; label = @5 - i32.const 1799 - local.set $0 - br 2 (;@3;) - end - else - i32.const 1799 - local.set $0 - end - br 1 (;@2;) - end - loop $label$8 ;; label = @3 - local.get $0 - local.set $2 - loop $label$9 ;; label = @4 - local.get $2 - i32.const 1 - i32.add - local.set $0 - local.get $2 - i32.load8_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.set $2 - br 2 (;@4;) - end - end - end - local.get $1 - i32.const -1 - i32.add - local.tee $1 - br_if 0 (;@3;) - end - end - local.get $0 - end - ) - (func $25 (;38;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) - (local $5 i32) (local $6 i32) (local $7 i32) - block $label$1 ;; label = @1 - global.get $global$1 - local.set $7 - global.get $global$1 - i32.const 256 - i32.add - global.set $global$1 - local.get $7 - local.set $6 - block $label$2 ;; label = @2 - local.get $2 - local.get $3 - i32.gt_s - local.get $4 - i32.const 73728 - i32.and - i32.eqz - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $6 - local.get $1 - local.get $2 - local.get $3 - i32.sub - local.tee $5 - i32.const 256 - i32.gt_u - if (result i32) ;; label = @5 - i32.const 256 - else - local.get $5 - end - call $39 - drop - local.get $0 - i32.load - local.tee $1 - i32.const 32 - i32.and - i32.eqz - local.set $4 - local.get $5 - i32.const 255 - i32.gt_u - if ;; label = @5 - block ;; label = @6 - loop $label$7 ;; label = @7 - local.get $4 - if ;; label = @8 - block ;; label = @9 - local.get $6 - i32.const 256 - local.get $0 - call $21 - drop - local.get $0 - i32.load - local.set $1 - end - end - local.get $1 - i32.const 32 - i32.and - i32.eqz - local.set $4 - local.get $5 - i32.const -256 - i32.add - local.tee $5 - i32.const 255 - i32.gt_u - br_if 0 (;@7;) - end - local.get $4 - i32.eqz - br_if 4 (;@2;) - local.get $2 - local.get $3 - i32.sub - i32.const 255 - i32.and - local.set $5 - end - else - local.get $4 - i32.eqz - br_if 3 (;@2;) - end - local.get $6 - local.get $5 - local.get $0 - call $21 - drop - end - end - end - local.get $7 - global.set $global$1 - end - ) - (func $26 (;39;) (type $4) (param $0 i32) (param $1 i32) (result i32) - local.get $0 - if (result i32) ;; label = @1 - local.get $0 - local.get $1 - i32.const 0 - call $29 - else - i32.const 0 - end - ) - (func $27 (;40;) (type $11) (param $0 f64) (param $1 i32) (result f64) - local.get $0 - local.get $1 - call $28 - ) - (func $28 (;41;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (local $2 i64) (local $3 i64) - block $label$1 (result f64) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - local.get $0 - i64.reinterpret_f64 - local.tee $2 - i64.const 52 - i64.shr_u - local.tee $3 - i32.wrap_i64 - i32.const 65535 - i32.and - i32.const 2047 - i32.and - i32.const 16 - i32.shl - i32.const 16 - i32.shr_s - i32.const 0 - i32.sub - br_table 0 (;@5;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 1 (;@4;) 2 (;@3;) - end - local.get $1 - local.get $0 - f64.const 0x0p+0 (;=0;) - f64.ne - if (result i32) ;; label = @5 - block (result i32) ;; label = @6 - local.get $0 - f64.const 0x1p+64 (;=18446744073709552000;) - f64.mul - local.get $1 - call $28 - local.set $0 - local.get $1 - i32.load - i32.const -64 - i32.add - end - else - i32.const 0 - end - i32.store - br 2 (;@2;) - end - br 1 (;@2;) - end - local.get $1 - local.get $3 - i32.wrap_i64 - i32.const 2047 - i32.and - i32.const -1022 - i32.add - i32.store - local.get $2 - i64.const -9218868437227405313 - i64.and - i64.const 4602678819172646912 - i64.or - f64.reinterpret_i64 - local.set $0 - end - local.get $0 - end - ) - (func $29 (;42;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $1 - i32.const 128 - i32.lt_u - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.store8 - i32.const 1 - br 4 (;@1;) - end - end - local.get $1 - i32.const 2048 - i32.lt_u - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 192 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - i32.const 2 - br 4 (;@1;) - end - end - local.get $1 - i32.const 55296 - i32.lt_u - local.get $1 - i32.const -8192 - i32.and - i32.const 57344 - i32.eq - i32.or - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.const 12 - i32.shr_u - i32.const 224 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=2 - i32.const 3 - br 4 (;@1;) - end - end - local.get $1 - i32.const -65536 - i32.add - i32.const 1048576 - i32.lt_u - if (result i32) ;; label = @4 - block (result i32) ;; label = @5 - local.get $0 - local.get $1 - i32.const 18 - i32.shr_u - i32.const 240 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 12 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=2 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=3 - i32.const 4 - end - else - block (result i32) ;; label = @5 - call $12 - i32.const 84 - i32.store - i32.const -1 - end - end - end - else - i32.const 1 - end - end - ) - (func $30 (;43;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.const 74 - i32.add - local.tee $2 - i32.load8_s - local.set $1 - local.get $2 - local.get $1 - i32.const 255 - i32.add - local.get $1 - i32.or - i32.store8 - local.get $0 - i32.load - local.tee $1 - i32.const 8 - i32.and - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $0 - local.get $1 - i32.const 32 - i32.or - i32.store - i32.const -1 - end - else - block (result i32) ;; label = @3 - local.get $0 - i32.const 0 - i32.store offset=8 - local.get $0 - i32.const 0 - i32.store offset=4 - local.get $0 - local.get $0 - i32.load offset=44 - local.tee $1 - i32.store offset=28 - local.get $0 - local.get $1 - i32.store offset=20 - local.get $0 - local.get $1 - local.get $0 - i32.load offset=48 - i32.add - i32.store offset=16 - i32.const 0 - end - end - local.tee $0 - end - ) - (func $31 (;44;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $3 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $3 - local.tee $4 - local.get $1 - i32.const 255 - i32.and - local.tee $7 - i32.store8 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $0 - i32.const 16 - i32.add - local.tee $2 - i32.load - local.tee $5 - br_if 0 (;@3;) - local.get $0 - call $30 - if ;; label = @4 - i32.const -1 - local.set $1 - else - block ;; label = @5 - local.get $2 - i32.load - local.set $5 - br 2 (;@3;) - end - end - br 1 (;@2;) - end - local.get $0 - i32.const 20 - i32.add - local.tee $2 - i32.load - local.tee $6 - local.get $5 - i32.lt_u - if ;; label = @3 - local.get $1 - i32.const 255 - i32.and - local.tee $1 - local.get $0 - i32.load8_s offset=75 - i32.ne - if ;; label = @4 - block ;; label = @5 - local.get $2 - local.get $6 - i32.const 1 - i32.add - i32.store - local.get $6 - local.get $7 - i32.store8 - br 3 (;@2;) - end - end - end - local.get $0 - local.get $4 - i32.const 1 - local.get $0 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - i32.const 1 - i32.eq - if (result i32) ;; label = @3 - local.get $4 - i32.load8_u - else - i32.const -1 - end - local.set $1 - end - local.get $3 - global.set $global$1 - local.get $1 - end - ) - (func $32 (;45;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) - block $label$1 (result i32) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $1 - i32.load offset=76 - i32.const 0 - i32.lt_s - br_if 0 (;@3;) - local.get $1 - call $20 - i32.eqz - br_if 0 (;@3;) - block $label$4 (result i32) ;; label = @4 - block $label$5 ;; label = @5 - local.get $1 - i32.load8_s offset=75 - local.get $0 - i32.eq - br_if 0 (;@5;) - local.get $1 - i32.const 20 - i32.add - local.tee $3 - i32.load - local.tee $2 - local.get $1 - i32.load offset=16 - i32.ge_u - br_if 0 (;@5;) - local.get $3 - local.get $2 - i32.const 1 - i32.add - i32.store - local.get $2 - local.get $0 - i32.store8 - local.get $0 - i32.const 255 - i32.and - br 1 (;@4;) - end - local.get $1 - local.get $0 - call $31 - end - local.set $0 - local.get $1 - call $13 - br 1 (;@2;) - end - local.get $1 - i32.load8_s offset=75 - local.get $0 - i32.ne - if ;; label = @3 - local.get $1 - i32.const 20 - i32.add - local.tee $3 - i32.load - local.tee $2 - local.get $1 - i32.load offset=16 - i32.lt_u - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.get $2 - i32.const 1 - i32.add - i32.store - local.get $2 - local.get $0 - i32.store8 - local.get $0 - i32.const 255 - i32.and - local.set $0 - br 3 (;@2;) - end - end - end - local.get $1 - local.get $0 - call $31 - local.set $0 - end - local.get $0 - end - ) - (func $33 (;46;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $2 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $2 - local.tee $3 - local.get $1 - i32.store - i32.const 1024 - i32.load - local.get $0 - local.get $3 - call $18 - local.set $0 - local.get $2 - global.set $global$1 - local.get $0 - end - ) - (func $34 (;47;) (type $1) (param $0 i32) (result i32) - local.get $0 - i32.const 1024 - i32.load - call $32 - ) - (func $35 (;48;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $14 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $14 - local.set $18 - block $label$2 ;; label = @2 - local.get $0 - i32.const 245 - i32.lt_u - if ;; label = @3 - block ;; label = @4 - local.get $0 - i32.const 11 - i32.add - i32.const -8 - i32.and - local.set $3 - i32.const 3652 - i32.load - local.tee $8 - local.get $0 - i32.const 11 - i32.lt_u - if (result i32) ;; label = @5 - i32.const 16 - local.tee $3 - else - local.get $3 - end - i32.const 3 - i32.shr_u - local.tee $2 - i32.shr_u - local.tee $0 - i32.const 3 - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $0 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $2 - i32.add - local.tee $5 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.tee $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $7 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.set $4 - local.get $2 - local.get $4 - i32.eq - if ;; label = @7 - i32.const 3652 - local.get $8 - i32.const 1 - local.get $5 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - else - block ;; label = @8 - local.get $4 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $4 - i32.const 12 - i32.add - local.tee $0 - i32.load - local.get $7 - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $0 - local.get $2 - i32.store - local.get $3 - local.get $4 - i32.store - end - else - call $fimport$10 - end - end - end - local.get $7 - local.get $5 - i32.const 3 - i32.shl - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $7 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - local.get $14 - global.set $global$1 - local.get $1 - return - end - end - local.get $3 - i32.const 3660 - i32.load - local.tee $16 - i32.gt_u - if ;; label = @5 - block ;; label = @6 - local.get $0 - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.get $2 - i32.shl - i32.const 2 - local.get $2 - i32.shl - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.or - i32.and - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $5 - local.get $0 - local.get $5 - i32.shr_u - local.tee $2 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $5 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - i32.add - local.tee $11 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.tee $4 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $9 - i32.const 8 - i32.add - local.tee $5 - i32.load - local.set $12 - local.get $4 - local.get $12 - i32.eq - if ;; label = @9 - i32.const 3652 - local.get $8 - i32.const 1 - local.get $11 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $7 - i32.store - else - block ;; label = @10 - local.get $12 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$10 - end - local.get $12 - i32.const 12 - i32.add - local.tee $0 - i32.load - local.get $9 - i32.eq - if ;; label = @11 - block ;; label = @12 - local.get $0 - local.get $4 - i32.store - local.get $2 - local.get $12 - i32.store - local.get $8 - local.set $7 - end - else - call $fimport$10 - end - end - end - local.get $9 - local.get $3 - i32.const 3 - i32.or - i32.store offset=4 - local.get $9 - local.get $3 - i32.add - local.tee $4 - local.get $11 - i32.const 3 - i32.shl - local.get $3 - i32.sub - local.tee $11 - i32.const 1 - i32.or - i32.store offset=4 - local.get $4 - local.get $11 - i32.add - local.get $11 - i32.store - local.get $16 - if ;; label = @9 - block ;; label = @10 - i32.const 3672 - i32.load - local.set $9 - local.get $16 - i32.const 3 - i32.shr_u - local.tee $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.set $2 - local.get $7 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @11 - local.get $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $0 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @12 - call $fimport$10 - else - block ;; label = @13 - local.get $3 - local.set $6 - local.get $0 - local.set $1 - end - end - else - block ;; label = @12 - i32.const 3652 - local.get $7 - local.get $0 - i32.or - i32.store - local.get $2 - i32.const 8 - i32.add - local.set $6 - local.get $2 - local.set $1 - end - end - local.get $6 - local.get $9 - i32.store - local.get $1 - local.get $9 - i32.store offset=12 - local.get $9 - local.get $1 - i32.store offset=8 - local.get $9 - local.get $2 - i32.store offset=12 - end - end - i32.const 3660 - local.get $11 - i32.store - i32.const 3672 - local.get $4 - i32.store - local.get $14 - global.set $global$1 - local.get $5 - return - end - end - i32.const 3656 - i32.load - local.tee $6 - if ;; label = @7 - block ;; label = @8 - local.get $6 - i32.const 0 - local.get $6 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $2 - local.get $0 - local.get $2 - i32.shr_u - local.tee $1 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $2 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - i32.add - i32.const 2 - i32.shl - i32.const 3956 - i32.add - i32.load - local.tee $2 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.sub - local.set $9 - local.get $2 - local.set $1 - loop $label$25 ;; label = @9 - block $label$26 ;; label = @10 - local.get $1 - i32.load offset=16 - local.tee $0 - i32.eqz - if ;; label = @11 - local.get $1 - i32.load offset=20 - local.tee $0 - i32.eqz - br_if 1 (;@10;) - end - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.sub - local.tee $1 - local.get $9 - i32.lt_u - local.tee $7 - if ;; label = @11 - local.get $1 - local.set $9 - end - local.get $0 - local.set $1 - local.get $7 - if ;; label = @11 - local.get $0 - local.set $2 - end - br 1 (;@9;) - end - end - local.get $2 - i32.const 3668 - i32.load - local.tee $12 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $2 - local.get $2 - local.get $3 - i32.add - local.tee $13 - i32.ge_u - if ;; label = @9 - call $fimport$10 - end - local.get $2 - i32.load offset=24 - local.set $15 - block $label$32 ;; label = @9 - local.get $2 - i32.load offset=12 - local.tee $0 - local.get $2 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $2 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @12 - local.get $2 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @13 - block ;; label = @14 - i32.const 0 - local.set $4 - br 5 (;@9;) - end - end - end - loop $label$36 ;; label = @12 - local.get $0 - i32.const 20 - i32.add - local.tee $11 - i32.load - local.tee $7 - if ;; label = @13 - block ;; label = @14 - local.get $7 - local.set $0 - local.get $11 - local.set $1 - br 2 (;@12;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $11 - i32.load - local.tee $7 - if ;; label = @13 - block ;; label = @14 - local.get $7 - local.set $0 - local.get $11 - local.set $1 - br 2 (;@12;) - end - end - end - local.get $1 - local.get $12 - i32.lt_u - if ;; label = @12 - call $fimport$10 - else - block ;; label = @13 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $4 - end - end - end - else - block ;; label = @11 - local.get $2 - i32.load offset=8 - local.tee $11 - local.get $12 - i32.lt_u - if ;; label = @12 - call $fimport$10 - end - local.get $11 - i32.const 12 - i32.add - local.tee $7 - i32.load - local.get $2 - i32.ne - if ;; label = @12 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $2 - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $7 - local.get $0 - i32.store - local.get $1 - local.get $11 - i32.store - local.get $0 - local.set $4 - end - else - call $fimport$10 - end - end - end - end - block $label$46 ;; label = @9 - local.get $15 - if ;; label = @10 - block ;; label = @11 - local.get $2 - local.get $2 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $0 - local.get $4 - i32.store - local.get $4 - i32.eqz - if ;; label = @14 - block ;; label = @15 - i32.const 3656 - local.get $6 - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 6 (;@9;) - end - end - end - else - block ;; label = @13 - local.get $15 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$10 - end - local.get $15 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $2 - i32.eq - if ;; label = @14 - local.get $0 - local.get $4 - i32.store - else - local.get $15 - local.get $4 - i32.store offset=20 - end - local.get $4 - i32.eqz - br_if 4 (;@9;) - end - end - local.get $4 - i32.const 3668 - i32.load - local.tee $0 - i32.lt_u - if ;; label = @12 - call $fimport$10 - end - local.get $4 - local.get $15 - i32.store offset=24 - local.get $2 - i32.load offset=16 - local.tee $1 - if ;; label = @12 - local.get $1 - local.get $0 - i32.lt_u - if ;; label = @13 - call $fimport$10 - else - block ;; label = @14 - local.get $4 - local.get $1 - i32.store offset=16 - local.get $1 - local.get $4 - i32.store offset=24 - end - end - end - local.get $2 - i32.load offset=20 - local.tee $0 - if ;; label = @12 - local.get $0 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @13 - call $fimport$10 - else - block ;; label = @14 - local.get $4 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $4 - i32.store offset=24 - end - end - end - end - end - end - local.get $9 - i32.const 16 - i32.lt_u - if ;; label = @9 - block ;; label = @10 - local.get $2 - local.get $9 - local.get $3 - i32.add - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - else - block ;; label = @10 - local.get $2 - local.get $3 - i32.const 3 - i32.or - i32.store offset=4 - local.get $13 - local.get $9 - i32.const 1 - i32.or - i32.store offset=4 - local.get $13 - local.get $9 - i32.add - local.get $9 - i32.store - local.get $16 - if ;; label = @11 - block ;; label = @12 - i32.const 3672 - i32.load - local.set $7 - local.get $16 - i32.const 3 - i32.shr_u - local.tee $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.set $3 - local.get $8 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @13 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$10 - else - block ;; label = @15 - local.get $1 - local.set $10 - local.get $0 - local.set $5 - end - end - else - block ;; label = @14 - i32.const 3652 - local.get $8 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $10 - local.get $3 - local.set $5 - end - end - local.get $10 - local.get $7 - i32.store - local.get $5 - local.get $7 - i32.store offset=12 - local.get $7 - local.get $5 - i32.store offset=8 - local.get $7 - local.get $3 - i32.store offset=12 - end - end - i32.const 3660 - local.get $9 - i32.store - i32.const 3672 - local.get $13 - i32.store - end - end - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - else - local.get $3 - local.set $0 - end - end - else - local.get $3 - local.set $0 - end - end - else - local.get $0 - i32.const -65 - i32.gt_u - if ;; label = @4 - i32.const -1 - local.set $0 - else - block ;; label = @5 - local.get $0 - i32.const 11 - i32.add - local.tee $0 - i32.const -8 - i32.and - local.set $7 - i32.const 3656 - i32.load - local.tee $5 - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @8 - local.get $7 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @9 - i32.const 31 - else - local.get $7 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $3 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $3 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.set $17 - i32.const 0 - local.get $7 - i32.sub - local.set $3 - block $label$78 ;; label = @8 - block $label$79 ;; label = @9 - block $label$80 ;; label = @10 - local.get $17 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - i32.load - local.tee $1 - if ;; label = @11 - block ;; label = @12 - i32.const 25 - local.get $17 - i32.const 1 - i32.shr_u - i32.sub - local.set $0 - i32.const 0 - local.set $4 - local.get $7 - local.get $17 - i32.const 31 - i32.eq - if (result i32) ;; label = @13 - i32.const 0 - else - local.get $0 - end - i32.shl - local.set $10 - i32.const 0 - local.set $0 - loop $label$84 ;; label = @13 - local.get $1 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.sub - local.tee $6 - local.get $3 - i32.lt_u - if ;; label = @14 - local.get $6 - if ;; label = @15 - block ;; label = @16 - local.get $6 - local.set $3 - local.get $1 - local.set $0 - end - else - block ;; label = @16 - i32.const 0 - local.set $3 - local.get $1 - local.set $0 - br 7 (;@9;) - end - end - end - local.get $1 - i32.load offset=20 - local.tee $19 - i32.eqz - local.get $19 - local.get $1 - i32.const 16 - i32.add - local.get $10 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - i32.load - local.tee $6 - i32.eq - i32.or - if (result i32) ;; label = @14 - local.get $4 - else - local.get $19 - end - local.set $1 - local.get $10 - local.get $6 - i32.eqz - local.tee $4 - i32.const 1 - i32.and - i32.const 1 - i32.xor - i32.shl - local.set $10 - local.get $4 - if ;; label = @14 - block ;; label = @15 - local.get $1 - local.set $4 - local.get $0 - local.set $1 - br 5 (;@10;) - end - else - block ;; label = @15 - local.get $1 - local.set $4 - local.get $6 - local.set $1 - br 2 (;@13;) - end - end - end - end - else - block ;; label = @12 - i32.const 0 - local.set $4 - i32.const 0 - local.set $1 - end - end - end - local.get $4 - i32.eqz - local.get $1 - i32.eqz - i32.and - if (result i32) ;; label = @10 - block (result i32) ;; label = @11 - local.get $5 - i32.const 2 - local.get $17 - i32.shl - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.or - i32.and - local.tee $0 - i32.eqz - if ;; label = @12 - block ;; label = @13 - local.get $7 - local.set $0 - br 11 (;@2;) - end - end - local.get $0 - i32.const 0 - local.get $0 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $10 - local.get $0 - local.get $10 - i32.shr_u - local.tee $4 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $10 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - i32.add - i32.const 2 - i32.shl - i32.const 3956 - i32.add - i32.load - end - else - local.get $4 - end - local.tee $0 - br_if 0 (;@9;) - local.get $1 - local.set $4 - br 1 (;@8;) - end - loop $label$96 ;; label = @9 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.sub - local.tee $4 - local.get $3 - i32.lt_u - local.tee $10 - if ;; label = @10 - local.get $4 - local.set $3 - end - local.get $10 - if ;; label = @10 - local.get $0 - local.set $1 - end - local.get $0 - i32.load offset=16 - local.tee $4 - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.set $0 - br 2 (;@9;) - end - end - local.get $0 - i32.load offset=20 - local.tee $0 - br_if 0 (;@9;) - local.get $1 - local.set $4 - end - end - local.get $4 - if ;; label = @8 - local.get $3 - i32.const 3660 - i32.load - local.get $7 - i32.sub - i32.lt_u - if ;; label = @9 - block ;; label = @10 - local.get $4 - i32.const 3668 - i32.load - local.tee $12 - i32.lt_u - if ;; label = @11 - call $fimport$10 - end - local.get $4 - local.get $4 - local.get $7 - i32.add - local.tee $6 - i32.ge_u - if ;; label = @11 - call $fimport$10 - end - local.get $4 - i32.load offset=24 - local.set $10 - block $label$104 ;; label = @11 - local.get $4 - i32.load offset=12 - local.tee $0 - local.get $4 - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $4 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @14 - local.get $4 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @15 - block ;; label = @16 - i32.const 0 - local.set $13 - br 5 (;@11;) - end - end - end - loop $label$108 ;; label = @14 - local.get $0 - i32.const 20 - i32.add - local.tee $9 - i32.load - local.tee $11 - if ;; label = @15 - block ;; label = @16 - local.get $11 - local.set $0 - local.get $9 - local.set $1 - br 2 (;@14;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $9 - i32.load - local.tee $11 - if ;; label = @15 - block ;; label = @16 - local.get $11 - local.set $0 - local.get $9 - local.set $1 - br 2 (;@14;) - end - end - end - local.get $1 - local.get $12 - i32.lt_u - if ;; label = @14 - call $fimport$10 - else - block ;; label = @15 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $13 - end - end - end - else - block ;; label = @13 - local.get $4 - i32.load offset=8 - local.tee $9 - local.get $12 - i32.lt_u - if ;; label = @14 - call $fimport$10 - end - local.get $9 - i32.const 12 - i32.add - local.tee $11 - i32.load - local.get $4 - i32.ne - if ;; label = @14 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $4 - i32.eq - if ;; label = @14 - block ;; label = @15 - local.get $11 - local.get $0 - i32.store - local.get $1 - local.get $9 - i32.store - local.get $0 - local.set $13 - end - else - call $fimport$10 - end - end - end - end - block $label$118 ;; label = @11 - local.get $10 - if ;; label = @12 - block ;; label = @13 - local.get $4 - local.get $4 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @14 - block ;; label = @15 - local.get $0 - local.get $13 - i32.store - local.get $13 - i32.eqz - if ;; label = @16 - block ;; label = @17 - i32.const 3656 - local.get $5 - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $2 - i32.store - br 6 (;@11;) - end - end - end - else - block ;; label = @15 - local.get $10 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @16 - call $fimport$10 - end - local.get $10 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @16 - local.get $0 - local.get $13 - i32.store - else - local.get $10 - local.get $13 - i32.store offset=20 - end - local.get $13 - i32.eqz - if ;; label = @16 - block ;; label = @17 - local.get $5 - local.set $2 - br 6 (;@11;) - end - end - end - end - local.get $13 - i32.const 3668 - i32.load - local.tee $0 - i32.lt_u - if ;; label = @14 - call $fimport$10 - end - local.get $13 - local.get $10 - i32.store offset=24 - local.get $4 - i32.load offset=16 - local.tee $1 - if ;; label = @14 - local.get $1 - local.get $0 - i32.lt_u - if ;; label = @15 - call $fimport$10 - else - block ;; label = @16 - local.get $13 - local.get $1 - i32.store offset=16 - local.get $1 - local.get $13 - i32.store offset=24 - end - end - end - local.get $4 - i32.load offset=20 - local.tee $0 - if ;; label = @14 - local.get $0 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @15 - call $fimport$10 - else - block ;; label = @16 - local.get $13 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $13 - i32.store offset=24 - local.get $5 - local.set $2 - end - end - else - local.get $5 - local.set $2 - end - end - else - local.get $5 - local.set $2 - end - end - block $label$136 ;; label = @11 - local.get $3 - i32.const 16 - i32.lt_u - if ;; label = @12 - block ;; label = @13 - local.get $4 - local.get $3 - local.get $7 - i32.add - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $4 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - else - block ;; label = @13 - local.get $4 - local.get $7 - i32.const 3 - i32.or - i32.store offset=4 - local.get $6 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $3 - i32.add - local.get $3 - i32.store - local.get $3 - i32.const 3 - i32.shr_u - local.set $0 - local.get $3 - i32.const 256 - i32.lt_u - if ;; label = @14 - block ;; label = @15 - local.get $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.set $3 - i32.const 3652 - i32.load - local.tee $1 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @16 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @17 - call $fimport$10 - else - block ;; label = @18 - local.get $1 - local.set $16 - local.get $0 - local.set $8 - end - end - else - block ;; label = @17 - i32.const 3652 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $16 - local.get $3 - local.set $8 - end - end - local.get $16 - local.get $6 - i32.store - local.get $8 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $8 - i32.store offset=8 - local.get $6 - local.get $3 - i32.store offset=12 - br 4 (;@11;) - end - end - local.get $3 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @14 - local.get $3 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @15 - i32.const 31 - else - local.get $3 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $5 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $5 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $5 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - local.set $1 - local.get $6 - local.get $5 - i32.store offset=28 - local.get $6 - i32.const 16 - i32.add - local.tee $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store - local.get $2 - i32.const 1 - local.get $5 - i32.shl - local.tee $0 - i32.and - i32.eqz - if ;; label = @14 - block ;; label = @15 - i32.const 3656 - local.get $2 - local.get $0 - i32.or - i32.store - local.get $1 - local.get $6 - i32.store - local.get $6 - local.get $1 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 4 (;@11;) - end - end - local.get $1 - i32.load - local.set $0 - i32.const 25 - local.get $5 - i32.const 1 - i32.shr_u - i32.sub - local.set $1 - local.get $3 - local.get $5 - i32.const 31 - i32.eq - if (result i32) ;; label = @14 - i32.const 0 - else - local.get $1 - end - i32.shl - local.set $5 - block $label$151 ;; label = @14 - block $label$152 ;; label = @15 - block $label$153 ;; label = @16 - loop $label$154 ;; label = @17 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.eq - br_if 2 (;@15;) - local.get $5 - i32.const 1 - i32.shl - local.set $2 - local.get $0 - i32.const 16 - i32.add - local.get $5 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $5 - i32.load - local.tee $1 - i32.eqz - br_if 1 (;@16;) - local.get $2 - local.set $5 - local.get $1 - local.set $0 - br 0 (;@17;) - end - end - local.get $5 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @16 - call $fimport$10 - else - block ;; label = @17 - local.get $5 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 6 (;@11;) - end - end - br 1 (;@14;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $2 - i32.const 3668 - i32.load - local.tee $1 - i32.ge_u - local.get $0 - local.get $1 - i32.ge_u - i32.and - if ;; label = @15 - block ;; label = @16 - local.get $2 - local.get $6 - i32.store offset=12 - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $2 - i32.store offset=8 - local.get $6 - local.get $0 - i32.store offset=12 - local.get $6 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - end - end - local.get $14 - global.set $global$1 - local.get $4 - i32.const 8 - i32.add - return - end - else - local.get $7 - local.set $0 - end - else - local.get $7 - local.set $0 - end - end - else - local.get $7 - local.set $0 - end - end - end - end - end - i32.const 3660 - i32.load - local.tee $1 - local.get $0 - i32.ge_u - if ;; label = @2 - block ;; label = @3 - i32.const 3672 - i32.load - local.set $2 - local.get $1 - local.get $0 - i32.sub - local.tee $3 - i32.const 15 - i32.gt_u - if ;; label = @4 - block ;; label = @5 - i32.const 3672 - local.get $2 - local.get $0 - i32.add - local.tee $1 - i32.store - i32.const 3660 - local.get $3 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $1 - local.get $3 - i32.add - local.get $3 - i32.store - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - end - else - block ;; label = @5 - i32.const 3660 - i32.const 0 - i32.store - i32.const 3672 - i32.const 0 - i32.store - local.get $2 - local.get $1 - i32.const 3 - i32.or - i32.store offset=4 - local.get $2 - local.get $1 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - end - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - i32.const 3664 - i32.load - local.tee $10 - local.get $0 - i32.gt_u - if ;; label = @2 - block ;; label = @3 - i32.const 3664 - local.get $10 - local.get $0 - i32.sub - local.tee $3 - i32.store - i32.const 3676 - i32.const 3676 - i32.load - local.tee $2 - local.get $0 - i32.add - local.tee $1 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - i32.const 4124 - i32.load - if (result i32) ;; label = @2 - i32.const 4132 - i32.load - else - block (result i32) ;; label = @3 - i32.const 4132 - i32.const 4096 - i32.store - i32.const 4128 - i32.const 4096 - i32.store - i32.const 4136 - i32.const -1 - i32.store - i32.const 4140 - i32.const -1 - i32.store - i32.const 4144 - i32.const 0 - i32.store - i32.const 4096 - i32.const 0 - i32.store - local.get $18 - local.get $18 - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - local.tee $1 - i32.store - i32.const 4124 - local.get $1 - i32.store - i32.const 4096 - end - end - local.tee $1 - local.get $0 - i32.const 47 - i32.add - local.tee $13 - i32.add - local.tee $8 - i32.const 0 - local.get $1 - i32.sub - local.tee $4 - i32.and - local.tee $6 - local.get $0 - i32.le_u - if ;; label = @2 - block ;; label = @3 - local.get $14 - global.set $global$1 - i32.const 0 - return - end - end - i32.const 4092 - i32.load - local.tee $2 - if ;; label = @2 - i32.const 4084 - i32.load - local.tee $3 - local.get $6 - i32.add - local.tee $1 - local.get $3 - i32.le_u - local.get $1 - local.get $2 - i32.gt_u - i32.or - if ;; label = @3 - block ;; label = @4 - local.get $14 - global.set $global$1 - i32.const 0 - return - end - end - end - local.get $0 - i32.const 48 - i32.add - local.set $7 - block $label$171 ;; label = @2 - block $label$172 ;; label = @3 - i32.const 4096 - i32.load - i32.const 4 - i32.and - i32.eqz - if ;; label = @4 - block ;; label = @5 - block $label$174 ;; label = @6 - block $label$175 ;; label = @7 - block $label$176 ;; label = @8 - i32.const 3676 - i32.load - local.tee $3 - i32.eqz - br_if 0 (;@8;) - i32.const 4100 - local.set $2 - loop $label$177 ;; label = @9 - block $label$178 ;; label = @10 - local.get $2 - i32.load - local.tee $1 - local.get $3 - i32.le_u - if ;; label = @11 - local.get $1 - local.get $2 - i32.const 4 - i32.add - local.tee $5 - i32.load - i32.add - local.get $3 - i32.gt_u - br_if 1 (;@10;) - end - local.get $2 - i32.load offset=8 - local.tee $1 - i32.eqz - br_if 2 (;@8;) - local.get $1 - local.set $2 - br 1 (;@9;) - end - end - local.get $8 - local.get $10 - i32.sub - local.get $4 - i32.and - local.tee $3 - i32.const 2147483647 - i32.lt_u - if ;; label = @9 - local.get $3 - call $38 - local.tee $1 - local.get $2 - i32.load - local.get $5 - i32.load - i32.add - i32.eq - if ;; label = @10 - local.get $1 - i32.const -1 - i32.ne - br_if 7 (;@3;) - else - block ;; label = @11 - local.get $1 - local.set $2 - local.get $3 - local.set $1 - br 4 (;@7;) - end - end - end - br 2 (;@6;) - end - i32.const 0 - call $38 - local.tee $1 - i32.const -1 - i32.ne - if ;; label = @8 - block ;; label = @9 - i32.const 4128 - i32.load - local.tee $2 - i32.const -1 - i32.add - local.tee $5 - local.get $1 - local.tee $3 - i32.add - i32.const 0 - local.get $2 - i32.sub - i32.and - local.get $3 - i32.sub - local.set $2 - local.get $5 - local.get $3 - i32.and - if (result i32) ;; label = @10 - local.get $2 - else - i32.const 0 - end - local.get $6 - i32.add - local.tee $3 - i32.const 4084 - i32.load - local.tee $5 - i32.add - local.set $4 - local.get $3 - local.get $0 - i32.gt_u - local.get $3 - i32.const 2147483647 - i32.lt_u - i32.and - if ;; label = @10 - block ;; label = @11 - i32.const 4092 - i32.load - local.tee $2 - if ;; label = @12 - local.get $4 - local.get $5 - i32.le_u - local.get $4 - local.get $2 - i32.gt_u - i32.or - br_if 6 (;@6;) - end - local.get $3 - call $38 - local.tee $2 - local.get $1 - i32.eq - br_if 8 (;@3;) - local.get $3 - local.set $1 - br 4 (;@7;) - end - end - end - end - br 1 (;@6;) - end - i32.const 0 - local.get $1 - i32.sub - local.set $5 - local.get $7 - local.get $1 - i32.gt_u - local.get $1 - i32.const 2147483647 - i32.lt_u - local.get $2 - i32.const -1 - i32.ne - i32.and - i32.and - if ;; label = @7 - local.get $13 - local.get $1 - i32.sub - i32.const 4132 - i32.load - local.tee $3 - i32.add - i32.const 0 - local.get $3 - i32.sub - i32.and - local.tee $3 - i32.const 2147483647 - i32.lt_u - if ;; label = @8 - local.get $3 - call $38 - i32.const -1 - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $5 - call $38 - drop - br 4 (;@6;) - end - else - local.get $3 - local.get $1 - i32.add - local.set $3 - end - else - local.get $1 - local.set $3 - end - else - local.get $1 - local.set $3 - end - local.get $2 - i32.const -1 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $2 - local.set $1 - br 5 (;@3;) - end - end - end - i32.const 4096 - i32.const 4096 - i32.load - i32.const 4 - i32.or - i32.store - end - end - local.get $6 - i32.const 2147483647 - i32.lt_u - if ;; label = @4 - local.get $6 - call $38 - local.tee $1 - i32.const 0 - call $38 - local.tee $3 - i32.lt_u - local.get $1 - i32.const -1 - i32.ne - local.get $3 - i32.const -1 - i32.ne - i32.and - i32.and - if ;; label = @5 - local.get $3 - local.get $1 - i32.sub - local.tee $3 - local.get $0 - i32.const 40 - i32.add - i32.gt_u - br_if 2 (;@3;) - end - end - br 1 (;@2;) - end - i32.const 4084 - i32.const 4084 - i32.load - local.get $3 - i32.add - local.tee $2 - i32.store - local.get $2 - i32.const 4088 - i32.load - i32.gt_u - if ;; label = @3 - i32.const 4088 - local.get $2 - i32.store - end - block $label$198 ;; label = @3 - i32.const 3676 - i32.load - local.tee $8 - if ;; label = @4 - block ;; label = @5 - i32.const 4100 - local.set $2 - block $label$200 ;; label = @6 - block $label$201 ;; label = @7 - loop $label$202 ;; label = @8 - local.get $1 - local.get $2 - i32.load - local.tee $4 - local.get $2 - i32.const 4 - i32.add - local.tee $7 - i32.load - local.tee $5 - i32.add - i32.eq - br_if 1 (;@7;) - local.get $2 - i32.load offset=8 - local.tee $2 - br_if 0 (;@8;) - end - br 1 (;@6;) - end - local.get $2 - i32.load offset=12 - i32.const 8 - i32.and - i32.eqz - if ;; label = @7 - local.get $8 - local.get $1 - i32.lt_u - local.get $8 - local.get $4 - i32.ge_u - i32.and - if ;; label = @8 - block ;; label = @9 - local.get $7 - local.get $5 - local.get $3 - i32.add - i32.store - i32.const 3664 - i32.load - local.set $5 - i32.const 0 - local.get $8 - i32.const 8 - i32.add - local.tee $2 - i32.sub - i32.const 7 - i32.and - local.set $1 - i32.const 3676 - local.get $8 - local.get $2 - i32.const 7 - i32.and - if (result i32) ;; label = @10 - local.get $1 - else - i32.const 0 - local.tee $1 - end - i32.add - local.tee $2 - i32.store - i32.const 3664 - local.get $3 - local.get $1 - i32.sub - local.get $5 - i32.add - local.tee $1 - i32.store - local.get $2 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $1 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 3680 - i32.const 4140 - i32.load - i32.store - br 6 (;@3;) - end - end - end - end - local.get $1 - i32.const 3668 - i32.load - local.tee $2 - i32.lt_u - if ;; label = @6 - block ;; label = @7 - i32.const 3668 - local.get $1 - i32.store - local.get $1 - local.set $2 - end - end - local.get $1 - local.get $3 - i32.add - local.set $10 - i32.const 4100 - local.set $5 - block $label$208 ;; label = @6 - block $label$209 ;; label = @7 - loop $label$210 ;; label = @8 - local.get $5 - i32.load - local.get $10 - i32.eq - br_if 1 (;@7;) - local.get $5 - i32.load offset=8 - local.tee $5 - br_if 0 (;@8;) - i32.const 4100 - local.set $5 - end - br 1 (;@6;) - end - local.get $5 - i32.load offset=12 - i32.const 8 - i32.and - if ;; label = @7 - i32.const 4100 - local.set $5 - else - block ;; label = @8 - local.get $5 - local.get $1 - i32.store - local.get $5 - i32.const 4 - i32.add - local.tee $5 - local.get $5 - i32.load - local.get $3 - i32.add - i32.store - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $4 - i32.sub - i32.const 7 - i32.and - local.set $7 - i32.const 0 - local.get $10 - i32.const 8 - i32.add - local.tee $5 - i32.sub - i32.const 7 - i32.and - local.set $3 - local.get $1 - local.get $4 - i32.const 7 - i32.and - if (result i32) ;; label = @9 - local.get $7 - else - i32.const 0 - end - i32.add - local.tee $13 - local.get $0 - i32.add - local.set $6 - local.get $10 - local.get $5 - i32.const 7 - i32.and - if (result i32) ;; label = @9 - local.get $3 - else - i32.const 0 - end - i32.add - local.tee $4 - local.get $13 - i32.sub - local.get $0 - i32.sub - local.set $7 - local.get $13 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - block $label$217 ;; label = @9 - local.get $4 - local.get $8 - i32.eq - if ;; label = @10 - block ;; label = @11 - i32.const 3664 - i32.const 3664 - i32.load - local.get $7 - i32.add - local.tee $0 - i32.store - i32.const 3676 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - end - else - block ;; label = @11 - local.get $4 - i32.const 3672 - i32.load - i32.eq - if ;; label = @12 - block ;; label = @13 - i32.const 3660 - i32.const 3660 - i32.load - local.get $7 - i32.add - local.tee $0 - i32.store - i32.const 3672 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $0 - i32.add - local.get $0 - i32.store - br 4 (;@9;) - end - end - local.get $4 - i32.load offset=4 - local.tee $0 - i32.const 3 - i32.and - i32.const 1 - i32.eq - if (result i32) ;; label = @12 - block (result i32) ;; label = @13 - local.get $0 - i32.const -8 - i32.and - local.set $11 - local.get $0 - i32.const 3 - i32.shr_u - local.set $1 - block $label$222 ;; label = @14 - local.get $0 - i32.const 256 - i32.lt_u - if ;; label = @15 - block ;; label = @16 - local.get $4 - i32.load offset=12 - local.set $5 - block $label$224 ;; label = @17 - local.get $4 - i32.load offset=8 - local.tee $3 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.tee $0 - i32.ne - if ;; label = @18 - block ;; label = @19 - local.get $3 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $3 - i32.load offset=12 - local.get $4 - i32.eq - br_if 2 (;@17;) - call $fimport$10 - end - end - end - local.get $5 - local.get $3 - i32.eq - if ;; label = @17 - block ;; label = @18 - i32.const 3652 - i32.const 3652 - i32.load - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 4 (;@14;) - end - end - block $label$228 ;; label = @17 - local.get $5 - local.get $0 - i32.eq - if ;; label = @18 - local.get $5 - i32.const 8 - i32.add - local.set $20 - else - block ;; label = @19 - local.get $5 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $5 - i32.const 8 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - block ;; label = @21 - local.get $0 - local.set $20 - br 4 (;@17;) - end - end - call $fimport$10 - end - end - end - local.get $3 - local.get $5 - i32.store offset=12 - local.get $20 - local.get $3 - i32.store - end - else - block ;; label = @16 - local.get $4 - i32.load offset=24 - local.set $8 - block $label$234 ;; label = @17 - local.get $4 - i32.load offset=12 - local.tee $0 - local.get $4 - i32.eq - if ;; label = @18 - block ;; label = @19 - local.get $4 - i32.const 16 - i32.add - local.tee $3 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @20 - local.get $3 - i32.load - local.tee $0 - if ;; label = @21 - local.get $3 - local.set $1 - else - block ;; label = @22 - i32.const 0 - local.set $12 - br 5 (;@17;) - end - end - end - loop $label$239 ;; label = @20 - local.get $0 - i32.const 20 - i32.add - local.tee $5 - i32.load - local.tee $3 - if ;; label = @21 - block ;; label = @22 - local.get $3 - local.set $0 - local.get $5 - local.set $1 - br 2 (;@20;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $5 - i32.load - local.tee $3 - if ;; label = @21 - block ;; label = @22 - local.get $3 - local.set $0 - local.get $5 - local.set $1 - br 2 (;@20;) - end - end - end - local.get $1 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - else - block ;; label = @21 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $12 - end - end - end - else - block ;; label = @19 - local.get $4 - i32.load offset=8 - local.tee $5 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $5 - i32.const 12 - i32.add - local.tee $3 - i32.load - local.get $4 - i32.ne - if ;; label = @20 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - block ;; label = @21 - local.get $3 - local.get $0 - i32.store - local.get $1 - local.get $5 - i32.store - local.get $0 - local.set $12 - end - else - call $fimport$10 - end - end - end - end - local.get $8 - i32.eqz - br_if 2 (;@14;) - block $label$249 ;; label = @17 - local.get $4 - local.get $4 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @18 - block ;; label = @19 - local.get $0 - local.get $12 - i32.store - local.get $12 - br_if 2 (;@17;) - i32.const 3656 - i32.const 3656 - i32.load - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 5 (;@14;) - end - else - block ;; label = @19 - local.get $8 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $8 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - local.get $0 - local.get $12 - i32.store - else - local.get $8 - local.get $12 - i32.store offset=20 - end - local.get $12 - i32.eqz - br_if 5 (;@14;) - end - end - end - local.get $12 - i32.const 3668 - i32.load - local.tee $1 - i32.lt_u - if ;; label = @17 - call $fimport$10 - end - local.get $12 - local.get $8 - i32.store offset=24 - local.get $4 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.tee $3 - if ;; label = @17 - local.get $3 - local.get $1 - i32.lt_u - if ;; label = @18 - call $fimport$10 - else - block ;; label = @19 - local.get $12 - local.get $3 - i32.store offset=16 - local.get $3 - local.get $12 - i32.store offset=24 - end - end - end - local.get $0 - i32.load offset=4 - local.tee $0 - i32.eqz - br_if 2 (;@14;) - local.get $0 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @17 - call $fimport$10 - else - block ;; label = @18 - local.get $12 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $12 - i32.store offset=24 - end - end - end - end - end - local.get $11 - local.get $7 - i32.add - local.set $7 - local.get $4 - local.get $11 - i32.add - end - else - local.get $4 - end - local.tee $0 - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const -2 - i32.and - i32.store - local.get $6 - local.get $7 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $7 - i32.add - local.get $7 - i32.store - local.get $7 - i32.const 3 - i32.shr_u - local.set $0 - local.get $7 - i32.const 256 - i32.lt_u - if ;; label = @12 - block ;; label = @13 - local.get $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.set $3 - block $label$263 ;; label = @14 - i32.const 3652 - i32.load - local.tee $1 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @15 - block ;; label = @16 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 3668 - i32.load - i32.ge_u - if ;; label = @17 - block ;; label = @18 - local.get $1 - local.set $21 - local.get $0 - local.set $9 - br 4 (;@14;) - end - end - call $fimport$10 - end - else - block ;; label = @16 - i32.const 3652 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $21 - local.get $3 - local.set $9 - end - end - end - local.get $21 - local.get $6 - i32.store - local.get $9 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $9 - i32.store offset=8 - local.get $6 - local.get $3 - i32.store offset=12 - br 4 (;@9;) - end - end - block $label$267 (result i32) ;; label = @12 - local.get $7 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @13 - block (result i32) ;; label = @14 - i32.const 31 - local.get $7 - i32.const 16777215 - i32.gt_u - br_if 2 (;@12;) - drop - local.get $7 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $3 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $3 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - end - local.tee $2 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - local.set $3 - local.get $6 - local.get $2 - i32.store offset=28 - local.get $6 - i32.const 16 - i32.add - local.tee $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store - i32.const 3656 - i32.load - local.tee $1 - i32.const 1 - local.get $2 - i32.shl - local.tee $0 - i32.and - i32.eqz - if ;; label = @12 - block ;; label = @13 - i32.const 3656 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $3 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 4 (;@9;) - end - end - local.get $3 - i32.load - local.set $0 - i32.const 25 - local.get $2 - i32.const 1 - i32.shr_u - i32.sub - local.set $1 - local.get $7 - local.get $2 - i32.const 31 - i32.eq - if (result i32) ;; label = @12 - i32.const 0 - else - local.get $1 - end - i32.shl - local.set $2 - block $label$273 ;; label = @12 - block $label$274 ;; label = @13 - block $label$275 ;; label = @14 - loop $label$276 ;; label = @15 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.eq - br_if 2 (;@13;) - local.get $2 - i32.const 1 - i32.shl - local.set $3 - local.get $0 - i32.const 16 - i32.add - local.get $2 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $2 - i32.load - local.tee $1 - i32.eqz - br_if 1 (;@14;) - local.get $3 - local.set $2 - local.get $1 - local.set $0 - br 0 (;@15;) - end - end - local.get $2 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$10 - else - block ;; label = @15 - local.get $2 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 6 (;@9;) - end - end - br 1 (;@12;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $2 - i32.const 3668 - i32.load - local.tee $1 - i32.ge_u - local.get $0 - local.get $1 - i32.ge_u - i32.and - if ;; label = @13 - block ;; label = @14 - local.get $2 - local.get $6 - i32.store offset=12 - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $2 - i32.store offset=8 - local.get $6 - local.get $0 - i32.store offset=12 - local.get $6 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - end - end - local.get $14 - global.set $global$1 - local.get $13 - i32.const 8 - i32.add - return - end - end - end - loop $label$281 ;; label = @6 - block $label$282 ;; label = @7 - local.get $5 - i32.load - local.tee $2 - local.get $8 - i32.le_u - if ;; label = @8 - local.get $2 - local.get $5 - i32.load offset=4 - i32.add - local.tee $13 - local.get $8 - i32.gt_u - br_if 1 (;@7;) - end - local.get $5 - i32.load offset=8 - local.set $5 - br 1 (;@6;) - end - end - i32.const 0 - local.get $13 - i32.const -47 - i32.add - local.tee $7 - i32.const 8 - i32.add - local.tee $5 - i32.sub - i32.const 7 - i32.and - local.set $2 - local.get $7 - local.get $5 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $2 - else - i32.const 0 - end - i32.add - local.tee $2 - local.get $8 - i32.const 16 - i32.add - local.tee $12 - i32.lt_u - if (result i32) ;; label = @6 - local.get $8 - else - local.get $2 - end - local.tee $7 - i32.const 8 - i32.add - local.set $10 - local.get $7 - i32.const 24 - i32.add - local.set $5 - local.get $3 - i32.const -40 - i32.add - local.set $9 - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $4 - i32.sub - i32.const 7 - i32.and - local.set $2 - i32.const 3676 - local.get $1 - local.get $4 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $2 - else - i32.const 0 - local.tee $2 - end - i32.add - local.tee $4 - i32.store - i32.const 3664 - local.get $9 - local.get $2 - i32.sub - local.tee $2 - i32.store - local.get $4 - local.get $2 - i32.const 1 - i32.or - i32.store offset=4 - local.get $4 - local.get $2 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 3680 - i32.const 4140 - i32.load - i32.store - local.get $7 - i32.const 4 - i32.add - local.tee $2 - i32.const 27 - i32.store - local.get $10 - i32.const 4100 - i64.load align=4 - i64.store align=4 - local.get $10 - i32.const 4108 - i64.load align=4 - i64.store offset=8 align=4 - i32.const 4100 - local.get $1 - i32.store - i32.const 4104 - local.get $3 - i32.store - i32.const 4112 - i32.const 0 - i32.store - i32.const 4108 - local.get $10 - i32.store - local.get $5 - local.set $1 - loop $label$290 ;; label = @6 - local.get $1 - i32.const 4 - i32.add - local.tee $1 - i32.const 7 - i32.store - local.get $1 - i32.const 4 - i32.add - local.get $13 - i32.lt_u - br_if 0 (;@6;) - end - local.get $7 - local.get $8 - i32.ne - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.get $2 - i32.load - i32.const -2 - i32.and - i32.store - local.get $8 - local.get $7 - local.get $8 - i32.sub - local.tee $4 - i32.const 1 - i32.or - i32.store offset=4 - local.get $7 - local.get $4 - i32.store - local.get $4 - i32.const 3 - i32.shr_u - local.set $1 - local.get $4 - i32.const 256 - i32.lt_u - if ;; label = @8 - block ;; label = @9 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.set $2 - i32.const 3652 - i32.load - local.tee $3 - i32.const 1 - local.get $1 - i32.shl - local.tee $1 - i32.and - if ;; label = @10 - local.get $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $1 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$10 - else - block ;; label = @12 - local.get $3 - local.set $15 - local.get $1 - local.set $11 - end - end - else - block ;; label = @11 - i32.const 3652 - local.get $3 - local.get $1 - i32.or - i32.store - local.get $2 - i32.const 8 - i32.add - local.set $15 - local.get $2 - local.set $11 - end - end - local.get $15 - local.get $8 - i32.store - local.get $11 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $11 - i32.store offset=8 - local.get $8 - local.get $2 - i32.store offset=12 - br 6 (;@3;) - end - end - local.get $4 - i32.const 8 - i32.shr_u - local.tee $1 - if (result i32) ;; label = @8 - local.get $4 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @9 - i32.const 31 - else - local.get $4 - i32.const 14 - local.get $1 - local.get $1 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $2 - i32.shl - local.tee $3 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $1 - local.get $2 - i32.or - local.get $3 - local.get $1 - i32.shl - local.tee $3 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $1 - i32.or - i32.sub - local.get $3 - local.get $1 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $1 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $1 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $5 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - local.set $2 - local.get $8 - local.get $5 - i32.store offset=28 - local.get $8 - i32.const 0 - i32.store offset=20 - local.get $12 - i32.const 0 - i32.store - i32.const 3656 - i32.load - local.tee $3 - i32.const 1 - local.get $5 - i32.shl - local.tee $1 - i32.and - i32.eqz - if ;; label = @8 - block ;; label = @9 - i32.const 3656 - local.get $3 - local.get $1 - i32.or - i32.store - local.get $2 - local.get $8 - i32.store - local.get $8 - local.get $2 - i32.store offset=24 - local.get $8 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $8 - i32.store offset=8 - br 6 (;@3;) - end - end - local.get $2 - i32.load - local.set $1 - i32.const 25 - local.get $5 - i32.const 1 - i32.shr_u - i32.sub - local.set $3 - local.get $4 - local.get $5 - i32.const 31 - i32.eq - if (result i32) ;; label = @8 - i32.const 0 - else - local.get $3 - end - i32.shl - local.set $5 - block $label$304 ;; label = @8 - block $label$305 ;; label = @9 - block $label$306 ;; label = @10 - loop $label$307 ;; label = @11 - local.get $1 - i32.load offset=4 - i32.const -8 - i32.and - local.get $4 - i32.eq - br_if 2 (;@9;) - local.get $5 - i32.const 1 - i32.shl - local.set $2 - local.get $1 - i32.const 16 - i32.add - local.get $5 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $5 - i32.load - local.tee $3 - i32.eqz - br_if 1 (;@10;) - local.get $2 - local.set $5 - local.get $3 - local.set $1 - br 0 (;@11;) - end - end - local.get $5 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $5 - local.get $8 - i32.store - local.get $8 - local.get $1 - i32.store offset=24 - local.get $8 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $8 - i32.store offset=8 - br 8 (;@3;) - end - end - br 1 (;@8;) - end - local.get $1 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $5 - i32.const 3668 - i32.load - local.tee $3 - i32.ge_u - local.get $1 - local.get $3 - i32.ge_u - i32.and - if ;; label = @9 - block ;; label = @10 - local.get $5 - local.get $8 - i32.store offset=12 - local.get $2 - local.get $8 - i32.store - local.get $8 - local.get $5 - i32.store offset=8 - local.get $8 - local.get $1 - i32.store offset=12 - local.get $8 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - end - end - else - block ;; label = @5 - i32.const 3668 - i32.load - local.tee $2 - i32.eqz - local.get $1 - local.get $2 - i32.lt_u - i32.or - if ;; label = @6 - i32.const 3668 - local.get $1 - i32.store - end - i32.const 4100 - local.get $1 - i32.store - i32.const 4104 - local.get $3 - i32.store - i32.const 4112 - i32.const 0 - i32.store - i32.const 3688 - i32.const 4124 - i32.load - i32.store - i32.const 3684 - i32.const -1 - i32.store - i32.const 0 - local.set $2 - loop $label$314 ;; label = @6 - local.get $2 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.tee $5 - local.get $5 - i32.store offset=12 - local.get $5 - local.get $5 - i32.store offset=8 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 32 - i32.ne - br_if 0 (;@6;) - end - local.get $3 - i32.const -40 - i32.add - local.set $5 - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $2 - i32.sub - i32.const 7 - i32.and - local.set $3 - i32.const 3676 - local.get $1 - local.get $2 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $3 - else - i32.const 0 - end - local.tee $1 - i32.add - local.tee $3 - i32.store - i32.const 3664 - local.get $5 - local.get $1 - i32.sub - local.tee $1 - i32.store - local.get $3 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $1 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 3680 - i32.const 4140 - i32.load - i32.store - end - end - end - i32.const 3664 - i32.load - local.tee $1 - local.get $0 - i32.gt_u - if ;; label = @3 - block ;; label = @4 - i32.const 3664 - local.get $1 - local.get $0 - i32.sub - local.tee $3 - i32.store - i32.const 3676 - i32.const 3676 - i32.load - local.tee $2 - local.get $0 - i32.add - local.tee $1 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - end - call $12 - i32.const 12 - i32.store - local.get $14 - global.set $global$1 - i32.const 0 - end - ) - (func $36 (;49;) (type $2) (param $0 i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) - block $label$1 ;; label = @1 - local.get $0 - i32.eqz - if ;; label = @2 - return - end - local.get $0 - i32.const -8 - i32.add - local.tee $1 - i32.const 3668 - i32.load - local.tee $11 - i32.lt_u - if ;; label = @2 - call $fimport$10 - end - local.get $0 - i32.const -4 - i32.add - i32.load - local.tee $0 - i32.const 3 - i32.and - local.tee $8 - i32.const 1 - i32.eq - if ;; label = @2 - call $fimport$10 - end - local.get $1 - local.get $0 - i32.const -8 - i32.and - local.tee $4 - i32.add - local.set $6 - block $label$5 ;; label = @2 - local.get $0 - i32.const 1 - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $1 - local.set $3 - local.get $4 - local.set $2 - end - else - block ;; label = @4 - local.get $8 - i32.eqz - if ;; label = @5 - return - end - local.get $1 - i32.const 0 - local.get $1 - i32.load - local.tee $8 - i32.sub - i32.add - local.tee $0 - local.get $11 - i32.lt_u - if ;; label = @5 - call $fimport$10 - end - local.get $8 - local.get $4 - i32.add - local.set $1 - local.get $0 - i32.const 3672 - i32.load - i32.eq - if ;; label = @5 - block ;; label = @6 - local.get $6 - i32.const 4 - i32.add - local.tee $2 - i32.load - local.tee $3 - i32.const 3 - i32.and - i32.const 3 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 6 (;@2;) - end - end - i32.const 3660 - local.get $1 - i32.store - local.get $2 - local.get $3 - i32.const -2 - i32.and - i32.store - local.get $0 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $0 - local.get $1 - i32.add - local.get $1 - i32.store - return - end - end - local.get $8 - i32.const 3 - i32.shr_u - local.set $10 - local.get $8 - i32.const 256 - i32.lt_u - if ;; label = @5 - block ;; label = @6 - local.get $0 - i32.load offset=12 - local.set $3 - local.get $0 - i32.load offset=8 - local.tee $4 - local.get $10 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.tee $2 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $4 - local.get $11 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $4 - i32.load offset=12 - local.get $0 - i32.ne - if ;; label = @9 - call $fimport$10 - end - end - end - local.get $3 - local.get $4 - i32.eq - if ;; label = @7 - block ;; label = @8 - i32.const 3652 - i32.const 3652 - i32.load - i32.const 1 - local.get $10 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 6 (;@2;) - end - end - local.get $3 - local.get $2 - i32.eq - if ;; label = @7 - local.get $3 - i32.const 8 - i32.add - local.set $5 - else - block ;; label = @8 - local.get $3 - local.get $11 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $3 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.get $0 - i32.eq - if ;; label = @9 - local.get $2 - local.set $5 - else - call $fimport$10 - end - end - end - local.get $4 - local.get $3 - i32.store offset=12 - local.get $5 - local.get $4 - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 4 (;@2;) - end - end - local.get $0 - i32.load offset=24 - local.set $12 - block $label$22 ;; label = @5 - local.get $0 - i32.load offset=12 - local.tee $4 - local.get $0 - i32.eq - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 16 - i32.add - local.tee $5 - i32.const 4 - i32.add - local.tee $8 - i32.load - local.tee $4 - if ;; label = @8 - local.get $8 - local.set $5 - else - local.get $5 - i32.load - local.tee $4 - i32.eqz - if ;; label = @9 - block ;; label = @10 - i32.const 0 - local.set $7 - br 5 (;@5;) - end - end - end - loop $label$27 ;; label = @8 - local.get $4 - i32.const 20 - i32.add - local.tee $8 - i32.load - local.tee $10 - if ;; label = @9 - block ;; label = @10 - local.get $10 - local.set $4 - local.get $8 - local.set $5 - br 2 (;@8;) - end - end - local.get $4 - i32.const 16 - i32.add - local.tee $8 - i32.load - local.tee $10 - if ;; label = @9 - block ;; label = @10 - local.get $10 - local.set $4 - local.get $8 - local.set $5 - br 2 (;@8;) - end - end - end - local.get $5 - local.get $11 - i32.lt_u - if ;; label = @8 - call $fimport$10 - else - block ;; label = @9 - local.get $5 - i32.const 0 - i32.store - local.get $4 - local.set $7 - end - end - end - else - block ;; label = @7 - local.get $0 - i32.load offset=8 - local.tee $5 - local.get $11 - i32.lt_u - if ;; label = @8 - call $fimport$10 - end - local.get $5 - i32.const 12 - i32.add - local.tee $8 - i32.load - local.get $0 - i32.ne - if ;; label = @8 - call $fimport$10 - end - local.get $4 - i32.const 8 - i32.add - local.tee $10 - i32.load - local.get $0 - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $8 - local.get $4 - i32.store - local.get $10 - local.get $5 - i32.store - local.get $4 - local.set $7 - end - else - call $fimport$10 - end - end - end - end - local.get $12 - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $0 - i32.load offset=28 - local.tee $4 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - local.tee $5 - i32.load - i32.eq - if ;; label = @7 - block ;; label = @8 - local.get $5 - local.get $7 - i32.store - local.get $7 - i32.eqz - if ;; label = @9 - block ;; label = @10 - i32.const 3656 - i32.const 3656 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 8 (;@2;) - end - end - end - else - block ;; label = @8 - local.get $12 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $12 - i32.const 16 - i32.add - local.tee $4 - i32.load - local.get $0 - i32.eq - if ;; label = @9 - local.get $4 - local.get $7 - i32.store - else - local.get $12 - local.get $7 - i32.store offset=20 - end - local.get $7 - i32.eqz - if ;; label = @9 - block ;; label = @10 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 8 (;@2;) - end - end - end - end - local.get $7 - i32.const 3668 - i32.load - local.tee $5 - i32.lt_u - if ;; label = @7 - call $fimport$10 - end - local.get $7 - local.get $12 - i32.store offset=24 - local.get $0 - i32.const 16 - i32.add - local.tee $8 - i32.load - local.tee $4 - if ;; label = @7 - local.get $4 - local.get $5 - i32.lt_u - if ;; label = @8 - call $fimport$10 - else - block ;; label = @9 - local.get $7 - local.get $4 - i32.store offset=16 - local.get $4 - local.get $7 - i32.store offset=24 - end - end - end - local.get $8 - i32.load offset=4 - local.tee $4 - if ;; label = @7 - local.get $4 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @8 - call $fimport$10 - else - block ;; label = @9 - local.get $7 - local.get $4 - i32.store offset=20 - local.get $4 - local.get $7 - i32.store offset=24 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - else - block ;; label = @8 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - end - else - block ;; label = @6 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - end - end - end - local.get $3 - local.get $6 - i32.ge_u - if ;; label = @2 - call $fimport$10 - end - local.get $6 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 1 - i32.and - i32.eqz - if ;; label = @2 - call $fimport$10 - end - local.get $0 - i32.const 2 - i32.and - if ;; label = @2 - block ;; label = @3 - local.get $1 - local.get $0 - i32.const -2 - i32.and - i32.store - local.get $3 - local.get $2 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $2 - i32.add - local.get $2 - i32.store - end - else - block ;; label = @3 - local.get $6 - i32.const 3676 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 3664 - i32.const 3664 - i32.load - local.get $2 - i32.add - local.tee $0 - i32.store - i32.const 3676 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - i32.const 3672 - i32.load - i32.ne - if ;; label = @6 - return - end - i32.const 3672 - i32.const 0 - i32.store - i32.const 3660 - i32.const 0 - i32.store - return - end - end - local.get $6 - i32.const 3672 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 3660 - i32.const 3660 - i32.load - local.get $2 - i32.add - local.tee $0 - i32.store - i32.const 3672 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $0 - i32.add - local.get $0 - i32.store - return - end - end - local.get $0 - i32.const -8 - i32.and - local.get $2 - i32.add - local.set $5 - local.get $0 - i32.const 3 - i32.shr_u - local.set $4 - block $label$61 ;; label = @4 - local.get $0 - i32.const 256 - i32.lt_u - if ;; label = @5 - block ;; label = @6 - local.get $6 - i32.load offset=12 - local.set $2 - local.get $6 - i32.load offset=8 - local.tee $1 - local.get $4 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.tee $0 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $1 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $1 - i32.load offset=12 - local.get $6 - i32.ne - if ;; label = @9 - call $fimport$10 - end - end - end - local.get $2 - local.get $1 - i32.eq - if ;; label = @7 - block ;; label = @8 - i32.const 3652 - i32.const 3652 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 4 (;@4;) - end - end - local.get $2 - local.get $0 - i32.eq - if ;; label = @7 - local.get $2 - i32.const 8 - i32.add - local.set $14 - else - block ;; label = @8 - local.get $2 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $2 - i32.const 8 - i32.add - local.tee $0 - i32.load - local.get $6 - i32.eq - if ;; label = @9 - local.get $0 - local.set $14 - else - call $fimport$10 - end - end - end - local.get $1 - local.get $2 - i32.store offset=12 - local.get $14 - local.get $1 - i32.store - end - else - block ;; label = @6 - local.get $6 - i32.load offset=24 - local.set $7 - block $label$73 ;; label = @7 - local.get $6 - i32.load offset=12 - local.tee $0 - local.get $6 - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $6 - i32.const 16 - i32.add - local.tee $2 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - if ;; label = @10 - local.get $1 - local.set $2 - else - local.get $2 - i32.load - local.tee $0 - i32.eqz - if ;; label = @11 - block ;; label = @12 - i32.const 0 - local.set $9 - br 5 (;@7;) - end - end - end - loop $label$78 ;; label = @10 - local.get $0 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $4 - if ;; label = @11 - block ;; label = @12 - local.get $4 - local.set $0 - local.get $1 - local.set $2 - br 2 (;@10;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $4 - if ;; label = @11 - block ;; label = @12 - local.get $4 - local.set $0 - local.get $1 - local.set $2 - br 2 (;@10;) - end - end - end - local.get $2 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $2 - i32.const 0 - i32.store - local.get $0 - local.set $9 - end - end - end - else - block ;; label = @9 - local.get $6 - i32.load offset=8 - local.tee $2 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - end - local.get $2 - i32.const 12 - i32.add - local.tee $1 - i32.load - local.get $6 - i32.ne - if ;; label = @10 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $4 - i32.load - local.get $6 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $1 - local.get $0 - i32.store - local.get $4 - local.get $2 - i32.store - local.get $0 - local.set $9 - end - else - call $fimport$10 - end - end - end - end - local.get $7 - if ;; label = @7 - block ;; label = @8 - local.get $6 - local.get $6 - i32.load offset=28 - local.tee $0 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - local.tee $2 - i32.load - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $2 - local.get $9 - i32.store - local.get $9 - i32.eqz - if ;; label = @11 - block ;; label = @12 - i32.const 3656 - i32.const 3656 - i32.load - i32.const 1 - local.get $0 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 8 (;@4;) - end - end - end - else - block ;; label = @10 - local.get $7 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$10 - end - local.get $7 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $6 - i32.eq - if ;; label = @11 - local.get $0 - local.get $9 - i32.store - else - local.get $7 - local.get $9 - i32.store offset=20 - end - local.get $9 - i32.eqz - br_if 6 (;@4;) - end - end - local.get $9 - i32.const 3668 - i32.load - local.tee $2 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $9 - local.get $7 - i32.store offset=24 - local.get $6 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - if ;; label = @9 - local.get $0 - local.get $2 - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $9 - local.get $0 - i32.store offset=16 - local.get $0 - local.get $9 - i32.store offset=24 - end - end - end - local.get $1 - i32.load offset=4 - local.tee $0 - if ;; label = @9 - local.get $0 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $9 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $9 - i32.store offset=24 - end - end - end - end - end - end - end - end - local.get $3 - local.get $5 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $5 - i32.add - local.get $5 - i32.store - local.get $3 - i32.const 3672 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 3660 - local.get $5 - i32.store - return - end - else - local.get $5 - local.set $2 - end - end - end - local.get $2 - i32.const 3 - i32.shr_u - local.set $1 - local.get $2 - i32.const 256 - i32.lt_u - if ;; label = @2 - block ;; label = @3 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3692 - i32.add - local.set $0 - i32.const 3652 - i32.load - local.tee $2 - i32.const 1 - local.get $1 - i32.shl - local.tee $1 - i32.and - if ;; label = @4 - local.get $0 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $1 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @5 - call $fimport$10 - else - block ;; label = @6 - local.get $2 - local.set $15 - local.get $1 - local.set $13 - end - end - else - block ;; label = @5 - i32.const 3652 - local.get $2 - local.get $1 - i32.or - i32.store - local.get $0 - i32.const 8 - i32.add - local.set $15 - local.get $0 - local.set $13 - end - end - local.get $15 - local.get $3 - i32.store - local.get $13 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $13 - i32.store offset=8 - local.get $3 - local.get $0 - i32.store offset=12 - return - end - end - local.get $2 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @2 - local.get $2 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @3 - i32.const 31 - else - local.get $2 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $4 - local.get $0 - i32.or - local.get $1 - local.get $4 - i32.shl - local.tee $0 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $1 - i32.or - i32.sub - local.get $0 - local.get $1 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $1 - i32.const 2 - i32.shl - i32.const 3956 - i32.add - local.set $0 - local.get $3 - local.get $1 - i32.store offset=28 - local.get $3 - i32.const 0 - i32.store offset=20 - local.get $3 - i32.const 0 - i32.store offset=16 - block $label$113 ;; label = @2 - i32.const 3656 - i32.load - local.tee $4 - i32.const 1 - local.get $1 - i32.shl - local.tee $5 - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $0 - i32.load - local.set $0 - i32.const 25 - local.get $1 - i32.const 1 - i32.shr_u - i32.sub - local.set $4 - local.get $2 - local.get $1 - i32.const 31 - i32.eq - if (result i32) ;; label = @5 - i32.const 0 - else - local.get $4 - end - i32.shl - local.set $1 - block $label$117 ;; label = @5 - block $label$118 ;; label = @6 - block $label$119 ;; label = @7 - loop $label$120 ;; label = @8 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $2 - i32.eq - br_if 2 (;@6;) - local.get $1 - i32.const 1 - i32.shl - local.set $4 - local.get $0 - i32.const 16 - i32.add - local.get $1 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $1 - i32.load - local.tee $5 - i32.eqz - br_if 1 (;@7;) - local.get $4 - local.set $1 - local.get $5 - local.set $0 - br 0 (;@8;) - end - end - local.get $1 - i32.const 3668 - i32.load - i32.lt_u - if ;; label = @7 - call $fimport$10 - else - block ;; label = @8 - local.get $1 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.store offset=24 - local.get $3 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $3 - i32.store offset=8 - br 6 (;@2;) - end - end - br 1 (;@5;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $2 - i32.const 3668 - i32.load - local.tee $4 - i32.ge_u - local.get $0 - local.get $4 - i32.ge_u - i32.and - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.get $3 - i32.store offset=12 - local.get $1 - local.get $3 - i32.store - local.get $3 - local.get $2 - i32.store offset=8 - local.get $3 - local.get $0 - i32.store offset=12 - local.get $3 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - else - block ;; label = @4 - i32.const 3656 - local.get $4 - local.get $5 - i32.or - i32.store - local.get $0 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.store offset=24 - local.get $3 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $3 - i32.store offset=8 - end - end - end - i32.const 3684 - i32.const 3684 - i32.load - i32.const -1 - i32.add - local.tee $0 - i32.store - local.get $0 - if ;; label = @2 - return - else - i32.const 4108 - local.set $0 - end - loop $label$128 ;; label = @2 - local.get $0 - i32.load - local.tee $2 - i32.const 8 - i32.add - local.set $0 - local.get $2 - br_if 0 (;@2;) - end - i32.const 3684 - i32.const -1 - i32.store - end - ) - (func $37 (;50;) (type $6) - nop - ) - (func $38 (;51;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$0 - i32.load - local.tee $2 - local.get $0 - i32.const 15 - i32.add - i32.const -16 - i32.and - local.tee $0 - i32.add - local.set $1 - local.get $0 - i32.const 0 - i32.gt_s - local.get $1 - local.get $2 - i32.lt_s - i32.and - local.get $1 - i32.const 0 - i32.lt_s - i32.or - if ;; label = @2 - block ;; label = @3 - call $fimport$6 - drop - i32.const 12 - call $fimport$11 - i32.const -1 - return - end - end - global.get $global$0 - local.get $1 - i32.store - local.get $1 - call $fimport$5 - i32.gt_s - if ;; label = @2 - call $fimport$4 - i32.eqz - if ;; label = @3 - block ;; label = @4 - i32.const 12 - call $fimport$11 - global.get $global$0 - local.get $2 - i32.store - i32.const -1 - return - end - end - end - local.get $2 - end - ) - (func $39 (;52;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - local.get $2 - i32.add - local.set $4 - local.get $2 - i32.const 20 - i32.ge_s - if ;; label = @2 - block ;; label = @3 - local.get $1 - i32.const 255 - i32.and - local.set $1 - local.get $0 - i32.const 3 - i32.and - local.tee $3 - if ;; label = @4 - block ;; label = @5 - local.get $0 - i32.const 4 - i32.add - local.get $3 - i32.sub - local.set $3 - loop $label$4 ;; label = @6 - local.get $0 - local.get $3 - i32.lt_s - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.get $1 - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - br 2 (;@6;) - end - end - end - end - end - local.get $1 - local.get $1 - i32.const 8 - i32.shl - i32.or - local.get $1 - i32.const 16 - i32.shl - i32.or - local.get $1 - i32.const 24 - i32.shl - i32.or - local.set $3 - local.get $4 - i32.const -4 - i32.and - local.set $5 - loop $label$6 ;; label = @4 - local.get $0 - local.get $5 - i32.lt_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $3 - i32.store - local.get $0 - i32.const 4 - i32.add - local.set $0 - br 2 (;@4;) - end - end - end - end - end - loop $label$8 ;; label = @2 - local.get $0 - local.get $4 - i32.lt_s - if ;; label = @3 - block ;; label = @4 - local.get $0 - local.get $1 - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - br 2 (;@2;) - end - end - end - local.get $0 - local.get $2 - i32.sub - end - ) - (func $40 (;53;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - block $label$1 (result i32) ;; label = @1 - local.get $2 - i32.const 4096 - i32.ge_s - if ;; label = @2 - local.get $0 - local.get $1 - local.get $2 - call $fimport$12 - return - end - local.get $0 - local.set $3 - local.get $0 - i32.const 3 - i32.and - local.get $1 - i32.const 3 - i32.and - i32.eq - if ;; label = @2 - block ;; label = @3 - loop $label$4 ;; label = @4 - local.get $0 - i32.const 3 - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $2 - i32.eqz - if ;; label = @7 - local.get $3 - return - end - local.get $0 - local.get $1 - i32.load8_s - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - br 2 (;@4;) - end - end - end - loop $label$7 ;; label = @4 - local.get $2 - i32.const 4 - i32.ge_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $1 - i32.load - i32.store - local.get $0 - i32.const 4 - i32.add - local.set $0 - local.get $1 - i32.const 4 - i32.add - local.set $1 - local.get $2 - i32.const 4 - i32.sub - local.set $2 - br 2 (;@4;) - end - end - end - end - end - loop $label$9 ;; label = @2 - local.get $2 - i32.const 0 - i32.gt_s - if ;; label = @3 - block ;; label = @4 - local.get $0 - local.get $1 - i32.load8_s - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - br 2 (;@2;) - end - end - end - local.get $3 - end - ) - (func $41 (;54;) (type $3) (result i32) - i32.const 0 - ) - (func $42 (;55;) (type $4) (param $0 i32) (param $1 i32) (result i32) - local.get $1 - local.get $0 - i32.const 1 - i32.and - i32.const 0 - i32.add - call_indirect (type $1) - ) - (func $43 (;56;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - local.get $1 - local.get $2 - local.get $3 - local.get $0 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - ) - (func $44 (;57;) (type $5) (param $0 i32) (param $1 i32) - local.get $1 - local.get $0 - i32.const 1 - i32.and - i32.const 6 - i32.add - call_indirect (type $2) - ) - (func $45 (;58;) (type $1) (param $0 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - i32.const 0 - call $fimport$3 - i32.const 0 - end - ) - (func $46 (;59;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - i32.const 1 - call $fimport$3 - i32.const 0 - end - ) - (func $47 (;60;) (type $2) (param $0 i32) - i32.const 2 - call $fimport$3 - ) - (global $global$0 (;5;) (mut i32) global.get $gimport$0) - (global $global$1 (;6;) (mut i32) global.get $gimport$1) - (global $global$2 (;7;) (mut i32) global.get $gimport$2) - (global $global$3 (;8;) (mut i32) i32.const 0) - (global $global$4 (;9;) (mut i32) i32.const 0) - (global $global$5 (;10;) (mut i32) i32.const 0) - (export "_sbrk" (func $38)) - (export "_free" (func $36)) - (export "_main" (func $8)) - (export "_pthread_self" (func $41)) - (export "_memset" (func $39)) - (export "_malloc" (func $35)) - (export "_memcpy" (func $40)) - (export "___errno_location" (func $12)) - (export "runPostSets" (func $37)) - (export "stackAlloc" (func $0)) - (export "stackSave" (func $1)) - (export "stackRestore" (func $2)) - (export "establishStackSpace" (func $3)) - (export "setThrew" (func $4)) - (export "setTempRet0" (func $5)) - (export "getTempRet0" (func $6)) - (export "dynCall_ii" (func $42)) - (export "dynCall_iiii" (func $43)) - (export "dynCall_vi" (func $44)) - (elem (;0;) (global.get $gimport$19) func $45 $9 $46 $14 $10 $15 $47 $16) - (data (;0;) (i32.const 1024) "\04\04\00\00\05") - (data (;1;) (i32.const 1040) "\01") - (data (;2;) (i32.const 1064) "\01\00\00\00\02\00\00\00<\10\00\00\00\04") - (data (;3;) (i32.const 1088) "\01") - (data (;4;) (i32.const 1103) "\0a\ff\ff\ff\ff") - (data (;5;) (i32.const 1140) "error: %d\0a\00Pfannkuchen(%d) = %d.\0a\00%d\00\11\00\0a\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\09\00\00\00\00\0b") - (data (;6;) (i32.const 1209) "\11\00\0f\0a\11\11\11\03\0a\07\00\01\13\09\0b\0b\00\00\09\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") - (data (;7;) (i32.const 1258) "\0b") - (data (;8;) (i32.const 1267) "\11\00\0a\0a\11\11\11\00\0a\00\00\02\00\09\0b\00\00\00\09\00\0b\00\00\0b") - (data (;9;) (i32.const 1316) "\0c") - (data (;10;) (i32.const 1328) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c") - (data (;11;) (i32.const 1374) "\0e") - (data (;12;) (i32.const 1386) "\0d\00\00\00\04\0d\00\00\00\00\09\0e\00\00\00\00\00\0e\00\00\0e") - (data (;13;) (i32.const 1432) "\10") - (data (;14;) (i32.const 1444) "\0f\00\00\00\00\0f\00\00\00\00\09\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") - (data (;15;) (i32.const 1499) "\12\00\00\00\12\12\12\00\00\00\00\00\00\09") - (data (;16;) (i32.const 1548) "\0b") - (data (;17;) (i32.const 1560) "\0a\00\00\00\00\0a\00\00\00\00\09\0b\00\00\00\00\00\0b\00\00\0b") - (data (;18;) (i32.const 1606) "\0c") - (data (;19;) (i32.const 1618) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\22\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\09\0a\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\5c]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") -) \ No newline at end of file diff --git a/cranelift/wasm/wasmtests/embenchen_fasta.wat b/cranelift/wasm/wasmtests/embenchen_fasta.wat deleted file mode 100644 index 028615e9f3d4..000000000000 --- a/cranelift/wasm/wasmtests/embenchen_fasta.wat +++ /dev/null @@ -1,12056 +0,0 @@ -(module - (type $0 (;0;) (func (param i32 i32 i32) (result i32))) - (type $1 (;1;) (func)) - (type $2 (;2;) (func (param i32) (result i32))) - (type $3 (;3;) (func (param i32))) - (type $4 (;4;) (func (result i32))) - (type $5 (;5;) (func (param i32 i32))) - (type $6 (;6;) (func (param i32 i32) (result i32))) - (type $7 (;7;) (func (param i32 i32 i32 i32 i32) (result i32))) - (type $8 (;8;) (func (param i32 i32 i32))) - (type $9 (;9;) (func (param i64 i32) (result i32))) - (type $10 (;10;) (func (param i32 i32 i32 i32 i32))) - (type $11 (;11;) (func (param f64 i32) (result f64))) - (type $12 (;12;) (func (param i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory $16 (;0;) 2048 2048)) - (import "env" "table" (table $timport$17 (;0;) 9 9 funcref)) - (import "env" "DYNAMICTOP_PTR" (global $gimport$0 (;0;) i32)) - (import "env" "STACKTOP" (global $gimport$1 (;1;) i32)) - (import "env" "STACK_MAX" (global $gimport$2 (;2;) i32)) - (import "env" "memoryBase" (global $gimport$18 (;3;) i32)) - (import "env" "tableBase" (global $gimport$19 (;4;) i32)) - (import "env" "abort" (func $fimport$3 (;0;) (type $3))) - (import "env" "enlargeMemory" (func $fimport$4 (;1;) (type $4))) - (import "env" "getTotalMemory" (func $fimport$5 (;2;) (type $4))) - (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (;3;) (type $4))) - (import "env" "_pthread_cleanup_pop" (func $fimport$7 (;4;) (type $3))) - (import "env" "_abort" (func $fimport$8 (;5;) (type $1))) - (import "env" "_pthread_cleanup_push" (func $fimport$9 (;6;) (type $5))) - (import "env" "___syscall6" (func $fimport$10 (;7;) (type $6))) - (import "env" "___setErrNo" (func $fimport$11 (;8;) (type $3))) - (import "env" "_emscripten_memcpy_big" (func $fimport$12 (;9;) (type $0))) - (import "env" "___syscall54" (func $fimport$13 (;10;) (type $6))) - (import "env" "___syscall140" (func $fimport$14 (;11;) (type $6))) - (import "env" "___syscall146" (func $fimport$15 (;12;) (type $6))) - (func $0 (;13;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $1 - global.get $global$1 - local.get $0 - i32.add - global.set $global$1 - global.get $global$1 - i32.const 15 - i32.add - i32.const -16 - i32.and - global.set $global$1 - local.get $1 - end - ) - (func $1 (;14;) (type $4) (result i32) - global.get $global$1 - ) - (func $2 (;15;) (type $3) (param $0 i32) - local.get $0 - global.set $global$1 - ) - (func $3 (;16;) (type $5) (param $0 i32) (param $1 i32) - block $label$1 ;; label = @1 - local.get $0 - global.set $global$1 - local.get $1 - global.set $global$2 - end - ) - (func $4 (;17;) (type $5) (param $0 i32) (param $1 i32) - global.get $global$3 - i32.eqz - if ;; label = @1 - block ;; label = @2 - local.get $0 - global.set $global$3 - local.get $1 - global.set $global$4 - end - end - ) - (func $5 (;18;) (type $3) (param $0 i32) - local.get $0 - global.set $global$5 - ) - (func $6 (;19;) (type $4) (result i32) - global.get $global$5 - ) - (func $7 (;20;) (type $6) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 f32) (local $12 f32) (local $13 f64) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $5 - global.get $global$1 - i32.const 4256 - i32.add - global.set $global$1 - local.get $5 - local.set $3 - local.get $5 - i32.const 2128 - i32.add - local.set $6 - local.get $5 - i32.const 8 - i32.add - local.set $7 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $0 - i32.const 1 - i32.le_s - br_if 0 (;@3;) - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - block $label$6 ;; label = @6 - block $label$7 ;; label = @7 - block $label$8 ;; label = @8 - block $label$9 ;; label = @9 - block $label$10 ;; label = @10 - local.get $1 - i32.load offset=4 - i32.load8_s - local.tee $0 - i32.const 48 - i32.sub - br_table 5 (;@5;) 0 (;@10;) 2 (;@8;) 1 (;@9;) 3 (;@7;) 4 (;@6;) 6 (;@4;) - end - i32.const 950000 - local.set $4 - br 7 (;@2;) - end - br 5 (;@3;) - end - i32.const 9500000 - local.set $4 - br 5 (;@2;) - end - i32.const 95000000 - local.set $4 - br 4 (;@2;) - end - i32.const 190000000 - local.set $4 - br 3 (;@2;) - end - local.get $5 - global.set $global$1 - i32.const 0 - return - end - local.get $3 - local.get $0 - i32.const -48 - i32.add - i32.store - i32.const 1400 - local.get $3 - call $34 - drop - local.get $5 - global.set $global$1 - i32.const -1 - return - end - i32.const 19000000 - local.set $4 - end - i32.const 347 - call $40 - local.tee $8 - i32.const 1411 - i32.const 287 - call $47 - drop - local.get $8 - i32.const 287 - i32.add - local.tee $0 - i32.const 1411 - i64.load align=1 - i64.store align=1 - local.get $0 - i32.const 1419 - i64.load align=1 - i64.store offset=8 align=1 - local.get $0 - i32.const 1427 - i64.load align=1 - i64.store offset=16 align=1 - local.get $0 - i32.const 1435 - i64.load align=1 - i64.store offset=24 align=1 - local.get $0 - i32.const 1443 - i64.load align=1 - i64.store offset=32 align=1 - local.get $0 - i32.const 1451 - i64.load align=1 - i64.store offset=40 align=1 - local.get $0 - i32.const 1459 - i64.load align=1 - i64.store offset=48 align=1 - local.get $0 - i32.const 1467 - i32.load align=1 - i32.store offset=56 align=1 - local.get $4 - i32.const 1 - i32.shl - local.set $0 - i32.const 0 - local.set $1 - loop $label$11 ;; label = @2 - local.get $0 - i32.const 60 - i32.lt_u - if (result i32) ;; label = @3 - local.get $0 - else - i32.const 60 - end - local.tee $3 - i32.const 2 - i32.add - call $40 - local.tee $2 - local.get $8 - local.get $1 - i32.add - local.get $3 - call $47 - drop - local.get $2 - local.get $3 - i32.add - i32.const 0 - i32.store8 - local.get $2 - call $31 - local.tee $10 - i32.const 1024 - i32.load - local.tee $9 - i32.gt_s - if ;; label = @3 - local.get $9 - i32.const 0 - i32.gt_s - if ;; label = @4 - block ;; label = @5 - local.get $2 - local.get $9 - i32.add - i32.const 0 - i32.store8 - local.get $2 - call $35 - drop - i32.const 1024 - i32.const 0 - i32.store - end - end - else - block ;; label = @4 - local.get $2 - call $35 - drop - i32.const 1024 - i32.const 1024 - i32.load - local.get $10 - i32.sub - i32.store - end - end - local.get $2 - call $41 - local.get $3 - local.get $1 - i32.add - local.tee $2 - i32.const -287 - i32.add - local.set $1 - local.get $2 - i32.const 287 - i32.le_u - if ;; label = @3 - local.get $2 - local.set $1 - end - local.get $0 - local.get $3 - i32.sub - local.tee $0 - br_if 0 (;@2;) - end - local.get $8 - call $42 - i32.const 1028 - i32.load - if ;; label = @2 - block ;; label = @3 - i32.const 1028 - local.set $0 - f32.const 0x0p+0 (;=0;) - local.set $11 - loop $label$19 ;; label = @4 - local.get $11 - local.get $0 - i32.const 4 - i32.add - local.tee $1 - f32.load - f32.add - local.tee $11 - f64.promote_f32 - local.tee $13 - f64.const 0x1p+0 (;=1;) - f64.lt - if (result f64) ;; label = @5 - local.get $13 - else - f64.const 0x1p+0 (;=1;) - end - f32.demote_f64 - local.set $12 - local.get $1 - local.get $12 - f32.store - local.get $0 - local.get $12 - f32.const 0x1p+9 (;=512;) - f32.mul - i32.trunc_f32_s - i32.store offset=8 - local.get $0 - i32.const 12 - i32.add - local.tee $0 - i32.load - br_if 0 (;@4;) - i32.const 0 - local.set $1 - i32.const 1028 - local.set $0 - end - end - else - block ;; label = @3 - i32.const 0 - local.set $1 - i32.const 1028 - local.set $0 - end - end - loop $label$23 ;; label = @2 - loop $label$24 ;; label = @3 - local.get $0 - i32.const 12 - i32.add - local.set $3 - local.get $1 - local.get $0 - i32.load offset=8 - local.tee $2 - i32.gt_u - local.get $2 - i32.const 0 - i32.ne - i32.and - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $0 - br 2 (;@3;) - end - end - end - local.get $6 - local.get $1 - i32.const 2 - i32.shl - i32.add - local.get $0 - i32.store - local.get $1 - i32.const 1 - i32.add - local.tee $1 - i32.const 513 - i32.ne - br_if 0 (;@2;) - end - local.get $6 - i32.const 2116 - i32.add - i32.const 0 - i32.store - local.get $4 - i32.const 3 - i32.mul - local.set $0 - loop $label$26 ;; label = @2 - local.get $6 - local.get $0 - i32.const 60 - i32.lt_u - if (result i32) ;; label = @3 - local.get $0 - else - i32.const 60 - end - local.tee $1 - call $8 - local.get $0 - local.get $1 - i32.sub - local.tee $0 - br_if 0 (;@2;) - end - i32.const 1220 - i32.load - if ;; label = @2 - block ;; label = @3 - i32.const 1220 - local.set $0 - f32.const 0x0p+0 (;=0;) - local.set $11 - loop $label$30 ;; label = @4 - local.get $11 - local.get $0 - i32.const 4 - i32.add - local.tee $1 - f32.load - f32.add - local.tee $11 - f64.promote_f32 - local.tee $13 - f64.const 0x1p+0 (;=1;) - f64.lt - if (result f64) ;; label = @5 - local.get $13 - else - f64.const 0x1p+0 (;=1;) - end - f32.demote_f64 - local.set $12 - local.get $1 - local.get $12 - f32.store - local.get $0 - local.get $12 - f32.const 0x1p+9 (;=512;) - f32.mul - i32.trunc_f32_s - i32.store offset=8 - local.get $0 - i32.const 12 - i32.add - local.tee $0 - i32.load - br_if 0 (;@4;) - i32.const 0 - local.set $1 - i32.const 1220 - local.set $0 - end - end - else - block ;; label = @3 - i32.const 0 - local.set $1 - i32.const 1220 - local.set $0 - end - end - loop $label$34 ;; label = @2 - loop $label$35 ;; label = @3 - local.get $0 - i32.const 12 - i32.add - local.set $3 - local.get $1 - local.get $0 - i32.load offset=8 - local.tee $2 - i32.gt_u - local.get $2 - i32.const 0 - i32.ne - i32.and - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $0 - br 2 (;@3;) - end - end - end - local.get $7 - local.get $1 - i32.const 2 - i32.shl - i32.add - local.get $0 - i32.store - local.get $1 - i32.const 1 - i32.add - local.tee $1 - i32.const 513 - i32.ne - br_if 0 (;@2;) - end - local.get $7 - i32.const 2116 - i32.add - i32.const 0 - i32.store - local.get $4 - i32.const 5 - i32.mul - local.set $0 - loop $label$37 ;; label = @2 - local.get $7 - local.get $0 - i32.const 60 - i32.lt_u - if (result i32) ;; label = @3 - local.get $0 - else - i32.const 60 - end - local.tee $1 - call $8 - local.get $0 - local.get $1 - i32.sub - local.tee $0 - br_if 0 (;@2;) - i32.const 0 - local.set $0 - end - local.get $5 - global.set $global$1 - local.get $0 - end - ) - (func $8 (;21;) (type $5) (param $0 i32) (param $1 i32) - (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 f32) - block $label$1 ;; label = @1 - local.get $1 - if ;; label = @2 - block ;; label = @3 - i32.const 0 - local.set $3 - i32.const 1396 - i32.load - local.set $2 - loop $label$3 ;; label = @4 - local.get $0 - local.get $2 - i32.const 3877 - i32.mul - i32.const 29573 - i32.add - i32.const 139968 - i32.rem_u - local.tee $4 - f32.convert_i32_u - f32.const 0x1.116p+17 (;=139968;) - f32.div - local.tee $6 - f32.const 0x1p+9 (;=512;) - f32.mul - i32.trunc_f32_s - i32.const 2 - i32.shl - i32.add - i32.load - local.set $2 - loop $label$4 ;; label = @5 - local.get $2 - i32.const 12 - i32.add - local.set $5 - local.get $2 - f32.load offset=4 - local.get $6 - f32.lt - if ;; label = @6 - block ;; label = @7 - local.get $5 - local.set $2 - br 2 (;@5;) - end - end - end - local.get $0 - i32.const 2052 - i32.add - local.get $3 - i32.add - local.get $2 - i32.load - i32.store8 - local.get $3 - i32.const 1 - i32.add - local.tee $3 - local.get $1 - i32.ne - if ;; label = @5 - block ;; label = @6 - local.get $4 - local.set $2 - br 2 (;@4;) - end - end - end - i32.const 1396 - local.get $4 - i32.store - end - end - local.get $0 - i32.const 2052 - i32.add - local.get $1 - i32.add - i32.const 10 - i32.store8 - local.get $0 - i32.const 2052 - i32.add - local.get $1 - i32.const 1 - i32.add - local.tee $1 - i32.add - i32.const 0 - i32.store8 - local.get $0 - i32.const 2116 - i32.add - local.get $1 - i32.store - local.get $0 - i32.const 2052 - i32.add - local.tee $1 - call $31 - local.tee $3 - i32.const 1024 - i32.load - local.tee $2 - i32.le_s - if ;; label = @2 - block ;; label = @3 - local.get $1 - call $35 - drop - i32.const 1024 - i32.const 1024 - i32.load - local.get $3 - i32.sub - i32.store - return - end - end - local.get $2 - i32.const 0 - i32.le_s - if ;; label = @2 - return - end - local.get $0 - i32.const 2052 - i32.add - local.get $2 - i32.add - i32.const 0 - i32.store8 - local.get $1 - call $35 - drop - local.get $0 - i32.const 2052 - i32.add - i32.const 1024 - i32.load - i32.add - i32.const 122 - i32.store8 - i32.const 1024 - i32.const 0 - i32.store - end - ) - (func $9 (;22;) (type $2) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $1 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $1 - local.tee $2 - local.get $0 - i32.load offset=60 - i32.store - i32.const 6 - local.get $2 - call $fimport$10 - call $11 - local.set $0 - local.get $1 - global.set $global$1 - local.get $0 - end - ) - (func $10 (;23;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 32 - i32.add - global.set $global$1 - local.get $4 - local.tee $3 - local.get $0 - i32.load offset=60 - i32.store - local.get $3 - i32.const 0 - i32.store offset=4 - local.get $3 - local.get $1 - i32.store offset=8 - local.get $3 - local.get $4 - i32.const 20 - i32.add - local.tee $0 - i32.store offset=12 - local.get $3 - local.get $2 - i32.store offset=16 - i32.const 140 - local.get $3 - call $fimport$14 - call $11 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $0 - i32.const -1 - i32.store - i32.const -1 - end - else - local.get $0 - i32.load - end - local.set $0 - local.get $4 - global.set $global$1 - local.get $0 - end - ) - (func $11 (;24;) (type $2) (param $0 i32) (result i32) - local.get $0 - i32.const -4096 - i32.gt_u - if (result i32) ;; label = @1 - block (result i32) ;; label = @2 - call $12 - i32.const 0 - local.get $0 - i32.sub - i32.store - i32.const -1 - end - else - local.get $0 - end - ) - (func $12 (;25;) (type $4) (result i32) - i32.const 4172 - ) - (func $13 (;26;) (type $3) (param $0 i32) - nop - ) - (func $14 (;27;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 80 - i32.add - global.set $global$1 - local.get $4 - local.set $3 - local.get $4 - i32.const 12 - i32.add - local.set $5 - local.get $0 - i32.const 3 - i32.store offset=36 - local.get $0 - i32.load - i32.const 64 - i32.and - i32.eqz - if ;; label = @2 - block ;; label = @3 - local.get $3 - local.get $0 - i32.load offset=60 - i32.store - local.get $3 - i32.const 21505 - i32.store offset=4 - local.get $3 - local.get $5 - i32.store offset=8 - i32.const 54 - local.get $3 - call $fimport$13 - if ;; label = @4 - local.get $0 - i32.const -1 - i32.store8 offset=75 - end - end - end - local.get $0 - local.get $1 - local.get $2 - call $15 - local.set $0 - local.get $4 - global.set $global$1 - local.get $0 - end - ) - (func $15 (;28;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $8 - global.get $global$1 - i32.const 48 - i32.add - global.set $global$1 - local.get $8 - i32.const 16 - i32.add - local.set $9 - local.get $8 - local.set $10 - local.get $8 - i32.const 32 - i32.add - local.tee $3 - local.get $0 - i32.const 28 - i32.add - local.tee $6 - i32.load - local.tee $4 - i32.store - local.get $3 - local.get $0 - i32.const 20 - i32.add - local.tee $11 - i32.load - local.get $4 - i32.sub - local.tee $5 - i32.store offset=4 - local.get $3 - local.get $1 - i32.store offset=8 - local.get $3 - local.get $2 - i32.store offset=12 - local.get $0 - i32.const 60 - i32.add - local.set $13 - local.get $0 - i32.const 44 - i32.add - local.set $14 - local.get $3 - local.set $1 - i32.const 2 - local.set $4 - local.get $5 - local.get $2 - i32.add - local.set $12 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - loop $label$5 ;; label = @5 - i32.const 4128 - i32.load - if ;; label = @6 - block ;; label = @7 - i32.const 1 - local.get $0 - call $fimport$9 - local.get $10 - local.get $13 - i32.load - i32.store - local.get $10 - local.get $1 - i32.store offset=4 - local.get $10 - local.get $4 - i32.store offset=8 - i32.const 146 - local.get $10 - call $fimport$15 - call $11 - local.set $3 - i32.const 0 - call $fimport$7 - end - else - block ;; label = @7 - local.get $9 - local.get $13 - i32.load - i32.store - local.get $9 - local.get $1 - i32.store offset=4 - local.get $9 - local.get $4 - i32.store offset=8 - i32.const 146 - local.get $9 - call $fimport$15 - call $11 - local.set $3 - end - end - local.get $12 - local.get $3 - i32.eq - br_if 1 (;@4;) - local.get $3 - i32.const 0 - i32.lt_s - br_if 2 (;@3;) - local.get $3 - local.get $1 - i32.load offset=4 - local.tee $5 - i32.gt_u - if (result i32) ;; label = @6 - block (result i32) ;; label = @7 - local.get $6 - local.get $14 - i32.load - local.tee $7 - i32.store - local.get $11 - local.get $7 - i32.store - local.get $1 - i32.load offset=12 - local.set $7 - local.get $1 - i32.const 8 - i32.add - local.set $1 - local.get $4 - i32.const -1 - i32.add - local.set $4 - local.get $3 - local.get $5 - i32.sub - end - else - local.get $4 - i32.const 2 - i32.eq - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - local.get $6 - local.get $6 - i32.load - local.get $3 - i32.add - i32.store - local.get $5 - local.set $7 - i32.const 2 - local.set $4 - local.get $3 - end - else - block (result i32) ;; label = @8 - local.get $5 - local.set $7 - local.get $3 - end - end - end - local.set $5 - local.get $1 - local.get $1 - i32.load - local.get $5 - i32.add - i32.store - local.get $1 - local.get $7 - local.get $5 - i32.sub - i32.store offset=4 - local.get $12 - local.get $3 - i32.sub - local.set $12 - br 0 (;@5;) - end - end - local.get $0 - local.get $14 - i32.load - local.tee $1 - local.get $0 - i32.load offset=48 - i32.add - i32.store offset=16 - local.get $6 - local.get $1 - i32.store - local.get $11 - local.get $1 - i32.store - br 1 (;@2;) - end - local.get $0 - i32.const 0 - i32.store offset=16 - local.get $6 - i32.const 0 - i32.store - local.get $11 - i32.const 0 - i32.store - local.get $0 - local.get $0 - i32.load - i32.const 32 - i32.or - i32.store - local.get $4 - i32.const 2 - i32.eq - if (result i32) ;; label = @3 - i32.const 0 - else - local.get $2 - local.get $1 - i32.load offset=4 - i32.sub - end - local.set $2 - end - local.get $8 - global.set $global$1 - local.get $2 - end - ) - (func $16 (;29;) (type $3) (param $0 i32) - local.get $0 - i32.load offset=68 - i32.eqz - if ;; label = @1 - local.get $0 - call $13 - end - ) - (func $17 (;30;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $1 - i32.const 255 - i32.and - local.set $5 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - local.get $2 - i32.const 0 - i32.ne - local.tee $4 - local.get $0 - i32.const 3 - i32.and - i32.const 0 - i32.ne - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $1 - i32.const 255 - i32.and - local.set $4 - local.get $2 - local.set $3 - local.get $0 - local.set $2 - loop $label$6 ;; label = @7 - local.get $2 - i32.load8_s - local.get $4 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $3 - local.set $0 - br 6 (;@3;) - end - end - local.get $3 - i32.const -1 - i32.add - local.tee $3 - i32.const 0 - i32.ne - local.tee $0 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 3 - i32.and - i32.const 0 - i32.ne - i32.and - br_if 0 (;@7;) - br 3 (;@4;) - end - end - else - block ;; label = @6 - local.get $2 - local.set $3 - local.get $0 - local.set $2 - local.get $4 - local.set $0 - end - end - end - local.get $0 - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $0 - br 2 (;@3;) - end - else - i32.const 0 - local.set $0 - end - br 1 (;@2;) - end - local.get $2 - i32.load8_s - local.get $1 - i32.const 255 - i32.and - local.tee $1 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.ne - if ;; label = @3 - block ;; label = @4 - local.get $5 - i32.const 16843009 - i32.mul - local.set $3 - block $label$12 ;; label = @5 - block $label$13 ;; label = @6 - local.get $0 - i32.const 3 - i32.le_u - br_if 0 (;@6;) - loop $label$14 ;; label = @7 - local.get $2 - i32.load - local.get $3 - i32.xor - local.tee $4 - i32.const -2139062144 - i32.and - i32.const -2139062144 - i32.xor - local.get $4 - i32.const -16843009 - i32.add - i32.and - i32.eqz - if ;; label = @8 - block ;; label = @9 - local.get $2 - i32.const 4 - i32.add - local.set $2 - local.get $0 - i32.const -4 - i32.add - local.tee $0 - i32.const 3 - i32.gt_u - br_if 2 (;@7;) - br 3 (;@6;) - end - end - end - br 1 (;@5;) - end - local.get $0 - i32.eqz - if ;; label = @6 - block ;; label = @7 - i32.const 0 - local.set $0 - br 5 (;@2;) - end - end - end - loop $label$17 ;; label = @5 - local.get $2 - i32.load8_s - local.get $1 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eq - br_if 3 (;@2;) - local.get $2 - i32.const 1 - i32.add - local.set $2 - local.get $0 - i32.const -1 - i32.add - local.tee $0 - br_if 0 (;@5;) - i32.const 0 - local.set $0 - end - end - end - end - local.get $0 - if (result i32) ;; label = @2 - local.get $2 - else - i32.const 0 - end - end - ) - (func $18 (;31;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 224 - i32.add - global.set $global$1 - local.get $4 - i32.const 136 - i32.add - local.set $5 - local.get $4 - i32.const 80 - i32.add - local.tee $3 - i64.const 0 - i64.store align=4 - local.get $3 - i64.const 0 - i64.store offset=8 align=4 - local.get $3 - i64.const 0 - i64.store offset=16 align=4 - local.get $3 - i64.const 0 - i64.store offset=24 align=4 - local.get $3 - i64.const 0 - i64.store offset=32 align=4 - local.get $4 - i32.const 120 - i32.add - local.tee $6 - local.get $2 - i32.load - i32.store - i32.const 0 - local.get $1 - local.get $6 - local.get $4 - local.tee $2 - local.get $3 - call $19 - i32.const 0 - i32.lt_s - if ;; label = @2 - i32.const -1 - local.set $1 - else - block ;; label = @3 - local.get $0 - i32.load offset=76 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @4 - local.get $0 - call $20 - else - i32.const 0 - end - local.set $12 - local.get $0 - i32.load - local.set $7 - local.get $0 - i32.load8_s offset=74 - i32.const 1 - i32.lt_s - if ;; label = @4 - local.get $0 - local.get $7 - i32.const -33 - i32.and - i32.store - end - local.get $0 - i32.const 48 - i32.add - local.tee $8 - i32.load - if ;; label = @4 - local.get $0 - local.get $1 - local.get $6 - local.get $2 - local.get $3 - call $19 - local.set $1 - else - block ;; label = @5 - local.get $0 - i32.const 44 - i32.add - local.tee $9 - i32.load - local.set $10 - local.get $9 - local.get $5 - i32.store - local.get $0 - i32.const 28 - i32.add - local.tee $13 - local.get $5 - i32.store - local.get $0 - i32.const 20 - i32.add - local.tee $11 - local.get $5 - i32.store - local.get $8 - i32.const 80 - i32.store - local.get $0 - i32.const 16 - i32.add - local.tee $14 - local.get $5 - i32.const 80 - i32.add - i32.store - local.get $0 - local.get $1 - local.get $6 - local.get $2 - local.get $3 - call $19 - local.set $1 - local.get $10 - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 0 - i32.const 0 - local.get $0 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - drop - local.get $11 - i32.load - i32.eqz - if ;; label = @8 - i32.const -1 - local.set $1 - end - local.get $9 - local.get $10 - i32.store - local.get $8 - i32.const 0 - i32.store - local.get $14 - i32.const 0 - i32.store - local.get $13 - i32.const 0 - i32.store - local.get $11 - i32.const 0 - i32.store - end - end - end - end - local.get $0 - local.get $0 - i32.load - local.tee $2 - local.get $7 - i32.const 32 - i32.and - i32.or - i32.store - local.get $12 - if ;; label = @4 - local.get $0 - call $13 - end - local.get $2 - i32.const 32 - i32.and - if ;; label = @4 - i32.const -1 - local.set $1 - end - end - end - local.get $4 - global.set $global$1 - local.get $1 - end - ) - (func $19 (;32;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) - (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) (local $47 i32) (local $48 i32) (local $49 i32) (local $50 i64) (local $51 i64) (local $52 f64) (local $53 f64) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $23 - global.get $global$1 - i32.const 624 - i32.add - global.set $global$1 - local.get $23 - i32.const 16 - i32.add - local.set $20 - local.get $23 - local.set $16 - local.get $23 - i32.const 528 - i32.add - local.set $36 - local.get $0 - i32.const 0 - i32.ne - local.set $30 - local.get $23 - i32.const 536 - i32.add - local.tee $17 - i32.const 40 - i32.add - local.tee $21 - local.set $38 - local.get $17 - i32.const 39 - i32.add - local.set $39 - local.get $23 - i32.const 8 - i32.add - local.tee $37 - i32.const 4 - i32.add - local.set $42 - i32.const 0 - local.get $23 - i32.const 588 - i32.add - local.tee $19 - local.tee $27 - i32.sub - local.set $43 - local.get $23 - i32.const 576 - i32.add - local.tee $17 - i32.const 12 - i32.add - local.set $33 - local.get $17 - i32.const 11 - i32.add - local.set $40 - local.get $33 - local.tee $28 - local.get $27 - i32.sub - local.set $44 - i32.const -2 - local.get $27 - i32.sub - local.set $45 - local.get $28 - i32.const 2 - i32.add - local.set $46 - local.get $23 - i32.const 24 - i32.add - local.tee $47 - i32.const 288 - i32.add - local.set $48 - local.get $19 - i32.const 9 - i32.add - local.tee $31 - local.set $41 - local.get $19 - i32.const 8 - i32.add - local.set $34 - i32.const 0 - local.set $15 - i32.const 0 - local.set $10 - i32.const 0 - local.set $17 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - loop $label$4 ;; label = @4 - block $label$5 ;; label = @5 - local.get $15 - i32.const -1 - i32.gt_s - if ;; label = @6 - local.get $10 - i32.const 2147483647 - local.get $15 - i32.sub - i32.gt_s - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - call $12 - i32.const 75 - i32.store - i32.const -1 - end - else - local.get $10 - local.get $15 - i32.add - end - local.set $15 - end - local.get $1 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eqz - br_if 2 (;@3;) - local.get $1 - local.set $11 - block $label$9 ;; label = @6 - block $label$10 ;; label = @7 - loop $label$11 ;; label = @8 - block $label$12 ;; label = @9 - block $label$13 ;; label = @10 - block $label$14 ;; label = @11 - block $label$15 ;; label = @12 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 0 - i32.sub - br_table 1 (;@11;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 0 (;@12;) 2 (;@10;) - end - local.get $11 - local.set $5 - br 4 (;@7;) - end - local.get $11 - local.set $5 - br 1 (;@9;) - end - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.set $5 - br 1 (;@8;) - end - end - br 1 (;@6;) - end - loop $label$16 ;; label = @7 - local.get $5 - i32.load8_s offset=1 - i32.const 37 - i32.ne - br_if 1 (;@6;) - local.get $11 - i32.const 1 - i32.add - local.set $11 - local.get $5 - i32.const 2 - i32.add - local.tee $5 - i32.load8_s - i32.const 37 - i32.eq - br_if 0 (;@7;) - end - end - local.get $11 - local.get $1 - i32.sub - local.set $10 - local.get $30 - if ;; label = @6 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @7 - local.get $1 - local.get $10 - local.get $0 - call $21 - drop - end - end - local.get $10 - if ;; label = @6 - block ;; label = @7 - local.get $5 - local.set $1 - br 3 (;@4;) - end - end - local.get $5 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $10 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $9 - i32.const 10 - i32.lt_u - if (result i32) ;; label = @6 - block (result i32) ;; label = @7 - local.get $5 - i32.const 3 - i32.add - local.set $10 - local.get $5 - i32.load8_s offset=2 - i32.const 36 - i32.eq - local.tee $12 - if ;; label = @8 - local.get $10 - local.set $11 - end - local.get $12 - if ;; label = @8 - i32.const 1 - local.set $17 - end - local.get $11 - i32.load8_s - local.set $5 - local.get $12 - i32.eqz - if ;; label = @8 - i32.const -1 - local.set $9 - end - local.get $17 - end - else - block (result i32) ;; label = @7 - local.get $10 - local.set $5 - i32.const -1 - local.set $9 - local.get $17 - end - end - local.set $10 - block $label$25 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - local.tee $12 - i32.const 32 - i32.lt_u - if ;; label = @7 - block ;; label = @8 - i32.const 0 - local.set $17 - loop $label$27 ;; label = @9 - i32.const 1 - local.get $12 - i32.shl - i32.const 75913 - i32.and - i32.eqz - br_if 3 (;@6;) - i32.const 1 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - i32.shl - local.get $17 - i32.or - local.set $17 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - local.tee $12 - i32.const 32 - i32.lt_u - br_if 0 (;@9;) - end - end - else - i32.const 0 - local.set $17 - end - end - block $label$29 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 42 - i32.eq - if ;; label = @7 - block ;; label = @8 - block $label$31 (result i32) ;; label = @9 - block $label$32 ;; label = @10 - local.get $11 - i32.const 1 - i32.add - local.tee $7 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $12 - i32.const 10 - i32.ge_u - br_if 0 (;@10;) - local.get $11 - i32.load8_s offset=2 - i32.const 36 - i32.ne - br_if 0 (;@10;) - local.get $4 - local.get $12 - i32.const 2 - i32.shl - i32.add - i32.const 10 - i32.store - i32.const 1 - local.set $8 - local.get $3 - local.get $7 - i32.load8_s - i32.const -48 - i32.add - i32.const 3 - i32.shl - i32.add - i64.load - i32.wrap_i64 - local.set $10 - local.get $11 - i32.const 3 - i32.add - br 1 (;@9;) - end - local.get $10 - if ;; label = @10 - block ;; label = @11 - i32.const -1 - local.set $15 - br 6 (;@5;) - end - end - local.get $30 - i32.eqz - if ;; label = @10 - block ;; label = @11 - local.get $17 - local.set $12 - i32.const 0 - local.set $17 - local.get $7 - local.set $11 - i32.const 0 - local.set $10 - br 5 (;@6;) - end - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $11 - i32.load - local.set $10 - local.get $2 - local.get $11 - i32.const 4 - i32.add - i32.store - i32.const 0 - local.set $8 - local.get $7 - end - local.set $11 - local.get $17 - i32.const 8192 - i32.or - local.set $12 - i32.const 0 - local.get $10 - i32.sub - local.set $7 - local.get $11 - i32.load8_s - local.set $5 - local.get $10 - i32.const 0 - i32.lt_s - local.tee $6 - i32.eqz - if ;; label = @9 - local.get $17 - local.set $12 - end - local.get $8 - local.set $17 - local.get $6 - if ;; label = @9 - local.get $7 - local.set $10 - end - end - else - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $12 - i32.const 10 - i32.lt_u - if ;; label = @8 - block ;; label = @9 - i32.const 0 - local.set $7 - local.get $12 - local.set $5 - loop $label$39 ;; label = @10 - local.get $7 - i32.const 10 - i32.mul - local.get $5 - i32.add - local.set $7 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $12 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - br_if 0 (;@10;) - end - local.get $7 - i32.const 0 - i32.lt_s - if ;; label = @10 - block ;; label = @11 - i32.const -1 - local.set $15 - br 6 (;@5;) - end - else - block ;; label = @11 - local.get $12 - local.set $5 - local.get $17 - local.set $12 - local.get $10 - local.set $17 - local.get $7 - local.set $10 - end - end - end - else - block ;; label = @9 - local.get $17 - local.set $12 - local.get $10 - local.set $17 - i32.const 0 - local.set $10 - end - end - end - end - block $label$43 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 46 - i32.eq - if ;; label = @7 - block ;; label = @8 - local.get $11 - i32.const 1 - i32.add - local.tee $7 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 42 - i32.ne - if ;; label = @9 - block ;; label = @10 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - if ;; label = @11 - block ;; label = @12 - local.get $7 - local.set $11 - i32.const 0 - local.set $7 - end - else - block ;; label = @12 - i32.const 0 - local.set $5 - local.get $7 - local.set $11 - br 6 (;@6;) - end - end - loop $label$48 ;; label = @11 - local.get $7 - i32.const 10 - i32.mul - local.get $5 - i32.add - local.set $5 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - i32.const -48 - i32.add - local.tee $8 - i32.const 10 - i32.ge_u - br_if 5 (;@6;) - local.get $5 - local.set $7 - local.get $8 - local.set $5 - br 0 (;@11;) - end - end - end - local.get $11 - i32.const 2 - i32.add - local.tee $7 - i32.load8_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - if ;; label = @9 - local.get $11 - i32.load8_s offset=3 - i32.const 36 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.get $5 - i32.const 2 - i32.shl - i32.add - i32.const 10 - i32.store - local.get $3 - local.get $7 - i32.load8_s - i32.const -48 - i32.add - i32.const 3 - i32.shl - i32.add - i64.load - i32.wrap_i64 - local.set $5 - local.get $11 - i32.const 4 - i32.add - local.set $11 - br 5 (;@6;) - end - end - end - local.get $17 - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - end - local.get $30 - if (result i32) ;; label = @9 - block (result i32) ;; label = @10 - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $11 - i32.load - local.set $5 - local.get $2 - local.get $11 - i32.const 4 - i32.add - i32.store - local.get $7 - end - else - block (result i32) ;; label = @10 - i32.const 0 - local.set $5 - local.get $7 - end - end - local.set $11 - end - else - i32.const -1 - local.set $5 - end - end - local.get $11 - local.set $7 - i32.const 0 - local.set $8 - loop $label$55 ;; label = @6 - local.get $7 - i32.load8_s - i32.const -65 - i32.add - local.tee $6 - i32.const 57 - i32.gt_u - if ;; label = @7 - block ;; label = @8 - i32.const -1 - local.set $15 - br 3 (;@5;) - end - end - local.get $7 - i32.const 1 - i32.add - local.set $11 - local.get $8 - i32.const 58 - i32.mul - i32.const 1699 - i32.add - local.get $6 - i32.add - i32.load8_s - local.tee $13 - i32.const 255 - i32.and - local.tee $6 - i32.const -1 - i32.add - i32.const 8 - i32.lt_u - if ;; label = @7 - block ;; label = @8 - local.get $11 - local.set $7 - local.get $6 - local.set $8 - br 2 (;@6;) - end - end - end - local.get $13 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eqz - if ;; label = @6 - block ;; label = @7 - i32.const -1 - local.set $15 - br 2 (;@5;) - end - end - local.get $9 - i32.const -1 - i32.gt_s - local.set $14 - block $label$59 ;; label = @6 - block $label$60 ;; label = @7 - local.get $13 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 19 - i32.eq - if ;; label = @8 - local.get $14 - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - else - br 2 (;@7;) - end - else - block ;; label = @9 - local.get $14 - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.get $9 - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store - local.get $16 - local.get $3 - local.get $9 - i32.const 3 - i32.shl - i32.add - i64.load - i64.store - br 4 (;@7;) - end - end - local.get $30 - i32.eqz - if ;; label = @10 - block ;; label = @11 - i32.const 0 - local.set $15 - br 6 (;@5;) - end - end - local.get $16 - local.get $6 - local.get $2 - call $22 - end - end - br 1 (;@6;) - end - local.get $30 - i32.eqz - if ;; label = @7 - block ;; label = @8 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 4 (;@4;) - end - end - end - local.get $7 - i32.load8_s - local.tee $7 - i32.const -33 - i32.and - local.set $9 - local.get $8 - i32.const 0 - i32.ne - local.get $7 - i32.const 15 - i32.and - i32.const 3 - i32.eq - i32.and - i32.eqz - if ;; label = @6 - local.get $7 - local.set $9 - end - local.get $12 - i32.const -65537 - i32.and - local.set $7 - local.get $12 - i32.const 8192 - i32.and - if ;; label = @6 - local.get $7 - local.set $12 - end - block $label$70 ;; label = @6 - block $label$71 ;; label = @7 - block $label$72 ;; label = @8 - block $label$73 ;; label = @9 - block $label$74 ;; label = @10 - block $label$75 ;; label = @11 - block $label$76 ;; label = @12 - block $label$77 ;; label = @13 - block $label$78 ;; label = @14 - block $label$79 ;; label = @15 - block $label$80 ;; label = @16 - block $label$81 ;; label = @17 - block $label$82 ;; label = @18 - block $label$83 ;; label = @19 - block $label$84 ;; label = @20 - block $label$85 ;; label = @21 - block $label$86 ;; label = @22 - block $label$87 ;; label = @23 - block $label$88 ;; label = @24 - block $label$89 ;; label = @25 - local.get $9 - i32.const 65 - i32.sub - br_table 11 (;@14;) 12 (;@13;) 9 (;@16;) 12 (;@13;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 10 (;@15;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 11 (;@14;) 12 (;@13;) 6 (;@19;) 4 (;@21;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 4 (;@21;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 7 (;@18;) 0 (;@25;) 3 (;@22;) 1 (;@24;) 12 (;@13;) 12 (;@13;) 8 (;@17;) 12 (;@13;) 5 (;@20;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) - end - block $label$90 ;; label = @25 - block $label$91 ;; label = @26 - block $label$92 ;; label = @27 - block $label$93 ;; label = @28 - block $label$94 ;; label = @29 - block $label$95 ;; label = @30 - block $label$96 ;; label = @31 - block $label$97 ;; label = @32 - local.get $8 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 0 - i32.sub - br_table 0 (;@32;) 1 (;@31;) 2 (;@30;) 3 (;@29;) 4 (;@28;) 7 (;@25;) 5 (;@27;) 6 (;@26;) 7 (;@25;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 27 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 26 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i64.extend_i32_s - i64.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 25 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store16 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 24 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store8 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 23 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 22 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i64.extend_i32_s - i64.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 21 (;@4;) - end - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 20 (;@4;) - end - local.get $12 - i32.const 8 - i32.or - local.set $12 - local.get $5 - i32.const 8 - i32.le_u - if ;; label = @24 - i32.const 8 - local.set $5 - end - i32.const 120 - local.set $9 - br 11 (;@12;) - end - br 10 (;@12;) - end - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.eq - if ;; label = @22 - local.get $21 - local.set $7 - else - block ;; label = @23 - local.get $21 - local.set $1 - loop $label$101 ;; label = @24 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $50 - i64.const 7 - i64.and - i64.const 48 - i64.or - i64.store8 - local.get $50 - i64.const 3 - i64.shr_u - local.tee $50 - i64.const 0 - i64.ne - br_if 0 (;@24;) - local.get $1 - local.set $7 - end - end - end - local.get $12 - i32.const 8 - i32.and - if ;; label = @22 - block ;; label = @23 - local.get $38 - local.get $7 - i32.sub - local.tee $1 - i32.const 1 - i32.add - local.set $8 - local.get $5 - local.get $1 - i32.le_s - if ;; label = @24 - local.get $8 - local.set $5 - end - i32.const 0 - local.set $6 - i32.const 2179 - local.set $8 - br 16 (;@7;) - end - else - block ;; label = @23 - i32.const 0 - local.set $6 - i32.const 2179 - local.set $8 - br 16 (;@7;) - end - end - end - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.lt_s - if ;; label = @21 - block ;; label = @22 - local.get $16 - i64.const 0 - local.get $50 - i64.sub - local.tee $50 - i64.store - i32.const 1 - local.set $6 - i32.const 2179 - local.set $8 - br 11 (;@11;) - end - end - local.get $12 - i32.const 2048 - i32.and - if ;; label = @21 - block ;; label = @22 - i32.const 1 - local.set $6 - i32.const 2180 - local.set $8 - br 11 (;@11;) - end - else - block ;; label = @22 - local.get $12 - i32.const 1 - i32.and - local.tee $1 - local.set $6 - local.get $1 - if (result i32) ;; label = @23 - i32.const 2181 - else - i32.const 2179 - end - local.set $8 - br 11 (;@11;) - end - end - end - local.get $16 - i64.load - local.set $50 - i32.const 0 - local.set $6 - i32.const 2179 - local.set $8 - br 8 (;@11;) - end - local.get $39 - local.get $16 - i64.load - i64.store8 - local.get $39 - local.set $1 - local.get $7 - local.set $12 - i32.const 1 - local.set $7 - i32.const 0 - local.set $6 - i32.const 2179 - local.set $8 - local.get $21 - local.set $5 - br 12 (;@6;) - end - call $12 - i32.load - call $24 - local.set $1 - br 7 (;@10;) - end - local.get $16 - i32.load - local.tee $1 - i32.eqz - if ;; label = @17 - i32.const 2189 - local.set $1 - end - br 6 (;@10;) - end - local.get $37 - local.get $16 - i64.load - i64.store32 - local.get $42 - i32.const 0 - i32.store - local.get $16 - local.get $37 - i32.store - local.get $37 - local.set $7 - i32.const -1 - local.set $6 - br 6 (;@9;) - end - local.get $16 - i32.load - local.set $7 - local.get $5 - if ;; label = @15 - block ;; label = @16 - local.get $5 - local.set $6 - br 7 (;@9;) - end - else - block ;; label = @16 - local.get $0 - i32.const 32 - local.get $10 - i32.const 0 - local.get $12 - call $25 - i32.const 0 - local.set $1 - br 8 (;@8;) - end - end - end - local.get $16 - f64.load - local.set $52 - local.get $20 - i32.const 0 - i32.store - local.get $52 - i64.reinterpret_f64 - i64.const 0 - i64.lt_s - if (result i32) ;; label = @14 - block (result i32) ;; label = @15 - i32.const 1 - local.set $24 - local.get $52 - f64.neg - local.set $52 - i32.const 2196 - end - else - block (result i32) ;; label = @15 - local.get $12 - i32.const 1 - i32.and - local.set $1 - local.get $12 - i32.const 2048 - i32.and - if (result i32) ;; label = @16 - block (result i32) ;; label = @17 - i32.const 1 - local.set $24 - i32.const 2199 - end - else - block (result i32) ;; label = @17 - local.get $1 - local.set $24 - local.get $1 - if (result i32) ;; label = @18 - i32.const 2202 - else - i32.const 2197 - end - end - end - end - end - local.set $26 - block $label$119 ;; label = @14 - local.get $52 - i64.reinterpret_f64 - i64.const 9218868437227405312 - i64.and - i64.const 9218868437227405312 - i64.lt_u - if ;; label = @15 - block ;; label = @16 - local.get $52 - local.get $20 - call $27 - f64.const 0x1p+1 (;=2;) - f64.mul - local.tee $52 - f64.const 0x0p+0 (;=0;) - f64.ne - local.tee $1 - if ;; label = @17 - local.get $20 - local.get $20 - i32.load - i32.const -1 - i32.add - i32.store - end - local.get $9 - i32.const 32 - i32.or - local.tee $22 - i32.const 97 - i32.eq - if ;; label = @17 - block ;; label = @18 - local.get $26 - i32.const 9 - i32.add - local.set $1 - local.get $9 - i32.const 32 - i32.and - local.tee $6 - if ;; label = @19 - local.get $1 - local.set $26 - end - local.get $5 - i32.const 11 - i32.gt_u - i32.const 12 - local.get $5 - i32.sub - local.tee $1 - i32.eqz - i32.or - i32.eqz - if ;; label = @19 - block ;; label = @20 - f64.const 0x1p+3 (;=8;) - local.set $53 - loop $label$125 ;; label = @21 - local.get $53 - f64.const 0x1p+4 (;=16;) - f64.mul - local.set $53 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - br_if 0 (;@21;) - end - local.get $26 - i32.load8_s - i32.const 45 - i32.eq - if (result f64) ;; label = @21 - local.get $53 - local.get $52 - f64.neg - local.get $53 - f64.sub - f64.add - f64.neg - else - local.get $52 - local.get $53 - f64.add - local.get $53 - f64.sub - end - local.set $52 - end - end - i32.const 0 - local.get $20 - i32.load - local.tee $7 - i32.sub - local.set $1 - local.get $7 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @19 - local.get $1 - else - local.get $7 - end - i64.extend_i32_s - local.get $33 - call $23 - local.tee $1 - local.get $33 - i32.eq - if ;; label = @19 - block ;; label = @20 - local.get $40 - i32.const 48 - i32.store8 - local.get $40 - local.set $1 - end - end - local.get $24 - i32.const 2 - i32.or - local.set $13 - local.get $1 - i32.const -1 - i32.add - local.get $7 - i32.const 31 - i32.shr_s - i32.const 2 - i32.and - i32.const 43 - i32.add - i32.store8 - local.get $1 - i32.const -2 - i32.add - local.tee $8 - local.get $9 - i32.const 15 - i32.add - i32.store8 - local.get $5 - i32.const 1 - i32.lt_s - local.set $9 - local.get $12 - i32.const 8 - i32.and - i32.eqz - local.set $14 - local.get $19 - local.set $1 - loop $label$131 ;; label = @19 - local.get $1 - local.get $52 - i32.trunc_f64_s - local.tee $7 - i32.const 2163 - i32.add - i32.load8_u - local.get $6 - i32.or - i32.store8 - local.get $52 - local.get $7 - f64.convert_i32_s - f64.sub - f64.const 0x1p+4 (;=16;) - f64.mul - local.set $52 - block $label$132 (result i32) ;; label = @20 - local.get $1 - i32.const 1 - i32.add - local.tee $7 - local.get $27 - i32.sub - i32.const 1 - i32.eq - if (result i32) ;; label = @21 - block (result i32) ;; label = @22 - local.get $7 - local.get $14 - local.get $9 - local.get $52 - f64.const 0x0p+0 (;=0;) - f64.eq - i32.and - i32.and - br_if 2 (;@20;) - drop - local.get $7 - i32.const 46 - i32.store8 - local.get $1 - i32.const 2 - i32.add - end - else - local.get $7 - end - end - local.set $1 - local.get $52 - f64.const 0x0p+0 (;=0;) - f64.ne - br_if 0 (;@19;) - end - local.get $46 - local.get $5 - i32.add - local.get $8 - local.tee $7 - i32.sub - local.set $6 - local.get $44 - local.get $7 - i32.sub - local.get $1 - i32.add - local.set $9 - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - i32.const 0 - i32.ne - local.get $45 - local.get $1 - i32.add - local.get $5 - i32.lt_s - i32.and - if (result i32) ;; label = @19 - local.get $6 - else - local.get $9 - local.tee $6 - end - local.get $13 - i32.add - local.tee $5 - local.get $12 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $26 - local.get $13 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $5 - local.get $12 - i32.const 65536 - i32.xor - call $25 - local.get $1 - local.get $27 - i32.sub - local.set $1 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $19 - local.get $1 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $6 - local.get $1 - local.get $28 - local.get $7 - i32.sub - local.tee $1 - i32.add - i32.sub - i32.const 0 - i32.const 0 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $8 - local.get $1 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $5 - local.get $10 - i32.ge_s - if ;; label = @19 - local.get $5 - local.set $10 - end - br 4 (;@14;) - end - end - local.get $1 - if ;; label = @17 - block ;; label = @18 - local.get $20 - local.get $20 - i32.load - i32.const -28 - i32.add - local.tee $6 - i32.store - local.get $52 - f64.const 0x1p+28 (;=268435456;) - f64.mul - local.set $52 - end - else - local.get $20 - i32.load - local.set $6 - end - local.get $6 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @17 - local.get $47 - else - local.get $48 - end - local.tee $7 - local.set $8 - loop $label$145 ;; label = @17 - local.get $8 - local.get $52 - i32.trunc_f64_s - local.tee $1 - i32.store - local.get $8 - i32.const 4 - i32.add - local.set $8 - local.get $52 - local.get $1 - f64.convert_i32_u - f64.sub - f64.const 0x1.dcd65p+29 (;=1000000000;) - f64.mul - local.tee $52 - f64.const 0x0p+0 (;=0;) - f64.ne - br_if 0 (;@17;) - end - local.get $6 - i32.const 0 - i32.gt_s - if ;; label = @17 - block ;; label = @18 - local.get $7 - local.set $1 - loop $label$147 ;; label = @19 - local.get $6 - i32.const 29 - i32.gt_s - if (result i32) ;; label = @20 - i32.const 29 - else - local.get $6 - end - local.set $14 - block $label$150 ;; label = @20 - local.get $8 - i32.const -4 - i32.add - local.tee $6 - local.get $1 - i32.ge_u - if ;; label = @21 - block ;; label = @22 - local.get $14 - i64.extend_i32_u - local.set $50 - i32.const 0 - local.set $13 - loop $label$152 ;; label = @23 - local.get $6 - local.get $6 - i32.load - i64.extend_i32_u - local.get $50 - i64.shl - local.get $13 - i64.extend_i32_u - i64.add - local.tee $51 - i64.const 1000000000 - i64.rem_u - i64.store32 - local.get $51 - i64.const 1000000000 - i64.div_u - i32.wrap_i64 - local.set $13 - local.get $6 - i32.const -4 - i32.add - local.tee $6 - local.get $1 - i32.ge_u - br_if 0 (;@23;) - end - local.get $13 - i32.eqz - br_if 2 (;@20;) - local.get $1 - i32.const -4 - i32.add - local.tee $1 - local.get $13 - i32.store - end - end - end - loop $label$153 ;; label = @20 - local.get $8 - local.get $1 - i32.gt_u - if ;; label = @21 - local.get $8 - i32.const -4 - i32.add - local.tee $6 - i32.load - i32.eqz - if ;; label = @22 - block ;; label = @23 - local.get $6 - local.set $8 - br 3 (;@20;) - end - end - end - end - local.get $20 - local.get $20 - i32.load - local.get $14 - i32.sub - local.tee $6 - i32.store - local.get $6 - i32.const 0 - i32.gt_s - br_if 0 (;@19;) - end - end - else - local.get $7 - local.set $1 - end - local.get $5 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @17 - i32.const 6 - else - local.get $5 - end - local.set $18 - local.get $6 - i32.const 0 - i32.lt_s - if ;; label = @17 - block ;; label = @18 - local.get $18 - i32.const 25 - i32.add - i32.const 9 - i32.div_s - i32.const 1 - i32.add - local.set $14 - local.get $22 - i32.const 102 - i32.eq - local.set $25 - local.get $8 - local.set $5 - loop $label$160 ;; label = @19 - i32.const 0 - local.get $6 - i32.sub - local.tee $13 - i32.const 9 - i32.gt_s - if ;; label = @20 - i32.const 9 - local.set $13 - end - block $label$162 ;; label = @20 - local.get $1 - local.get $5 - i32.lt_u - if ;; label = @21 - block ;; label = @22 - i32.const 1 - local.get $13 - i32.shl - i32.const -1 - i32.add - local.set $29 - i32.const 1000000000 - local.get $13 - i32.shr_u - local.set $35 - i32.const 0 - local.set $6 - local.get $1 - local.set $8 - loop $label$164 ;; label = @23 - local.get $8 - local.get $8 - i32.load - local.tee $32 - local.get $13 - i32.shr_u - local.get $6 - i32.add - i32.store - local.get $32 - local.get $29 - i32.and - local.get $35 - i32.mul - local.set $6 - local.get $8 - i32.const 4 - i32.add - local.tee $8 - local.get $5 - i32.lt_u - br_if 0 (;@23;) - end - local.get $1 - i32.const 4 - i32.add - local.set $8 - local.get $1 - i32.load - i32.eqz - if ;; label = @23 - local.get $8 - local.set $1 - end - local.get $6 - i32.eqz - br_if 2 (;@20;) - local.get $5 - local.get $6 - i32.store - local.get $5 - i32.const 4 - i32.add - local.set $5 - end - else - block ;; label = @22 - local.get $1 - i32.const 4 - i32.add - local.set $8 - local.get $1 - i32.load - i32.eqz - if ;; label = @23 - local.get $8 - local.set $1 - end - end - end - end - local.get $25 - if (result i32) ;; label = @20 - local.get $7 - else - local.get $1 - end - local.tee $8 - local.get $14 - i32.const 2 - i32.shl - i32.add - local.set $6 - local.get $5 - local.get $8 - i32.sub - i32.const 2 - i32.shr_s - local.get $14 - i32.gt_s - if ;; label = @20 - local.get $6 - local.set $5 - end - local.get $20 - local.get $20 - i32.load - local.get $13 - i32.add - local.tee $6 - i32.store - local.get $6 - i32.const 0 - i32.lt_s - br_if 0 (;@19;) - local.get $5 - local.set $13 - end - end - else - local.get $8 - local.set $13 - end - local.get $7 - local.set $25 - block $label$172 ;; label = @17 - local.get $1 - local.get $13 - i32.lt_u - if ;; label = @18 - block ;; label = @19 - local.get $25 - local.get $1 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - local.set $5 - local.get $1 - i32.load - local.tee $6 - i32.const 10 - i32.lt_u - br_if 2 (;@17;) - i32.const 10 - local.set $8 - loop $label$174 ;; label = @20 - local.get $5 - i32.const 1 - i32.add - local.set $5 - local.get $6 - local.get $8 - i32.const 10 - i32.mul - local.tee $8 - i32.ge_u - br_if 0 (;@20;) - end - end - else - i32.const 0 - local.set $5 - end - end - local.get $22 - i32.const 103 - i32.eq - local.set $29 - local.get $18 - i32.const 0 - i32.ne - local.set $35 - local.get $18 - local.get $22 - i32.const 102 - i32.ne - if (result i32) ;; label = @17 - local.get $5 - else - i32.const 0 - end - i32.sub - local.get $35 - local.get $29 - i32.and - i32.const 31 - i32.shl - i32.const 31 - i32.shr_s - i32.add - local.tee $8 - local.get $13 - local.get $25 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - i32.const -9 - i32.add - i32.lt_s - if ;; label = @17 - block ;; label = @18 - local.get $8 - i32.const 9216 - i32.add - local.tee $14 - i32.const 9 - i32.rem_s - i32.const 1 - i32.add - local.tee $8 - i32.const 9 - i32.lt_s - if ;; label = @19 - block ;; label = @20 - i32.const 10 - local.set $6 - loop $label$180 ;; label = @21 - local.get $6 - i32.const 10 - i32.mul - local.set $6 - local.get $8 - i32.const 1 - i32.add - local.tee $8 - i32.const 9 - i32.ne - br_if 0 (;@21;) - end - end - else - i32.const 10 - local.set $6 - end - local.get $7 - i32.const 4 - i32.add - local.get $14 - i32.const 9 - i32.div_s - i32.const -1024 - i32.add - i32.const 2 - i32.shl - i32.add - local.tee $8 - i32.load - local.tee $22 - local.get $6 - i32.rem_u - local.set $14 - block $label$182 ;; label = @19 - local.get $8 - i32.const 4 - i32.add - local.get $13 - i32.eq - local.tee $32 - local.get $14 - i32.eqz - i32.and - i32.eqz - if ;; label = @20 - block ;; label = @21 - local.get $14 - local.get $6 - i32.const 2 - i32.div_s - local.tee $49 - i32.lt_u - if (result f64) ;; label = @22 - f64.const 0x1p-1 (;=0.5;) - else - local.get $32 - local.get $14 - local.get $49 - i32.eq - i32.and - if (result f64) ;; label = @23 - f64.const 0x1p+0 (;=1;) - else - f64.const 0x1.8p+0 (;=1.5;) - end - end - local.set $52 - local.get $22 - local.get $6 - i32.div_u - i32.const 1 - i32.and - if (result f64) ;; label = @22 - f64.const 0x1.0000000000001p+53 (;=9007199254740994;) - else - f64.const 0x1p+53 (;=9007199254740992;) - end - local.set $53 - block $label$190 ;; label = @22 - local.get $24 - if ;; label = @23 - block ;; label = @24 - local.get $26 - i32.load8_s - i32.const 45 - i32.ne - br_if 2 (;@22;) - local.get $53 - f64.neg - local.set $53 - local.get $52 - f64.neg - local.set $52 - end - end - end - local.get $8 - local.get $22 - local.get $14 - i32.sub - local.tee $14 - i32.store - local.get $53 - local.get $52 - f64.add - local.get $53 - f64.eq - br_if 2 (;@19;) - local.get $8 - local.get $14 - local.get $6 - i32.add - local.tee $5 - i32.store - local.get $5 - i32.const 999999999 - i32.gt_u - if ;; label = @22 - loop $label$193 ;; label = @23 - local.get $8 - i32.const 0 - i32.store - local.get $8 - i32.const -4 - i32.add - local.tee $8 - local.get $1 - i32.lt_u - if ;; label = @24 - local.get $1 - i32.const -4 - i32.add - local.tee $1 - i32.const 0 - i32.store - end - local.get $8 - local.get $8 - i32.load - i32.const 1 - i32.add - local.tee $5 - i32.store - local.get $5 - i32.const 999999999 - i32.gt_u - br_if 0 (;@23;) - end - end - local.get $25 - local.get $1 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - local.set $5 - local.get $1 - i32.load - local.tee $14 - i32.const 10 - i32.lt_u - br_if 2 (;@19;) - i32.const 10 - local.set $6 - loop $label$195 ;; label = @22 - local.get $5 - i32.const 1 - i32.add - local.set $5 - local.get $14 - local.get $6 - i32.const 10 - i32.mul - local.tee $6 - i32.ge_u - br_if 0 (;@22;) - end - end - end - end - local.get $1 - local.set $14 - local.get $5 - local.set $6 - local.get $13 - local.get $8 - i32.const 4 - i32.add - local.tee $8 - i32.le_u - if ;; label = @19 - local.get $13 - local.set $8 - end - end - else - block ;; label = @18 - local.get $1 - local.set $14 - local.get $5 - local.set $6 - local.get $13 - local.set $8 - end - end - i32.const 0 - local.get $6 - i32.sub - local.set $32 - loop $label$198 ;; label = @17 - block $label$199 ;; label = @18 - local.get $8 - local.get $14 - i32.le_u - if ;; label = @19 - block ;; label = @20 - i32.const 0 - local.set $22 - br 2 (;@18;) - end - end - local.get $8 - i32.const -4 - i32.add - local.tee $1 - i32.load - if ;; label = @19 - i32.const 1 - local.set $22 - else - block ;; label = @20 - local.get $1 - local.set $8 - br 3 (;@17;) - end - end - end - end - block $label$203 ;; label = @17 - local.get $29 - if ;; label = @18 - block ;; label = @19 - local.get $35 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $18 - i32.add - local.tee $1 - local.get $6 - i32.gt_s - local.get $6 - i32.const -5 - i32.gt_s - i32.and - if (result i32) ;; label = @20 - block (result i32) ;; label = @21 - local.get $9 - i32.const -1 - i32.add - local.set $5 - local.get $1 - i32.const -1 - i32.add - local.get $6 - i32.sub - end - else - block (result i32) ;; label = @21 - local.get $9 - i32.const -2 - i32.add - local.set $5 - local.get $1 - i32.const -1 - i32.add - end - end - local.set $1 - local.get $12 - i32.const 8 - i32.and - local.tee $13 - br_if 2 (;@17;) - block $label$207 ;; label = @20 - local.get $22 - if ;; label = @21 - block ;; label = @22 - local.get $8 - i32.const -4 - i32.add - i32.load - local.tee $18 - i32.eqz - if ;; label = @23 - block ;; label = @24 - i32.const 9 - local.set $9 - br 4 (;@20;) - end - end - local.get $18 - i32.const 10 - i32.rem_u - if ;; label = @23 - block ;; label = @24 - i32.const 0 - local.set $9 - br 4 (;@20;) - end - else - block ;; label = @24 - i32.const 10 - local.set $13 - i32.const 0 - local.set $9 - end - end - loop $label$212 ;; label = @23 - local.get $9 - i32.const 1 - i32.add - local.set $9 - local.get $18 - local.get $13 - i32.const 10 - i32.mul - local.tee $13 - i32.rem_u - i32.eqz - br_if 0 (;@23;) - end - end - else - i32.const 9 - local.set $9 - end - end - local.get $8 - local.get $25 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - i32.const -9 - i32.add - local.set $18 - local.get $5 - i32.const 32 - i32.or - i32.const 102 - i32.eq - if ;; label = @20 - block ;; label = @21 - i32.const 0 - local.set $13 - local.get $1 - local.get $18 - local.get $9 - i32.sub - local.tee $9 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @22 - i32.const 0 - local.tee $9 - else - local.get $9 - end - i32.ge_s - if ;; label = @22 - local.get $9 - local.set $1 - end - end - else - block ;; label = @21 - i32.const 0 - local.set $13 - local.get $1 - local.get $18 - local.get $6 - i32.add - local.get $9 - i32.sub - local.tee $9 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @22 - i32.const 0 - local.tee $9 - else - local.get $9 - end - i32.ge_s - if ;; label = @22 - local.get $9 - local.set $1 - end - end - end - end - else - block ;; label = @19 - local.get $12 - i32.const 8 - i32.and - local.set $13 - local.get $18 - local.set $1 - local.get $9 - local.set $5 - end - end - end - local.get $5 - i32.const 32 - i32.or - i32.const 102 - i32.eq - local.tee $25 - if ;; label = @17 - block ;; label = @18 - i32.const 0 - local.set $9 - local.get $6 - i32.const 0 - i32.le_s - if ;; label = @19 - i32.const 0 - local.set $6 - end - end - else - block ;; label = @18 - local.get $28 - local.get $6 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @19 - local.get $32 - else - local.get $6 - end - i64.extend_i32_s - local.get $33 - call $23 - local.tee $9 - i32.sub - i32.const 2 - i32.lt_s - if ;; label = @19 - loop $label$229 ;; label = @20 - local.get $9 - i32.const -1 - i32.add - local.tee $9 - i32.const 48 - i32.store8 - local.get $28 - local.get $9 - i32.sub - i32.const 2 - i32.lt_s - br_if 0 (;@20;) - end - end - local.get $9 - i32.const -1 - i32.add - local.get $6 - i32.const 31 - i32.shr_s - i32.const 2 - i32.and - i32.const 43 - i32.add - i32.store8 - local.get $9 - i32.const -2 - i32.add - local.tee $6 - local.get $5 - i32.store8 - local.get $6 - local.set $9 - local.get $28 - local.get $6 - i32.sub - local.set $6 - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $24 - i32.const 1 - i32.add - local.get $1 - i32.add - local.get $1 - local.get $13 - i32.or - local.tee $29 - i32.const 0 - i32.ne - i32.add - local.get $6 - i32.add - local.tee $18 - local.get $12 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - local.get $26 - local.get $24 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $18 - local.get $12 - i32.const 65536 - i32.xor - call $25 - block $label$231 ;; label = @17 - local.get $25 - if ;; label = @18 - block ;; label = @19 - local.get $14 - local.get $7 - i32.gt_u - if (result i32) ;; label = @20 - local.get $7 - else - local.get $14 - end - local.tee $9 - local.set $6 - loop $label$235 ;; label = @20 - local.get $6 - i32.load - i64.extend_i32_u - local.get $31 - call $23 - local.set $5 - block $label$236 ;; label = @21 - local.get $6 - local.get $9 - i32.eq - if ;; label = @22 - block ;; label = @23 - local.get $5 - local.get $31 - i32.ne - br_if 2 (;@21;) - local.get $34 - i32.const 48 - i32.store8 - local.get $34 - local.set $5 - end - else - block ;; label = @23 - local.get $5 - local.get $19 - i32.le_u - br_if 2 (;@21;) - local.get $19 - i32.const 48 - local.get $5 - local.get $27 - i32.sub - call $46 - drop - loop $label$239 ;; label = @24 - local.get $5 - i32.const -1 - i32.add - local.tee $5 - local.get $19 - i32.gt_u - br_if 0 (;@24;) - end - end - end - end - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @21 - local.get $5 - local.get $41 - local.get $5 - i32.sub - local.get $0 - call $21 - drop - end - local.get $6 - i32.const 4 - i32.add - local.tee $5 - local.get $7 - i32.le_u - if ;; label = @21 - block ;; label = @22 - local.get $5 - local.set $6 - br 2 (;@20;) - end - end - end - block $label$242 ;; label = @20 - local.get $29 - if ;; label = @21 - block ;; label = @22 - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@20;) - i32.const 2231 - i32.const 1 - local.get $0 - call $21 - drop - end - end - end - local.get $1 - i32.const 0 - i32.gt_s - local.get $5 - local.get $8 - i32.lt_u - i32.and - if ;; label = @20 - loop $label$245 ;; label = @21 - local.get $5 - i32.load - i64.extend_i32_u - local.get $31 - call $23 - local.tee $7 - local.get $19 - i32.gt_u - if ;; label = @22 - block ;; label = @23 - local.get $19 - i32.const 48 - local.get $7 - local.get $27 - i32.sub - call $46 - drop - loop $label$247 ;; label = @24 - local.get $7 - i32.const -1 - i32.add - local.tee $7 - local.get $19 - i32.gt_u - br_if 0 (;@24;) - end - end - end - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @22 - local.get $7 - local.get $1 - i32.const 9 - i32.gt_s - if (result i32) ;; label = @23 - i32.const 9 - else - local.get $1 - end - local.get $0 - call $21 - drop - end - local.get $1 - i32.const -9 - i32.add - local.set $7 - local.get $1 - i32.const 9 - i32.gt_s - local.get $5 - i32.const 4 - i32.add - local.tee $5 - local.get $8 - i32.lt_u - i32.and - if ;; label = @22 - block ;; label = @23 - local.get $7 - local.set $1 - br 2 (;@21;) - end - else - local.get $7 - local.set $1 - end - end - end - local.get $0 - i32.const 48 - local.get $1 - i32.const 9 - i32.add - i32.const 9 - i32.const 0 - call $25 - end - else - block ;; label = @19 - local.get $14 - i32.const 4 - i32.add - local.set $5 - local.get $22 - i32.eqz - if ;; label = @20 - local.get $5 - local.set $8 - end - local.get $1 - i32.const -1 - i32.gt_s - if ;; label = @20 - block ;; label = @21 - local.get $13 - i32.eqz - local.set $13 - local.get $14 - local.set $7 - local.get $1 - local.set $5 - loop $label$256 ;; label = @22 - local.get $7 - i32.load - i64.extend_i32_u - local.get $31 - call $23 - local.tee $1 - local.get $31 - i32.eq - if ;; label = @23 - block ;; label = @24 - local.get $34 - i32.const 48 - i32.store8 - local.get $34 - local.set $1 - end - end - block $label$258 ;; label = @23 - local.get $7 - local.get $14 - i32.eq - if ;; label = @24 - block ;; label = @25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @26 - local.get $1 - i32.const 1 - local.get $0 - call $21 - drop - end - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $13 - local.get $5 - i32.const 1 - i32.lt_s - i32.and - br_if 2 (;@23;) - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@23;) - i32.const 2231 - i32.const 1 - local.get $0 - call $21 - drop - end - else - block ;; label = @25 - local.get $1 - local.get $19 - i32.le_u - br_if 2 (;@23;) - local.get $19 - i32.const 48 - local.get $1 - local.get $43 - i32.add - call $46 - drop - loop $label$262 ;; label = @26 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $19 - i32.gt_u - br_if 0 (;@26;) - end - end - end - end - local.get $41 - local.get $1 - i32.sub - local.set $6 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @23 - local.get $1 - local.get $5 - local.get $6 - i32.gt_s - if (result i32) ;; label = @24 - local.get $6 - else - local.get $5 - end - local.get $0 - call $21 - drop - end - local.get $7 - i32.const 4 - i32.add - local.tee $7 - local.get $8 - i32.lt_u - local.get $5 - local.get $6 - i32.sub - local.tee $5 - i32.const -1 - i32.gt_s - i32.and - br_if 0 (;@22;) - local.get $5 - local.set $1 - end - end - end - local.get $0 - i32.const 48 - local.get $1 - i32.const 18 - i32.add - i32.const 18 - i32.const 0 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@17;) - local.get $9 - local.get $28 - local.get $9 - i32.sub - local.get $0 - call $21 - drop - end - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $18 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $18 - local.get $10 - i32.ge_s - if ;; label = @17 - local.get $18 - local.set $10 - end - end - else - block ;; label = @16 - local.get $0 - i32.const 32 - local.get $10 - local.get $52 - local.get $52 - f64.ne - i32.const 0 - i32.or - local.tee $6 - if (result i32) ;; label = @17 - i32.const 0 - local.tee $24 - else - local.get $24 - end - i32.const 3 - i32.add - local.tee $8 - local.get $7 - call $25 - local.get $0 - i32.load - local.tee $1 - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - block ;; label = @18 - local.get $26 - local.get $24 - local.get $0 - call $21 - drop - local.get $0 - i32.load - local.set $1 - end - end - local.get $9 - i32.const 32 - i32.and - i32.const 0 - i32.ne - local.tee $5 - if (result i32) ;; label = @17 - i32.const 2215 - else - i32.const 2219 - end - local.set $7 - local.get $5 - if (result i32) ;; label = @17 - i32.const 2223 - else - i32.const 2227 - end - local.set $5 - local.get $6 - i32.eqz - if ;; label = @17 - local.get $7 - local.set $5 - end - local.get $1 - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - local.get $5 - i32.const 3 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $8 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $8 - local.get $10 - i32.ge_s - if ;; label = @17 - local.get $8 - local.set $10 - end - end - end - end - local.get $11 - local.set $1 - br 9 (;@4;) - end - local.get $5 - local.set $7 - i32.const 0 - local.set $6 - i32.const 2179 - local.set $8 - local.get $21 - local.set $5 - br 6 (;@6;) - end - local.get $9 - i32.const 32 - i32.and - local.set $7 - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.eq - if (result i32) ;; label = @12 - block (result i32) ;; label = @13 - i64.const 0 - local.set $50 - local.get $21 - end - else - block (result i32) ;; label = @13 - local.get $21 - local.set $1 - loop $label$280 ;; label = @14 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $50 - i32.wrap_i64 - i32.const 15 - i32.and - i32.const 2163 - i32.add - i32.load8_u - local.get $7 - i32.or - i32.store8 - local.get $50 - i64.const 4 - i64.shr_u - local.tee $50 - i64.const 0 - i64.ne - br_if 0 (;@14;) - end - local.get $16 - i64.load - local.set $50 - local.get $1 - end - end - local.set $7 - local.get $9 - i32.const 4 - i32.shr_s - i32.const 2179 - i32.add - local.set $8 - local.get $12 - i32.const 8 - i32.and - i32.eqz - local.get $50 - i64.const 0 - i64.eq - i32.or - local.tee $1 - if ;; label = @12 - i32.const 2179 - local.set $8 - end - local.get $1 - if (result i32) ;; label = @12 - i32.const 0 - else - i32.const 2 - end - local.set $6 - br 4 (;@7;) - end - local.get $50 - local.get $21 - call $23 - local.set $7 - br 3 (;@7;) - end - local.get $1 - i32.const 0 - local.get $5 - call $17 - local.tee $13 - i32.eqz - local.set $14 - local.get $13 - local.get $1 - i32.sub - local.set $8 - local.get $1 - local.get $5 - i32.add - local.set $9 - local.get $7 - local.set $12 - local.get $14 - if (result i32) ;; label = @10 - local.get $5 - else - local.get $8 - end - local.set $7 - i32.const 0 - local.set $6 - i32.const 2179 - local.set $8 - local.get $14 - if (result i32) ;; label = @10 - local.get $9 - else - local.get $13 - end - local.set $5 - br 3 (;@6;) - end - i32.const 0 - local.set $1 - i32.const 0 - local.set $5 - local.get $7 - local.set $8 - loop $label$288 ;; label = @9 - block $label$289 ;; label = @10 - local.get $8 - i32.load - local.tee $9 - i32.eqz - br_if 0 (;@10;) - local.get $36 - local.get $9 - call $26 - local.tee $5 - i32.const 0 - i32.lt_s - local.get $5 - local.get $6 - local.get $1 - i32.sub - i32.gt_u - i32.or - br_if 0 (;@10;) - local.get $8 - i32.const 4 - i32.add - local.set $8 - local.get $6 - local.get $5 - local.get $1 - i32.add - local.tee $1 - i32.gt_u - br_if 1 (;@9;) - end - end - local.get $5 - i32.const 0 - i32.lt_s - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $1 - local.get $12 - call $25 - local.get $1 - if ;; label = @9 - block ;; label = @10 - i32.const 0 - local.set $5 - loop $label$292 ;; label = @11 - local.get $7 - i32.load - local.tee $8 - i32.eqz - br_if 3 (;@8;) - local.get $36 - local.get $8 - call $26 - local.tee $8 - local.get $5 - i32.add - local.tee $5 - local.get $1 - i32.gt_s - br_if 3 (;@8;) - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @12 - local.get $36 - local.get $8 - local.get $0 - call $21 - drop - end - local.get $7 - i32.const 4 - i32.add - local.set $7 - local.get $5 - local.get $1 - i32.lt_u - br_if 0 (;@11;) - br 3 (;@8;) - end - end - else - block ;; label = @10 - i32.const 0 - local.set $1 - br 2 (;@8;) - end - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $1 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $10 - local.get $1 - i32.le_s - if ;; label = @8 - local.get $1 - local.set $10 - end - local.get $11 - local.set $1 - br 3 (;@4;) - end - local.get $12 - i32.const -65537 - i32.and - local.set $1 - local.get $5 - i32.const -1 - i32.gt_s - if ;; label = @7 - local.get $1 - local.set $12 - end - local.get $5 - local.get $16 - i64.load - i64.const 0 - i64.ne - local.tee $9 - i32.or - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - local.get $7 - local.set $1 - local.get $5 - local.get $9 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $38 - local.get $7 - i32.sub - i32.add - local.tee $7 - i32.gt_s - if ;; label = @9 - local.get $5 - local.set $7 - end - local.get $21 - end - else - block (result i32) ;; label = @8 - local.get $21 - local.set $1 - i32.const 0 - local.set $7 - local.get $21 - end - end - local.set $5 - end - local.get $0 - i32.const 32 - local.get $10 - local.get $7 - local.get $5 - local.get $1 - i32.sub - local.tee $9 - i32.lt_s - if (result i32) ;; label = @6 - local.get $9 - local.tee $7 - else - local.get $7 - end - local.get $6 - i32.add - local.tee $5 - i32.lt_s - if (result i32) ;; label = @6 - local.get $5 - local.tee $10 - else - local.get $10 - end - local.get $5 - local.get $12 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @6 - local.get $8 - local.get $6 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $5 - local.get $12 - i32.const 65536 - i32.xor - call $25 - local.get $0 - i32.const 48 - local.get $7 - local.get $9 - i32.const 0 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @6 - local.get $1 - local.get $9 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $11 - local.set $1 - br 1 (;@4;) - end - end - br 1 (;@2;) - end - local.get $0 - i32.eqz - if ;; label = @3 - local.get $17 - if ;; label = @4 - block ;; label = @5 - i32.const 1 - local.set $0 - loop $label$308 ;; label = @6 - local.get $4 - local.get $0 - i32.const 2 - i32.shl - i32.add - i32.load - local.tee $1 - if ;; label = @7 - block ;; label = @8 - local.get $3 - local.get $0 - i32.const 3 - i32.shl - i32.add - local.get $1 - local.get $2 - call $22 - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.const 10 - i32.lt_s - br_if 2 (;@6;) - i32.const 1 - local.set $15 - br 6 (;@2;) - end - end - end - loop $label$310 ;; label = @6 - local.get $4 - local.get $0 - i32.const 2 - i32.shl - i32.add - i32.load - if ;; label = @7 - block ;; label = @8 - i32.const -1 - local.set $15 - br 6 (;@2;) - end - end - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.const 10 - i32.lt_s - br_if 0 (;@6;) - i32.const 1 - local.set $15 - end - end - else - i32.const 0 - local.set $15 - end - end - end - local.get $23 - global.set $global$1 - local.get $15 - end - ) - (func $20 (;33;) (type $2) (param $0 i32) (result i32) - i32.const 0 - ) - (func $21 (;34;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) - block $label$1 (result i32) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $2 - i32.const 16 - i32.add - local.tee $4 - i32.load - local.tee $3 - br_if 0 (;@3;) - local.get $2 - call $30 - if ;; label = @4 - i32.const 0 - local.set $3 - else - block ;; label = @5 - local.get $4 - i32.load - local.set $3 - br 2 (;@3;) - end - end - br 1 (;@2;) - end - local.get $3 - local.get $2 - i32.const 20 - i32.add - local.tee $5 - i32.load - local.tee $4 - i32.sub - local.get $1 - i32.lt_u - if ;; label = @3 - block ;; label = @4 - local.get $2 - local.get $0 - local.get $1 - local.get $2 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - local.set $3 - br 2 (;@2;) - end - end - block $label$7 (result i32) ;; label = @3 - local.get $2 - i32.load8_s offset=75 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @4 - block (result i32) ;; label = @5 - local.get $1 - local.set $3 - loop $label$9 ;; label = @6 - i32.const 0 - local.get $3 - i32.eqz - br_if 3 (;@3;) - drop - local.get $0 - local.get $3 - i32.const -1 - i32.add - local.tee $6 - i32.add - i32.load8_s - i32.const 10 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $6 - local.set $3 - br 2 (;@6;) - end - end - end - local.get $2 - local.get $0 - local.get $3 - local.get $2 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - local.get $3 - i32.lt_u - br_if 3 (;@2;) - local.get $5 - i32.load - local.set $4 - local.get $1 - local.get $3 - i32.sub - local.set $1 - local.get $0 - local.get $3 - i32.add - local.set $0 - local.get $3 - end - else - i32.const 0 - end - end - local.set $2 - local.get $4 - local.get $0 - local.get $1 - call $47 - drop - local.get $5 - local.get $5 - i32.load - local.get $1 - i32.add - i32.store - local.get $2 - local.get $1 - i32.add - local.set $3 - end - local.get $3 - end - ) - (func $22 (;35;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) (local $4 i64) (local $5 f64) - block $label$1 ;; label = @1 - local.get $1 - i32.const 20 - i32.le_u - if ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - block $label$6 ;; label = @6 - block $label$7 ;; label = @7 - block $label$8 ;; label = @8 - block $label$9 ;; label = @9 - block $label$10 ;; label = @10 - block $label$11 ;; label = @11 - block $label$12 ;; label = @12 - block $label$13 ;; label = @13 - local.get $1 - i32.const 9 - i32.sub - br_table 0 (;@13;) 1 (;@12;) 2 (;@11;) 3 (;@10;) 4 (;@9;) 5 (;@8;) 6 (;@7;) 7 (;@6;) 8 (;@5;) 9 (;@4;) 10 (;@3;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.store - br 11 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i64.extend_i32_s - i64.store - br 10 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i64.extend_i32_u - i64.store - br 9 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - i64.load - local.set $4 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $4 - i64.store - br 8 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 65535 - i32.and - i32.const 16 - i32.shl - i32.const 16 - i32.shr_s - i64.extend_i32_s - i64.store - br 7 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 65535 - i32.and - i64.extend_i32_u - i64.store - br 6 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i64.extend_i32_s - i64.store - br 5 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 255 - i32.and - i64.extend_i32_u - i64.store - br 4 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - f64.load - local.set $5 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $5 - f64.store - br 3 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - f64.load - local.set $5 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $5 - f64.store - end - end - end - ) - (func $23 (;36;) (type $9) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i64) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.wrap_i64 - local.set $2 - local.get $0 - i64.const 4294967295 - i64.gt_u - if ;; label = @2 - block ;; label = @3 - loop $label$3 ;; label = @4 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $0 - i64.const 10 - i64.rem_u - i64.const 48 - i64.or - i64.store8 - local.get $0 - i64.const 10 - i64.div_u - local.set $4 - local.get $0 - i64.const 42949672959 - i64.gt_u - if ;; label = @5 - block ;; label = @6 - local.get $4 - local.set $0 - br 2 (;@4;) - end - end - end - local.get $4 - i32.wrap_i64 - local.set $2 - end - end - local.get $2 - if ;; label = @2 - loop $label$6 ;; label = @3 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $2 - i32.const 10 - i32.rem_u - i32.const 48 - i32.or - i32.store8 - local.get $2 - i32.const 10 - i32.div_u - local.set $3 - local.get $2 - i32.const 10 - i32.ge_u - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $2 - br 2 (;@3;) - end - end - end - end - local.get $1 - end - ) - (func $24 (;37;) (type $2) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - i32.const 0 - local.set $1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - loop $label$5 ;; label = @5 - local.get $1 - i32.const 2233 - i32.add - i32.load8_u - local.get $0 - i32.eq - br_if 1 (;@4;) - local.get $1 - i32.const 1 - i32.add - local.tee $1 - i32.const 87 - i32.ne - br_if 0 (;@5;) - i32.const 87 - local.set $1 - i32.const 2321 - local.set $0 - br 2 (;@3;) - end - end - local.get $1 - if ;; label = @4 - block ;; label = @5 - i32.const 2321 - local.set $0 - br 2 (;@3;) - end - else - i32.const 2321 - local.set $0 - end - br 1 (;@2;) - end - loop $label$8 ;; label = @3 - local.get $0 - local.set $2 - loop $label$9 ;; label = @4 - local.get $2 - i32.const 1 - i32.add - local.set $0 - local.get $2 - i32.load8_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.set $2 - br 2 (;@4;) - end - end - end - local.get $1 - i32.const -1 - i32.add - local.tee $1 - br_if 0 (;@3;) - end - end - local.get $0 - end - ) - (func $25 (;38;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) - (local $5 i32) (local $6 i32) (local $7 i32) - block $label$1 ;; label = @1 - global.get $global$1 - local.set $7 - global.get $global$1 - i32.const 256 - i32.add - global.set $global$1 - local.get $7 - local.set $6 - block $label$2 ;; label = @2 - local.get $2 - local.get $3 - i32.gt_s - local.get $4 - i32.const 73728 - i32.and - i32.eqz - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $6 - local.get $1 - local.get $2 - local.get $3 - i32.sub - local.tee $5 - i32.const 256 - i32.gt_u - if (result i32) ;; label = @5 - i32.const 256 - else - local.get $5 - end - call $46 - drop - local.get $0 - i32.load - local.tee $1 - i32.const 32 - i32.and - i32.eqz - local.set $4 - local.get $5 - i32.const 255 - i32.gt_u - if ;; label = @5 - block ;; label = @6 - loop $label$7 ;; label = @7 - local.get $4 - if ;; label = @8 - block ;; label = @9 - local.get $6 - i32.const 256 - local.get $0 - call $21 - drop - local.get $0 - i32.load - local.set $1 - end - end - local.get $1 - i32.const 32 - i32.and - i32.eqz - local.set $4 - local.get $5 - i32.const -256 - i32.add - local.tee $5 - i32.const 255 - i32.gt_u - br_if 0 (;@7;) - end - local.get $4 - i32.eqz - br_if 4 (;@2;) - local.get $2 - local.get $3 - i32.sub - i32.const 255 - i32.and - local.set $5 - end - else - local.get $4 - i32.eqz - br_if 3 (;@2;) - end - local.get $6 - local.get $5 - local.get $0 - call $21 - drop - end - end - end - local.get $7 - global.set $global$1 - end - ) - (func $26 (;39;) (type $6) (param $0 i32) (param $1 i32) (result i32) - local.get $0 - if (result i32) ;; label = @1 - local.get $0 - local.get $1 - i32.const 0 - call $29 - else - i32.const 0 - end - ) - (func $27 (;40;) (type $11) (param $0 f64) (param $1 i32) (result f64) - local.get $0 - local.get $1 - call $28 - ) - (func $28 (;41;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (local $2 i64) (local $3 i64) - block $label$1 (result f64) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - local.get $0 - i64.reinterpret_f64 - local.tee $2 - i64.const 52 - i64.shr_u - local.tee $3 - i32.wrap_i64 - i32.const 65535 - i32.and - i32.const 2047 - i32.and - i32.const 16 - i32.shl - i32.const 16 - i32.shr_s - i32.const 0 - i32.sub - br_table 0 (;@5;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 1 (;@4;) 2 (;@3;) - end - local.get $1 - local.get $0 - f64.const 0x0p+0 (;=0;) - f64.ne - if (result i32) ;; label = @5 - block (result i32) ;; label = @6 - local.get $0 - f64.const 0x1p+64 (;=18446744073709552000;) - f64.mul - local.get $1 - call $28 - local.set $0 - local.get $1 - i32.load - i32.const -64 - i32.add - end - else - i32.const 0 - end - i32.store - br 2 (;@2;) - end - br 1 (;@2;) - end - local.get $1 - local.get $3 - i32.wrap_i64 - i32.const 2047 - i32.and - i32.const -1022 - i32.add - i32.store - local.get $2 - i64.const -9218868437227405313 - i64.and - i64.const 4602678819172646912 - i64.or - f64.reinterpret_i64 - local.set $0 - end - local.get $0 - end - ) - (func $29 (;42;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $1 - i32.const 128 - i32.lt_u - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.store8 - i32.const 1 - br 4 (;@1;) - end - end - local.get $1 - i32.const 2048 - i32.lt_u - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 192 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - i32.const 2 - br 4 (;@1;) - end - end - local.get $1 - i32.const 55296 - i32.lt_u - local.get $1 - i32.const -8192 - i32.and - i32.const 57344 - i32.eq - i32.or - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.const 12 - i32.shr_u - i32.const 224 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=2 - i32.const 3 - br 4 (;@1;) - end - end - local.get $1 - i32.const -65536 - i32.add - i32.const 1048576 - i32.lt_u - if (result i32) ;; label = @4 - block (result i32) ;; label = @5 - local.get $0 - local.get $1 - i32.const 18 - i32.shr_u - i32.const 240 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 12 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=2 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=3 - i32.const 4 - end - else - block (result i32) ;; label = @5 - call $12 - i32.const 84 - i32.store - i32.const -1 - end - end - end - else - i32.const 1 - end - end - ) - (func $30 (;43;) (type $2) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.const 74 - i32.add - local.tee $2 - i32.load8_s - local.set $1 - local.get $2 - local.get $1 - i32.const 255 - i32.add - local.get $1 - i32.or - i32.store8 - local.get $0 - i32.load - local.tee $1 - i32.const 8 - i32.and - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $0 - local.get $1 - i32.const 32 - i32.or - i32.store - i32.const -1 - end - else - block (result i32) ;; label = @3 - local.get $0 - i32.const 0 - i32.store offset=8 - local.get $0 - i32.const 0 - i32.store offset=4 - local.get $0 - local.get $0 - i32.load offset=44 - local.tee $1 - i32.store offset=28 - local.get $0 - local.get $1 - i32.store offset=20 - local.get $0 - local.get $1 - local.get $0 - i32.load offset=48 - i32.add - i32.store offset=16 - i32.const 0 - end - end - local.tee $0 - end - ) - (func $31 (;44;) (type $2) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) - block $label$1 (result i32) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $0 - local.tee $2 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get $2 - local.set $1 - loop $label$4 ;; label = @4 - local.get $0 - i32.load8_s - i32.eqz - if ;; label = @5 - block ;; label = @6 - local.get $1 - local.set $0 - br 4 (;@2;) - end - end - local.get $0 - i32.const 1 - i32.add - local.tee $0 - local.tee $1 - i32.const 3 - i32.and - br_if 0 (;@4;) - br 1 (;@3;) - end - end - loop $label$6 ;; label = @3 - local.get $0 - i32.const 4 - i32.add - local.set $1 - local.get $0 - i32.load - local.tee $3 - i32.const -2139062144 - i32.and - i32.const -2139062144 - i32.xor - local.get $3 - i32.const -16843009 - i32.add - i32.and - i32.eqz - if ;; label = @4 - block ;; label = @5 - local.get $1 - local.set $0 - br 2 (;@3;) - end - end - end - local.get $3 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - if ;; label = @3 - loop $label$9 ;; label = @4 - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.load8_s - br_if 0 (;@4;) - end - end - end - local.get $0 - local.get $2 - i32.sub - end - ) - (func $32 (;45;) (type $6) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $3 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $3 - local.tee $4 - local.get $1 - i32.const 255 - i32.and - local.tee $7 - i32.store8 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $0 - i32.const 16 - i32.add - local.tee $2 - i32.load - local.tee $5 - br_if 0 (;@3;) - local.get $0 - call $30 - if ;; label = @4 - i32.const -1 - local.set $1 - else - block ;; label = @5 - local.get $2 - i32.load - local.set $5 - br 2 (;@3;) - end - end - br 1 (;@2;) - end - local.get $0 - i32.const 20 - i32.add - local.tee $2 - i32.load - local.tee $6 - local.get $5 - i32.lt_u - if ;; label = @3 - local.get $1 - i32.const 255 - i32.and - local.tee $1 - local.get $0 - i32.load8_s offset=75 - i32.ne - if ;; label = @4 - block ;; label = @5 - local.get $2 - local.get $6 - i32.const 1 - i32.add - i32.store - local.get $6 - local.get $7 - i32.store8 - br 3 (;@2;) - end - end - end - local.get $0 - local.get $4 - i32.const 1 - local.get $0 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - i32.const 1 - i32.eq - if (result i32) ;; label = @3 - local.get $4 - i32.load8_u - else - i32.const -1 - end - local.set $1 - end - local.get $3 - global.set $global$1 - local.get $1 - end - ) - (func $33 (;46;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $2 - local.get $1 - i32.mul - local.set $4 - local.get $3 - i32.load offset=76 - i32.const -1 - i32.gt_s - if ;; label = @2 - block ;; label = @3 - local.get $3 - call $20 - i32.eqz - local.set $5 - local.get $0 - local.get $4 - local.get $3 - call $21 - local.set $0 - local.get $5 - i32.eqz - if ;; label = @4 - local.get $3 - call $13 - end - end - else - local.get $0 - local.get $4 - local.get $3 - call $21 - local.set $0 - end - local.get $0 - local.get $4 - i32.ne - if ;; label = @2 - local.get $0 - local.get $1 - i32.div_u - local.set $2 - end - local.get $2 - end - ) - (func $34 (;47;) (type $6) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $2 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $2 - local.tee $3 - local.get $1 - i32.store - i32.const 1280 - i32.load - local.get $0 - local.get $3 - call $18 - local.set $0 - local.get $2 - global.set $global$1 - local.get $0 - end - ) - (func $35 (;48;) (type $2) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) - block $label$1 (result i32) ;; label = @1 - i32.const 1280 - i32.load - local.tee $1 - i32.load offset=76 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @2 - local.get $1 - call $20 - else - i32.const 0 - end - local.set $2 - block $label$4 (result i32) ;; label = @2 - local.get $0 - local.get $1 - call $36 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @3 - i32.const 1 - else - block (result i32) ;; label = @4 - local.get $1 - i32.load8_s offset=75 - i32.const 10 - i32.ne - if ;; label = @5 - local.get $1 - i32.const 20 - i32.add - local.tee $3 - i32.load - local.tee $0 - local.get $1 - i32.load offset=16 - i32.lt_u - if ;; label = @6 - block ;; label = @7 - local.get $3 - local.get $0 - i32.const 1 - i32.add - i32.store - local.get $0 - i32.const 10 - i32.store8 - i32.const 0 - br 5 (;@2;) - end - end - end - local.get $1 - i32.const 10 - call $32 - i32.const 0 - i32.lt_s - end - end - end - local.set $0 - local.get $2 - if ;; label = @2 - local.get $1 - call $13 - end - local.get $0 - i32.const 31 - i32.shl - i32.const 31 - i32.shr_s - end - ) - (func $36 (;49;) (type $6) (param $0 i32) (param $1 i32) (result i32) - local.get $0 - local.get $0 - call $31 - i32.const 1 - local.get $1 - call $33 - i32.const -1 - i32.add - ) - (func $37 (;50;) (type $2) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $14 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $14 - local.set $18 - block $label$2 ;; label = @2 - local.get $0 - i32.const 245 - i32.lt_u - if ;; label = @3 - block ;; label = @4 - local.get $0 - i32.const 11 - i32.add - i32.const -8 - i32.and - local.set $3 - i32.const 4176 - i32.load - local.tee $8 - local.get $0 - i32.const 11 - i32.lt_u - if (result i32) ;; label = @5 - i32.const 16 - local.tee $3 - else - local.get $3 - end - i32.const 3 - i32.shr_u - local.tee $2 - i32.shr_u - local.tee $0 - i32.const 3 - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $0 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $2 - i32.add - local.tee $5 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.tee $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $7 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.set $4 - local.get $2 - local.get $4 - i32.eq - if ;; label = @7 - i32.const 4176 - local.get $8 - i32.const 1 - local.get $5 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - else - block ;; label = @8 - local.get $4 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$8 - end - local.get $4 - i32.const 12 - i32.add - local.tee $0 - i32.load - local.get $7 - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $0 - local.get $2 - i32.store - local.get $3 - local.get $4 - i32.store - end - else - call $fimport$8 - end - end - end - local.get $7 - local.get $5 - i32.const 3 - i32.shl - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $7 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - local.get $14 - global.set $global$1 - local.get $1 - return - end - end - local.get $3 - i32.const 4184 - i32.load - local.tee $16 - i32.gt_u - if ;; label = @5 - block ;; label = @6 - local.get $0 - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.get $2 - i32.shl - i32.const 2 - local.get $2 - i32.shl - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.or - i32.and - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $5 - local.get $0 - local.get $5 - i32.shr_u - local.tee $2 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $5 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - i32.add - local.tee $11 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.tee $4 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $9 - i32.const 8 - i32.add - local.tee $5 - i32.load - local.set $12 - local.get $4 - local.get $12 - i32.eq - if ;; label = @9 - i32.const 4176 - local.get $8 - i32.const 1 - local.get $11 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $7 - i32.store - else - block ;; label = @10 - local.get $12 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$8 - end - local.get $12 - i32.const 12 - i32.add - local.tee $0 - i32.load - local.get $9 - i32.eq - if ;; label = @11 - block ;; label = @12 - local.get $0 - local.get $4 - i32.store - local.get $2 - local.get $12 - i32.store - local.get $8 - local.set $7 - end - else - call $fimport$8 - end - end - end - local.get $9 - local.get $3 - i32.const 3 - i32.or - i32.store offset=4 - local.get $9 - local.get $3 - i32.add - local.tee $4 - local.get $11 - i32.const 3 - i32.shl - local.get $3 - i32.sub - local.tee $11 - i32.const 1 - i32.or - i32.store offset=4 - local.get $4 - local.get $11 - i32.add - local.get $11 - i32.store - local.get $16 - if ;; label = @9 - block ;; label = @10 - i32.const 4196 - i32.load - local.set $9 - local.get $16 - i32.const 3 - i32.shr_u - local.tee $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.set $2 - local.get $7 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @11 - local.get $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $0 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @12 - call $fimport$8 - else - block ;; label = @13 - local.get $3 - local.set $6 - local.get $0 - local.set $1 - end - end - else - block ;; label = @12 - i32.const 4176 - local.get $7 - local.get $0 - i32.or - i32.store - local.get $2 - i32.const 8 - i32.add - local.set $6 - local.get $2 - local.set $1 - end - end - local.get $6 - local.get $9 - i32.store - local.get $1 - local.get $9 - i32.store offset=12 - local.get $9 - local.get $1 - i32.store offset=8 - local.get $9 - local.get $2 - i32.store offset=12 - end - end - i32.const 4184 - local.get $11 - i32.store - i32.const 4196 - local.get $4 - i32.store - local.get $14 - global.set $global$1 - local.get $5 - return - end - end - i32.const 4180 - i32.load - local.tee $6 - if ;; label = @7 - block ;; label = @8 - local.get $6 - i32.const 0 - local.get $6 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $2 - local.get $0 - local.get $2 - i32.shr_u - local.tee $1 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $2 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - i32.add - i32.const 2 - i32.shl - i32.const 4480 - i32.add - i32.load - local.tee $2 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.sub - local.set $9 - local.get $2 - local.set $1 - loop $label$25 ;; label = @9 - block $label$26 ;; label = @10 - local.get $1 - i32.load offset=16 - local.tee $0 - i32.eqz - if ;; label = @11 - local.get $1 - i32.load offset=20 - local.tee $0 - i32.eqz - br_if 1 (;@10;) - end - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.sub - local.tee $1 - local.get $9 - i32.lt_u - local.tee $7 - if ;; label = @11 - local.get $1 - local.set $9 - end - local.get $0 - local.set $1 - local.get $7 - if ;; label = @11 - local.get $0 - local.set $2 - end - br 1 (;@9;) - end - end - local.get $2 - i32.const 4192 - i32.load - local.tee $12 - i32.lt_u - if ;; label = @9 - call $fimport$8 - end - local.get $2 - local.get $2 - local.get $3 - i32.add - local.tee $13 - i32.ge_u - if ;; label = @9 - call $fimport$8 - end - local.get $2 - i32.load offset=24 - local.set $15 - block $label$32 ;; label = @9 - local.get $2 - i32.load offset=12 - local.tee $0 - local.get $2 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $2 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @12 - local.get $2 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @13 - block ;; label = @14 - i32.const 0 - local.set $4 - br 5 (;@9;) - end - end - end - loop $label$36 ;; label = @12 - local.get $0 - i32.const 20 - i32.add - local.tee $11 - i32.load - local.tee $7 - if ;; label = @13 - block ;; label = @14 - local.get $7 - local.set $0 - local.get $11 - local.set $1 - br 2 (;@12;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $11 - i32.load - local.tee $7 - if ;; label = @13 - block ;; label = @14 - local.get $7 - local.set $0 - local.get $11 - local.set $1 - br 2 (;@12;) - end - end - end - local.get $1 - local.get $12 - i32.lt_u - if ;; label = @12 - call $fimport$8 - else - block ;; label = @13 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $4 - end - end - end - else - block ;; label = @11 - local.get $2 - i32.load offset=8 - local.tee $11 - local.get $12 - i32.lt_u - if ;; label = @12 - call $fimport$8 - end - local.get $11 - i32.const 12 - i32.add - local.tee $7 - i32.load - local.get $2 - i32.ne - if ;; label = @12 - call $fimport$8 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $2 - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $7 - local.get $0 - i32.store - local.get $1 - local.get $11 - i32.store - local.get $0 - local.set $4 - end - else - call $fimport$8 - end - end - end - end - block $label$46 ;; label = @9 - local.get $15 - if ;; label = @10 - block ;; label = @11 - local.get $2 - local.get $2 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $0 - local.get $4 - i32.store - local.get $4 - i32.eqz - if ;; label = @14 - block ;; label = @15 - i32.const 4180 - local.get $6 - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 6 (;@9;) - end - end - end - else - block ;; label = @13 - local.get $15 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$8 - end - local.get $15 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $2 - i32.eq - if ;; label = @14 - local.get $0 - local.get $4 - i32.store - else - local.get $15 - local.get $4 - i32.store offset=20 - end - local.get $4 - i32.eqz - br_if 4 (;@9;) - end - end - local.get $4 - i32.const 4192 - i32.load - local.tee $0 - i32.lt_u - if ;; label = @12 - call $fimport$8 - end - local.get $4 - local.get $15 - i32.store offset=24 - local.get $2 - i32.load offset=16 - local.tee $1 - if ;; label = @12 - local.get $1 - local.get $0 - i32.lt_u - if ;; label = @13 - call $fimport$8 - else - block ;; label = @14 - local.get $4 - local.get $1 - i32.store offset=16 - local.get $1 - local.get $4 - i32.store offset=24 - end - end - end - local.get $2 - i32.load offset=20 - local.tee $0 - if ;; label = @12 - local.get $0 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @13 - call $fimport$8 - else - block ;; label = @14 - local.get $4 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $4 - i32.store offset=24 - end - end - end - end - end - end - local.get $9 - i32.const 16 - i32.lt_u - if ;; label = @9 - block ;; label = @10 - local.get $2 - local.get $9 - local.get $3 - i32.add - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - else - block ;; label = @10 - local.get $2 - local.get $3 - i32.const 3 - i32.or - i32.store offset=4 - local.get $13 - local.get $9 - i32.const 1 - i32.or - i32.store offset=4 - local.get $13 - local.get $9 - i32.add - local.get $9 - i32.store - local.get $16 - if ;; label = @11 - block ;; label = @12 - i32.const 4196 - i32.load - local.set $7 - local.get $16 - i32.const 3 - i32.shr_u - local.tee $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.set $3 - local.get $8 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @13 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$8 - else - block ;; label = @15 - local.get $1 - local.set $10 - local.get $0 - local.set $5 - end - end - else - block ;; label = @14 - i32.const 4176 - local.get $8 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $10 - local.get $3 - local.set $5 - end - end - local.get $10 - local.get $7 - i32.store - local.get $5 - local.get $7 - i32.store offset=12 - local.get $7 - local.get $5 - i32.store offset=8 - local.get $7 - local.get $3 - i32.store offset=12 - end - end - i32.const 4184 - local.get $9 - i32.store - i32.const 4196 - local.get $13 - i32.store - end - end - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - else - local.get $3 - local.set $0 - end - end - else - local.get $3 - local.set $0 - end - end - else - local.get $0 - i32.const -65 - i32.gt_u - if ;; label = @4 - i32.const -1 - local.set $0 - else - block ;; label = @5 - local.get $0 - i32.const 11 - i32.add - local.tee $0 - i32.const -8 - i32.and - local.set $7 - i32.const 4180 - i32.load - local.tee $5 - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @8 - local.get $7 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @9 - i32.const 31 - else - local.get $7 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $3 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $3 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.set $17 - i32.const 0 - local.get $7 - i32.sub - local.set $3 - block $label$78 ;; label = @8 - block $label$79 ;; label = @9 - block $label$80 ;; label = @10 - local.get $17 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - i32.load - local.tee $1 - if ;; label = @11 - block ;; label = @12 - i32.const 25 - local.get $17 - i32.const 1 - i32.shr_u - i32.sub - local.set $0 - i32.const 0 - local.set $4 - local.get $7 - local.get $17 - i32.const 31 - i32.eq - if (result i32) ;; label = @13 - i32.const 0 - else - local.get $0 - end - i32.shl - local.set $10 - i32.const 0 - local.set $0 - loop $label$84 ;; label = @13 - local.get $1 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.sub - local.tee $6 - local.get $3 - i32.lt_u - if ;; label = @14 - local.get $6 - if ;; label = @15 - block ;; label = @16 - local.get $6 - local.set $3 - local.get $1 - local.set $0 - end - else - block ;; label = @16 - i32.const 0 - local.set $3 - local.get $1 - local.set $0 - br 7 (;@9;) - end - end - end - local.get $1 - i32.load offset=20 - local.tee $19 - i32.eqz - local.get $19 - local.get $1 - i32.const 16 - i32.add - local.get $10 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - i32.load - local.tee $6 - i32.eq - i32.or - if (result i32) ;; label = @14 - local.get $4 - else - local.get $19 - end - local.set $1 - local.get $10 - local.get $6 - i32.eqz - local.tee $4 - i32.const 1 - i32.and - i32.const 1 - i32.xor - i32.shl - local.set $10 - local.get $4 - if ;; label = @14 - block ;; label = @15 - local.get $1 - local.set $4 - local.get $0 - local.set $1 - br 5 (;@10;) - end - else - block ;; label = @15 - local.get $1 - local.set $4 - local.get $6 - local.set $1 - br 2 (;@13;) - end - end - end - end - else - block ;; label = @12 - i32.const 0 - local.set $4 - i32.const 0 - local.set $1 - end - end - end - local.get $4 - i32.eqz - local.get $1 - i32.eqz - i32.and - if (result i32) ;; label = @10 - block (result i32) ;; label = @11 - local.get $5 - i32.const 2 - local.get $17 - i32.shl - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.or - i32.and - local.tee $0 - i32.eqz - if ;; label = @12 - block ;; label = @13 - local.get $7 - local.set $0 - br 11 (;@2;) - end - end - local.get $0 - i32.const 0 - local.get $0 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $10 - local.get $0 - local.get $10 - i32.shr_u - local.tee $4 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $10 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - i32.add - i32.const 2 - i32.shl - i32.const 4480 - i32.add - i32.load - end - else - local.get $4 - end - local.tee $0 - br_if 0 (;@9;) - local.get $1 - local.set $4 - br 1 (;@8;) - end - loop $label$96 ;; label = @9 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.sub - local.tee $4 - local.get $3 - i32.lt_u - local.tee $10 - if ;; label = @10 - local.get $4 - local.set $3 - end - local.get $10 - if ;; label = @10 - local.get $0 - local.set $1 - end - local.get $0 - i32.load offset=16 - local.tee $4 - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.set $0 - br 2 (;@9;) - end - end - local.get $0 - i32.load offset=20 - local.tee $0 - br_if 0 (;@9;) - local.get $1 - local.set $4 - end - end - local.get $4 - if ;; label = @8 - local.get $3 - i32.const 4184 - i32.load - local.get $7 - i32.sub - i32.lt_u - if ;; label = @9 - block ;; label = @10 - local.get $4 - i32.const 4192 - i32.load - local.tee $12 - i32.lt_u - if ;; label = @11 - call $fimport$8 - end - local.get $4 - local.get $4 - local.get $7 - i32.add - local.tee $6 - i32.ge_u - if ;; label = @11 - call $fimport$8 - end - local.get $4 - i32.load offset=24 - local.set $10 - block $label$104 ;; label = @11 - local.get $4 - i32.load offset=12 - local.tee $0 - local.get $4 - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $4 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @14 - local.get $4 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @15 - block ;; label = @16 - i32.const 0 - local.set $13 - br 5 (;@11;) - end - end - end - loop $label$108 ;; label = @14 - local.get $0 - i32.const 20 - i32.add - local.tee $9 - i32.load - local.tee $11 - if ;; label = @15 - block ;; label = @16 - local.get $11 - local.set $0 - local.get $9 - local.set $1 - br 2 (;@14;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $9 - i32.load - local.tee $11 - if ;; label = @15 - block ;; label = @16 - local.get $11 - local.set $0 - local.get $9 - local.set $1 - br 2 (;@14;) - end - end - end - local.get $1 - local.get $12 - i32.lt_u - if ;; label = @14 - call $fimport$8 - else - block ;; label = @15 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $13 - end - end - end - else - block ;; label = @13 - local.get $4 - i32.load offset=8 - local.tee $9 - local.get $12 - i32.lt_u - if ;; label = @14 - call $fimport$8 - end - local.get $9 - i32.const 12 - i32.add - local.tee $11 - i32.load - local.get $4 - i32.ne - if ;; label = @14 - call $fimport$8 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $4 - i32.eq - if ;; label = @14 - block ;; label = @15 - local.get $11 - local.get $0 - i32.store - local.get $1 - local.get $9 - i32.store - local.get $0 - local.set $13 - end - else - call $fimport$8 - end - end - end - end - block $label$118 ;; label = @11 - local.get $10 - if ;; label = @12 - block ;; label = @13 - local.get $4 - local.get $4 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @14 - block ;; label = @15 - local.get $0 - local.get $13 - i32.store - local.get $13 - i32.eqz - if ;; label = @16 - block ;; label = @17 - i32.const 4180 - local.get $5 - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $2 - i32.store - br 6 (;@11;) - end - end - end - else - block ;; label = @15 - local.get $10 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @16 - call $fimport$8 - end - local.get $10 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @16 - local.get $0 - local.get $13 - i32.store - else - local.get $10 - local.get $13 - i32.store offset=20 - end - local.get $13 - i32.eqz - if ;; label = @16 - block ;; label = @17 - local.get $5 - local.set $2 - br 6 (;@11;) - end - end - end - end - local.get $13 - i32.const 4192 - i32.load - local.tee $0 - i32.lt_u - if ;; label = @14 - call $fimport$8 - end - local.get $13 - local.get $10 - i32.store offset=24 - local.get $4 - i32.load offset=16 - local.tee $1 - if ;; label = @14 - local.get $1 - local.get $0 - i32.lt_u - if ;; label = @15 - call $fimport$8 - else - block ;; label = @16 - local.get $13 - local.get $1 - i32.store offset=16 - local.get $1 - local.get $13 - i32.store offset=24 - end - end - end - local.get $4 - i32.load offset=20 - local.tee $0 - if ;; label = @14 - local.get $0 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @15 - call $fimport$8 - else - block ;; label = @16 - local.get $13 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $13 - i32.store offset=24 - local.get $5 - local.set $2 - end - end - else - local.get $5 - local.set $2 - end - end - else - local.get $5 - local.set $2 - end - end - block $label$136 ;; label = @11 - local.get $3 - i32.const 16 - i32.lt_u - if ;; label = @12 - block ;; label = @13 - local.get $4 - local.get $3 - local.get $7 - i32.add - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $4 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - else - block ;; label = @13 - local.get $4 - local.get $7 - i32.const 3 - i32.or - i32.store offset=4 - local.get $6 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $3 - i32.add - local.get $3 - i32.store - local.get $3 - i32.const 3 - i32.shr_u - local.set $0 - local.get $3 - i32.const 256 - i32.lt_u - if ;; label = @14 - block ;; label = @15 - local.get $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.set $3 - i32.const 4176 - i32.load - local.tee $1 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @16 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @17 - call $fimport$8 - else - block ;; label = @18 - local.get $1 - local.set $16 - local.get $0 - local.set $8 - end - end - else - block ;; label = @17 - i32.const 4176 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $16 - local.get $3 - local.set $8 - end - end - local.get $16 - local.get $6 - i32.store - local.get $8 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $8 - i32.store offset=8 - local.get $6 - local.get $3 - i32.store offset=12 - br 4 (;@11;) - end - end - local.get $3 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @14 - local.get $3 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @15 - i32.const 31 - else - local.get $3 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $5 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $5 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $5 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - local.set $1 - local.get $6 - local.get $5 - i32.store offset=28 - local.get $6 - i32.const 16 - i32.add - local.tee $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store - local.get $2 - i32.const 1 - local.get $5 - i32.shl - local.tee $0 - i32.and - i32.eqz - if ;; label = @14 - block ;; label = @15 - i32.const 4180 - local.get $2 - local.get $0 - i32.or - i32.store - local.get $1 - local.get $6 - i32.store - local.get $6 - local.get $1 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 4 (;@11;) - end - end - local.get $1 - i32.load - local.set $0 - i32.const 25 - local.get $5 - i32.const 1 - i32.shr_u - i32.sub - local.set $1 - local.get $3 - local.get $5 - i32.const 31 - i32.eq - if (result i32) ;; label = @14 - i32.const 0 - else - local.get $1 - end - i32.shl - local.set $5 - block $label$151 ;; label = @14 - block $label$152 ;; label = @15 - block $label$153 ;; label = @16 - loop $label$154 ;; label = @17 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.eq - br_if 2 (;@15;) - local.get $5 - i32.const 1 - i32.shl - local.set $2 - local.get $0 - i32.const 16 - i32.add - local.get $5 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $5 - i32.load - local.tee $1 - i32.eqz - br_if 1 (;@16;) - local.get $2 - local.set $5 - local.get $1 - local.set $0 - br 0 (;@17;) - end - end - local.get $5 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @16 - call $fimport$8 - else - block ;; label = @17 - local.get $5 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 6 (;@11;) - end - end - br 1 (;@14;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $2 - i32.const 4192 - i32.load - local.tee $1 - i32.ge_u - local.get $0 - local.get $1 - i32.ge_u - i32.and - if ;; label = @15 - block ;; label = @16 - local.get $2 - local.get $6 - i32.store offset=12 - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $2 - i32.store offset=8 - local.get $6 - local.get $0 - i32.store offset=12 - local.get $6 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$8 - end - end - end - end - end - local.get $14 - global.set $global$1 - local.get $4 - i32.const 8 - i32.add - return - end - else - local.get $7 - local.set $0 - end - else - local.get $7 - local.set $0 - end - end - else - local.get $7 - local.set $0 - end - end - end - end - end - i32.const 4184 - i32.load - local.tee $1 - local.get $0 - i32.ge_u - if ;; label = @2 - block ;; label = @3 - i32.const 4196 - i32.load - local.set $2 - local.get $1 - local.get $0 - i32.sub - local.tee $3 - i32.const 15 - i32.gt_u - if ;; label = @4 - block ;; label = @5 - i32.const 4196 - local.get $2 - local.get $0 - i32.add - local.tee $1 - i32.store - i32.const 4184 - local.get $3 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $1 - local.get $3 - i32.add - local.get $3 - i32.store - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - end - else - block ;; label = @5 - i32.const 4184 - i32.const 0 - i32.store - i32.const 4196 - i32.const 0 - i32.store - local.get $2 - local.get $1 - i32.const 3 - i32.or - i32.store offset=4 - local.get $2 - local.get $1 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - end - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - i32.const 4188 - i32.load - local.tee $10 - local.get $0 - i32.gt_u - if ;; label = @2 - block ;; label = @3 - i32.const 4188 - local.get $10 - local.get $0 - i32.sub - local.tee $3 - i32.store - i32.const 4200 - i32.const 4200 - i32.load - local.tee $2 - local.get $0 - i32.add - local.tee $1 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - i32.const 4648 - i32.load - if (result i32) ;; label = @2 - i32.const 4656 - i32.load - else - block (result i32) ;; label = @3 - i32.const 4656 - i32.const 4096 - i32.store - i32.const 4652 - i32.const 4096 - i32.store - i32.const 4660 - i32.const -1 - i32.store - i32.const 4664 - i32.const -1 - i32.store - i32.const 4668 - i32.const 0 - i32.store - i32.const 4620 - i32.const 0 - i32.store - local.get $18 - local.get $18 - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - local.tee $1 - i32.store - i32.const 4648 - local.get $1 - i32.store - i32.const 4096 - end - end - local.tee $1 - local.get $0 - i32.const 47 - i32.add - local.tee $13 - i32.add - local.tee $8 - i32.const 0 - local.get $1 - i32.sub - local.tee $4 - i32.and - local.tee $6 - local.get $0 - i32.le_u - if ;; label = @2 - block ;; label = @3 - local.get $14 - global.set $global$1 - i32.const 0 - return - end - end - i32.const 4616 - i32.load - local.tee $2 - if ;; label = @2 - i32.const 4608 - i32.load - local.tee $3 - local.get $6 - i32.add - local.tee $1 - local.get $3 - i32.le_u - local.get $1 - local.get $2 - i32.gt_u - i32.or - if ;; label = @3 - block ;; label = @4 - local.get $14 - global.set $global$1 - i32.const 0 - return - end - end - end - local.get $0 - i32.const 48 - i32.add - local.set $7 - block $label$171 ;; label = @2 - block $label$172 ;; label = @3 - i32.const 4620 - i32.load - i32.const 4 - i32.and - i32.eqz - if ;; label = @4 - block ;; label = @5 - block $label$174 ;; label = @6 - block $label$175 ;; label = @7 - block $label$176 ;; label = @8 - i32.const 4200 - i32.load - local.tee $3 - i32.eqz - br_if 0 (;@8;) - i32.const 4624 - local.set $2 - loop $label$177 ;; label = @9 - block $label$178 ;; label = @10 - local.get $2 - i32.load - local.tee $1 - local.get $3 - i32.le_u - if ;; label = @11 - local.get $1 - local.get $2 - i32.const 4 - i32.add - local.tee $5 - i32.load - i32.add - local.get $3 - i32.gt_u - br_if 1 (;@10;) - end - local.get $2 - i32.load offset=8 - local.tee $1 - i32.eqz - br_if 2 (;@8;) - local.get $1 - local.set $2 - br 1 (;@9;) - end - end - local.get $8 - local.get $10 - i32.sub - local.get $4 - i32.and - local.tee $3 - i32.const 2147483647 - i32.lt_u - if ;; label = @9 - local.get $3 - call $45 - local.tee $1 - local.get $2 - i32.load - local.get $5 - i32.load - i32.add - i32.eq - if ;; label = @10 - local.get $1 - i32.const -1 - i32.ne - br_if 7 (;@3;) - else - block ;; label = @11 - local.get $1 - local.set $2 - local.get $3 - local.set $1 - br 4 (;@7;) - end - end - end - br 2 (;@6;) - end - i32.const 0 - call $45 - local.tee $1 - i32.const -1 - i32.ne - if ;; label = @8 - block ;; label = @9 - i32.const 4652 - i32.load - local.tee $2 - i32.const -1 - i32.add - local.tee $5 - local.get $1 - local.tee $3 - i32.add - i32.const 0 - local.get $2 - i32.sub - i32.and - local.get $3 - i32.sub - local.set $2 - local.get $5 - local.get $3 - i32.and - if (result i32) ;; label = @10 - local.get $2 - else - i32.const 0 - end - local.get $6 - i32.add - local.tee $3 - i32.const 4608 - i32.load - local.tee $5 - i32.add - local.set $4 - local.get $3 - local.get $0 - i32.gt_u - local.get $3 - i32.const 2147483647 - i32.lt_u - i32.and - if ;; label = @10 - block ;; label = @11 - i32.const 4616 - i32.load - local.tee $2 - if ;; label = @12 - local.get $4 - local.get $5 - i32.le_u - local.get $4 - local.get $2 - i32.gt_u - i32.or - br_if 6 (;@6;) - end - local.get $3 - call $45 - local.tee $2 - local.get $1 - i32.eq - br_if 8 (;@3;) - local.get $3 - local.set $1 - br 4 (;@7;) - end - end - end - end - br 1 (;@6;) - end - i32.const 0 - local.get $1 - i32.sub - local.set $5 - local.get $7 - local.get $1 - i32.gt_u - local.get $1 - i32.const 2147483647 - i32.lt_u - local.get $2 - i32.const -1 - i32.ne - i32.and - i32.and - if ;; label = @7 - local.get $13 - local.get $1 - i32.sub - i32.const 4656 - i32.load - local.tee $3 - i32.add - i32.const 0 - local.get $3 - i32.sub - i32.and - local.tee $3 - i32.const 2147483647 - i32.lt_u - if ;; label = @8 - local.get $3 - call $45 - i32.const -1 - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $5 - call $45 - drop - br 4 (;@6;) - end - else - local.get $3 - local.get $1 - i32.add - local.set $3 - end - else - local.get $1 - local.set $3 - end - else - local.get $1 - local.set $3 - end - local.get $2 - i32.const -1 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $2 - local.set $1 - br 5 (;@3;) - end - end - end - i32.const 4620 - i32.const 4620 - i32.load - i32.const 4 - i32.or - i32.store - end - end - local.get $6 - i32.const 2147483647 - i32.lt_u - if ;; label = @4 - local.get $6 - call $45 - local.tee $1 - i32.const 0 - call $45 - local.tee $3 - i32.lt_u - local.get $1 - i32.const -1 - i32.ne - local.get $3 - i32.const -1 - i32.ne - i32.and - i32.and - if ;; label = @5 - local.get $3 - local.get $1 - i32.sub - local.tee $3 - local.get $0 - i32.const 40 - i32.add - i32.gt_u - br_if 2 (;@3;) - end - end - br 1 (;@2;) - end - i32.const 4608 - i32.const 4608 - i32.load - local.get $3 - i32.add - local.tee $2 - i32.store - local.get $2 - i32.const 4612 - i32.load - i32.gt_u - if ;; label = @3 - i32.const 4612 - local.get $2 - i32.store - end - block $label$198 ;; label = @3 - i32.const 4200 - i32.load - local.tee $8 - if ;; label = @4 - block ;; label = @5 - i32.const 4624 - local.set $2 - block $label$200 ;; label = @6 - block $label$201 ;; label = @7 - loop $label$202 ;; label = @8 - local.get $1 - local.get $2 - i32.load - local.tee $4 - local.get $2 - i32.const 4 - i32.add - local.tee $7 - i32.load - local.tee $5 - i32.add - i32.eq - br_if 1 (;@7;) - local.get $2 - i32.load offset=8 - local.tee $2 - br_if 0 (;@8;) - end - br 1 (;@6;) - end - local.get $2 - i32.load offset=12 - i32.const 8 - i32.and - i32.eqz - if ;; label = @7 - local.get $8 - local.get $1 - i32.lt_u - local.get $8 - local.get $4 - i32.ge_u - i32.and - if ;; label = @8 - block ;; label = @9 - local.get $7 - local.get $5 - local.get $3 - i32.add - i32.store - i32.const 4188 - i32.load - local.set $5 - i32.const 0 - local.get $8 - i32.const 8 - i32.add - local.tee $2 - i32.sub - i32.const 7 - i32.and - local.set $1 - i32.const 4200 - local.get $8 - local.get $2 - i32.const 7 - i32.and - if (result i32) ;; label = @10 - local.get $1 - else - i32.const 0 - local.tee $1 - end - i32.add - local.tee $2 - i32.store - i32.const 4188 - local.get $3 - local.get $1 - i32.sub - local.get $5 - i32.add - local.tee $1 - i32.store - local.get $2 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $1 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 4204 - i32.const 4664 - i32.load - i32.store - br 6 (;@3;) - end - end - end - end - local.get $1 - i32.const 4192 - i32.load - local.tee $2 - i32.lt_u - if ;; label = @6 - block ;; label = @7 - i32.const 4192 - local.get $1 - i32.store - local.get $1 - local.set $2 - end - end - local.get $1 - local.get $3 - i32.add - local.set $10 - i32.const 4624 - local.set $5 - block $label$208 ;; label = @6 - block $label$209 ;; label = @7 - loop $label$210 ;; label = @8 - local.get $5 - i32.load - local.get $10 - i32.eq - br_if 1 (;@7;) - local.get $5 - i32.load offset=8 - local.tee $5 - br_if 0 (;@8;) - i32.const 4624 - local.set $5 - end - br 1 (;@6;) - end - local.get $5 - i32.load offset=12 - i32.const 8 - i32.and - if ;; label = @7 - i32.const 4624 - local.set $5 - else - block ;; label = @8 - local.get $5 - local.get $1 - i32.store - local.get $5 - i32.const 4 - i32.add - local.tee $5 - local.get $5 - i32.load - local.get $3 - i32.add - i32.store - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $4 - i32.sub - i32.const 7 - i32.and - local.set $7 - i32.const 0 - local.get $10 - i32.const 8 - i32.add - local.tee $5 - i32.sub - i32.const 7 - i32.and - local.set $3 - local.get $1 - local.get $4 - i32.const 7 - i32.and - if (result i32) ;; label = @9 - local.get $7 - else - i32.const 0 - end - i32.add - local.tee $13 - local.get $0 - i32.add - local.set $6 - local.get $10 - local.get $5 - i32.const 7 - i32.and - if (result i32) ;; label = @9 - local.get $3 - else - i32.const 0 - end - i32.add - local.tee $4 - local.get $13 - i32.sub - local.get $0 - i32.sub - local.set $7 - local.get $13 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - block $label$217 ;; label = @9 - local.get $4 - local.get $8 - i32.eq - if ;; label = @10 - block ;; label = @11 - i32.const 4188 - i32.const 4188 - i32.load - local.get $7 - i32.add - local.tee $0 - i32.store - i32.const 4200 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - end - else - block ;; label = @11 - local.get $4 - i32.const 4196 - i32.load - i32.eq - if ;; label = @12 - block ;; label = @13 - i32.const 4184 - i32.const 4184 - i32.load - local.get $7 - i32.add - local.tee $0 - i32.store - i32.const 4196 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $0 - i32.add - local.get $0 - i32.store - br 4 (;@9;) - end - end - local.get $4 - i32.load offset=4 - local.tee $0 - i32.const 3 - i32.and - i32.const 1 - i32.eq - if (result i32) ;; label = @12 - block (result i32) ;; label = @13 - local.get $0 - i32.const -8 - i32.and - local.set $11 - local.get $0 - i32.const 3 - i32.shr_u - local.set $1 - block $label$222 ;; label = @14 - local.get $0 - i32.const 256 - i32.lt_u - if ;; label = @15 - block ;; label = @16 - local.get $4 - i32.load offset=12 - local.set $5 - block $label$224 ;; label = @17 - local.get $4 - i32.load offset=8 - local.tee $3 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.tee $0 - i32.ne - if ;; label = @18 - block ;; label = @19 - local.get $3 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$8 - end - local.get $3 - i32.load offset=12 - local.get $4 - i32.eq - br_if 2 (;@17;) - call $fimport$8 - end - end - end - local.get $5 - local.get $3 - i32.eq - if ;; label = @17 - block ;; label = @18 - i32.const 4176 - i32.const 4176 - i32.load - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 4 (;@14;) - end - end - block $label$228 ;; label = @17 - local.get $5 - local.get $0 - i32.eq - if ;; label = @18 - local.get $5 - i32.const 8 - i32.add - local.set $20 - else - block ;; label = @19 - local.get $5 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$8 - end - local.get $5 - i32.const 8 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - block ;; label = @21 - local.get $0 - local.set $20 - br 4 (;@17;) - end - end - call $fimport$8 - end - end - end - local.get $3 - local.get $5 - i32.store offset=12 - local.get $20 - local.get $3 - i32.store - end - else - block ;; label = @16 - local.get $4 - i32.load offset=24 - local.set $8 - block $label$234 ;; label = @17 - local.get $4 - i32.load offset=12 - local.tee $0 - local.get $4 - i32.eq - if ;; label = @18 - block ;; label = @19 - local.get $4 - i32.const 16 - i32.add - local.tee $3 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @20 - local.get $3 - i32.load - local.tee $0 - if ;; label = @21 - local.get $3 - local.set $1 - else - block ;; label = @22 - i32.const 0 - local.set $12 - br 5 (;@17;) - end - end - end - loop $label$239 ;; label = @20 - local.get $0 - i32.const 20 - i32.add - local.tee $5 - i32.load - local.tee $3 - if ;; label = @21 - block ;; label = @22 - local.get $3 - local.set $0 - local.get $5 - local.set $1 - br 2 (;@20;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $5 - i32.load - local.tee $3 - if ;; label = @21 - block ;; label = @22 - local.get $3 - local.set $0 - local.get $5 - local.set $1 - br 2 (;@20;) - end - end - end - local.get $1 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$8 - else - block ;; label = @21 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $12 - end - end - end - else - block ;; label = @19 - local.get $4 - i32.load offset=8 - local.tee $5 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$8 - end - local.get $5 - i32.const 12 - i32.add - local.tee $3 - i32.load - local.get $4 - i32.ne - if ;; label = @20 - call $fimport$8 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - block ;; label = @21 - local.get $3 - local.get $0 - i32.store - local.get $1 - local.get $5 - i32.store - local.get $0 - local.set $12 - end - else - call $fimport$8 - end - end - end - end - local.get $8 - i32.eqz - br_if 2 (;@14;) - block $label$249 ;; label = @17 - local.get $4 - local.get $4 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @18 - block ;; label = @19 - local.get $0 - local.get $12 - i32.store - local.get $12 - br_if 2 (;@17;) - i32.const 4180 - i32.const 4180 - i32.load - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 5 (;@14;) - end - else - block ;; label = @19 - local.get $8 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @20 - call $fimport$8 - end - local.get $8 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - local.get $0 - local.get $12 - i32.store - else - local.get $8 - local.get $12 - i32.store offset=20 - end - local.get $12 - i32.eqz - br_if 5 (;@14;) - end - end - end - local.get $12 - i32.const 4192 - i32.load - local.tee $1 - i32.lt_u - if ;; label = @17 - call $fimport$8 - end - local.get $12 - local.get $8 - i32.store offset=24 - local.get $4 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.tee $3 - if ;; label = @17 - local.get $3 - local.get $1 - i32.lt_u - if ;; label = @18 - call $fimport$8 - else - block ;; label = @19 - local.get $12 - local.get $3 - i32.store offset=16 - local.get $3 - local.get $12 - i32.store offset=24 - end - end - end - local.get $0 - i32.load offset=4 - local.tee $0 - i32.eqz - br_if 2 (;@14;) - local.get $0 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @17 - call $fimport$8 - else - block ;; label = @18 - local.get $12 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $12 - i32.store offset=24 - end - end - end - end - end - local.get $11 - local.get $7 - i32.add - local.set $7 - local.get $4 - local.get $11 - i32.add - end - else - local.get $4 - end - local.tee $0 - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const -2 - i32.and - i32.store - local.get $6 - local.get $7 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $7 - i32.add - local.get $7 - i32.store - local.get $7 - i32.const 3 - i32.shr_u - local.set $0 - local.get $7 - i32.const 256 - i32.lt_u - if ;; label = @12 - block ;; label = @13 - local.get $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.set $3 - block $label$263 ;; label = @14 - i32.const 4176 - i32.load - local.tee $1 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @15 - block ;; label = @16 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 4192 - i32.load - i32.ge_u - if ;; label = @17 - block ;; label = @18 - local.get $1 - local.set $21 - local.get $0 - local.set $9 - br 4 (;@14;) - end - end - call $fimport$8 - end - else - block ;; label = @16 - i32.const 4176 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $21 - local.get $3 - local.set $9 - end - end - end - local.get $21 - local.get $6 - i32.store - local.get $9 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $9 - i32.store offset=8 - local.get $6 - local.get $3 - i32.store offset=12 - br 4 (;@9;) - end - end - block $label$267 (result i32) ;; label = @12 - local.get $7 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @13 - block (result i32) ;; label = @14 - i32.const 31 - local.get $7 - i32.const 16777215 - i32.gt_u - br_if 2 (;@12;) - drop - local.get $7 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $3 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $3 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - end - local.tee $2 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - local.set $3 - local.get $6 - local.get $2 - i32.store offset=28 - local.get $6 - i32.const 16 - i32.add - local.tee $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store - i32.const 4180 - i32.load - local.tee $1 - i32.const 1 - local.get $2 - i32.shl - local.tee $0 - i32.and - i32.eqz - if ;; label = @12 - block ;; label = @13 - i32.const 4180 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $3 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 4 (;@9;) - end - end - local.get $3 - i32.load - local.set $0 - i32.const 25 - local.get $2 - i32.const 1 - i32.shr_u - i32.sub - local.set $1 - local.get $7 - local.get $2 - i32.const 31 - i32.eq - if (result i32) ;; label = @12 - i32.const 0 - else - local.get $1 - end - i32.shl - local.set $2 - block $label$273 ;; label = @12 - block $label$274 ;; label = @13 - block $label$275 ;; label = @14 - loop $label$276 ;; label = @15 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.eq - br_if 2 (;@13;) - local.get $2 - i32.const 1 - i32.shl - local.set $3 - local.get $0 - i32.const 16 - i32.add - local.get $2 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $2 - i32.load - local.tee $1 - i32.eqz - br_if 1 (;@14;) - local.get $3 - local.set $2 - local.get $1 - local.set $0 - br 0 (;@15;) - end - end - local.get $2 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$8 - else - block ;; label = @15 - local.get $2 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 6 (;@9;) - end - end - br 1 (;@12;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $2 - i32.const 4192 - i32.load - local.tee $1 - i32.ge_u - local.get $0 - local.get $1 - i32.ge_u - i32.and - if ;; label = @13 - block ;; label = @14 - local.get $2 - local.get $6 - i32.store offset=12 - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $2 - i32.store offset=8 - local.get $6 - local.get $0 - i32.store offset=12 - local.get $6 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$8 - end - end - end - end - end - local.get $14 - global.set $global$1 - local.get $13 - i32.const 8 - i32.add - return - end - end - end - loop $label$281 ;; label = @6 - block $label$282 ;; label = @7 - local.get $5 - i32.load - local.tee $2 - local.get $8 - i32.le_u - if ;; label = @8 - local.get $2 - local.get $5 - i32.load offset=4 - i32.add - local.tee $13 - local.get $8 - i32.gt_u - br_if 1 (;@7;) - end - local.get $5 - i32.load offset=8 - local.set $5 - br 1 (;@6;) - end - end - i32.const 0 - local.get $13 - i32.const -47 - i32.add - local.tee $7 - i32.const 8 - i32.add - local.tee $5 - i32.sub - i32.const 7 - i32.and - local.set $2 - local.get $7 - local.get $5 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $2 - else - i32.const 0 - end - i32.add - local.tee $2 - local.get $8 - i32.const 16 - i32.add - local.tee $12 - i32.lt_u - if (result i32) ;; label = @6 - local.get $8 - else - local.get $2 - end - local.tee $7 - i32.const 8 - i32.add - local.set $10 - local.get $7 - i32.const 24 - i32.add - local.set $5 - local.get $3 - i32.const -40 - i32.add - local.set $9 - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $4 - i32.sub - i32.const 7 - i32.and - local.set $2 - i32.const 4200 - local.get $1 - local.get $4 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $2 - else - i32.const 0 - local.tee $2 - end - i32.add - local.tee $4 - i32.store - i32.const 4188 - local.get $9 - local.get $2 - i32.sub - local.tee $2 - i32.store - local.get $4 - local.get $2 - i32.const 1 - i32.or - i32.store offset=4 - local.get $4 - local.get $2 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 4204 - i32.const 4664 - i32.load - i32.store - local.get $7 - i32.const 4 - i32.add - local.tee $2 - i32.const 27 - i32.store - local.get $10 - i32.const 4624 - i64.load align=4 - i64.store align=4 - local.get $10 - i32.const 4632 - i64.load align=4 - i64.store offset=8 align=4 - i32.const 4624 - local.get $1 - i32.store - i32.const 4628 - local.get $3 - i32.store - i32.const 4636 - i32.const 0 - i32.store - i32.const 4632 - local.get $10 - i32.store - local.get $5 - local.set $1 - loop $label$290 ;; label = @6 - local.get $1 - i32.const 4 - i32.add - local.tee $1 - i32.const 7 - i32.store - local.get $1 - i32.const 4 - i32.add - local.get $13 - i32.lt_u - br_if 0 (;@6;) - end - local.get $7 - local.get $8 - i32.ne - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.get $2 - i32.load - i32.const -2 - i32.and - i32.store - local.get $8 - local.get $7 - local.get $8 - i32.sub - local.tee $4 - i32.const 1 - i32.or - i32.store offset=4 - local.get $7 - local.get $4 - i32.store - local.get $4 - i32.const 3 - i32.shr_u - local.set $1 - local.get $4 - i32.const 256 - i32.lt_u - if ;; label = @8 - block ;; label = @9 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.set $2 - i32.const 4176 - i32.load - local.tee $3 - i32.const 1 - local.get $1 - i32.shl - local.tee $1 - i32.and - if ;; label = @10 - local.get $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $1 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$8 - else - block ;; label = @12 - local.get $3 - local.set $15 - local.get $1 - local.set $11 - end - end - else - block ;; label = @11 - i32.const 4176 - local.get $3 - local.get $1 - i32.or - i32.store - local.get $2 - i32.const 8 - i32.add - local.set $15 - local.get $2 - local.set $11 - end - end - local.get $15 - local.get $8 - i32.store - local.get $11 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $11 - i32.store offset=8 - local.get $8 - local.get $2 - i32.store offset=12 - br 6 (;@3;) - end - end - local.get $4 - i32.const 8 - i32.shr_u - local.tee $1 - if (result i32) ;; label = @8 - local.get $4 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @9 - i32.const 31 - else - local.get $4 - i32.const 14 - local.get $1 - local.get $1 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $2 - i32.shl - local.tee $3 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $1 - local.get $2 - i32.or - local.get $3 - local.get $1 - i32.shl - local.tee $3 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $1 - i32.or - i32.sub - local.get $3 - local.get $1 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $1 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $1 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $5 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - local.set $2 - local.get $8 - local.get $5 - i32.store offset=28 - local.get $8 - i32.const 0 - i32.store offset=20 - local.get $12 - i32.const 0 - i32.store - i32.const 4180 - i32.load - local.tee $3 - i32.const 1 - local.get $5 - i32.shl - local.tee $1 - i32.and - i32.eqz - if ;; label = @8 - block ;; label = @9 - i32.const 4180 - local.get $3 - local.get $1 - i32.or - i32.store - local.get $2 - local.get $8 - i32.store - local.get $8 - local.get $2 - i32.store offset=24 - local.get $8 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $8 - i32.store offset=8 - br 6 (;@3;) - end - end - local.get $2 - i32.load - local.set $1 - i32.const 25 - local.get $5 - i32.const 1 - i32.shr_u - i32.sub - local.set $3 - local.get $4 - local.get $5 - i32.const 31 - i32.eq - if (result i32) ;; label = @8 - i32.const 0 - else - local.get $3 - end - i32.shl - local.set $5 - block $label$304 ;; label = @8 - block $label$305 ;; label = @9 - block $label$306 ;; label = @10 - loop $label$307 ;; label = @11 - local.get $1 - i32.load offset=4 - i32.const -8 - i32.and - local.get $4 - i32.eq - br_if 2 (;@9;) - local.get $5 - i32.const 1 - i32.shl - local.set $2 - local.get $1 - i32.const 16 - i32.add - local.get $5 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $5 - i32.load - local.tee $3 - i32.eqz - br_if 1 (;@10;) - local.get $2 - local.set $5 - local.get $3 - local.set $1 - br 0 (;@11;) - end - end - local.get $5 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$8 - else - block ;; label = @11 - local.get $5 - local.get $8 - i32.store - local.get $8 - local.get $1 - i32.store offset=24 - local.get $8 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $8 - i32.store offset=8 - br 8 (;@3;) - end - end - br 1 (;@8;) - end - local.get $1 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $5 - i32.const 4192 - i32.load - local.tee $3 - i32.ge_u - local.get $1 - local.get $3 - i32.ge_u - i32.and - if ;; label = @9 - block ;; label = @10 - local.get $5 - local.get $8 - i32.store offset=12 - local.get $2 - local.get $8 - i32.store - local.get $8 - local.get $5 - i32.store offset=8 - local.get $8 - local.get $1 - i32.store offset=12 - local.get $8 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$8 - end - end - end - end - end - else - block ;; label = @5 - i32.const 4192 - i32.load - local.tee $2 - i32.eqz - local.get $1 - local.get $2 - i32.lt_u - i32.or - if ;; label = @6 - i32.const 4192 - local.get $1 - i32.store - end - i32.const 4624 - local.get $1 - i32.store - i32.const 4628 - local.get $3 - i32.store - i32.const 4636 - i32.const 0 - i32.store - i32.const 4212 - i32.const 4648 - i32.load - i32.store - i32.const 4208 - i32.const -1 - i32.store - i32.const 0 - local.set $2 - loop $label$314 ;; label = @6 - local.get $2 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.tee $5 - local.get $5 - i32.store offset=12 - local.get $5 - local.get $5 - i32.store offset=8 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 32 - i32.ne - br_if 0 (;@6;) - end - local.get $3 - i32.const -40 - i32.add - local.set $5 - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $2 - i32.sub - i32.const 7 - i32.and - local.set $3 - i32.const 4200 - local.get $1 - local.get $2 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $3 - else - i32.const 0 - end - local.tee $1 - i32.add - local.tee $3 - i32.store - i32.const 4188 - local.get $5 - local.get $1 - i32.sub - local.tee $1 - i32.store - local.get $3 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $1 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 4204 - i32.const 4664 - i32.load - i32.store - end - end - end - i32.const 4188 - i32.load - local.tee $1 - local.get $0 - i32.gt_u - if ;; label = @3 - block ;; label = @4 - i32.const 4188 - local.get $1 - local.get $0 - i32.sub - local.tee $3 - i32.store - i32.const 4200 - i32.const 4200 - i32.load - local.tee $2 - local.get $0 - i32.add - local.tee $1 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - end - call $12 - i32.const 12 - i32.store - local.get $14 - global.set $global$1 - i32.const 0 - end - ) - (func $38 (;51;) (type $3) (param $0 i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) - block $label$1 ;; label = @1 - local.get $0 - i32.eqz - if ;; label = @2 - return - end - local.get $0 - i32.const -8 - i32.add - local.tee $1 - i32.const 4192 - i32.load - local.tee $11 - i32.lt_u - if ;; label = @2 - call $fimport$8 - end - local.get $0 - i32.const -4 - i32.add - i32.load - local.tee $0 - i32.const 3 - i32.and - local.tee $8 - i32.const 1 - i32.eq - if ;; label = @2 - call $fimport$8 - end - local.get $1 - local.get $0 - i32.const -8 - i32.and - local.tee $4 - i32.add - local.set $6 - block $label$5 ;; label = @2 - local.get $0 - i32.const 1 - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $1 - local.set $3 - local.get $4 - local.set $2 - end - else - block ;; label = @4 - local.get $8 - i32.eqz - if ;; label = @5 - return - end - local.get $1 - i32.const 0 - local.get $1 - i32.load - local.tee $8 - i32.sub - i32.add - local.tee $0 - local.get $11 - i32.lt_u - if ;; label = @5 - call $fimport$8 - end - local.get $8 - local.get $4 - i32.add - local.set $1 - local.get $0 - i32.const 4196 - i32.load - i32.eq - if ;; label = @5 - block ;; label = @6 - local.get $6 - i32.const 4 - i32.add - local.tee $2 - i32.load - local.tee $3 - i32.const 3 - i32.and - i32.const 3 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 6 (;@2;) - end - end - i32.const 4184 - local.get $1 - i32.store - local.get $2 - local.get $3 - i32.const -2 - i32.and - i32.store - local.get $0 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $0 - local.get $1 - i32.add - local.get $1 - i32.store - return - end - end - local.get $8 - i32.const 3 - i32.shr_u - local.set $10 - local.get $8 - i32.const 256 - i32.lt_u - if ;; label = @5 - block ;; label = @6 - local.get $0 - i32.load offset=12 - local.set $3 - local.get $0 - i32.load offset=8 - local.tee $4 - local.get $10 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.tee $2 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $4 - local.get $11 - i32.lt_u - if ;; label = @9 - call $fimport$8 - end - local.get $4 - i32.load offset=12 - local.get $0 - i32.ne - if ;; label = @9 - call $fimport$8 - end - end - end - local.get $3 - local.get $4 - i32.eq - if ;; label = @7 - block ;; label = @8 - i32.const 4176 - i32.const 4176 - i32.load - i32.const 1 - local.get $10 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 6 (;@2;) - end - end - local.get $3 - local.get $2 - i32.eq - if ;; label = @7 - local.get $3 - i32.const 8 - i32.add - local.set $5 - else - block ;; label = @8 - local.get $3 - local.get $11 - i32.lt_u - if ;; label = @9 - call $fimport$8 - end - local.get $3 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.get $0 - i32.eq - if ;; label = @9 - local.get $2 - local.set $5 - else - call $fimport$8 - end - end - end - local.get $4 - local.get $3 - i32.store offset=12 - local.get $5 - local.get $4 - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 4 (;@2;) - end - end - local.get $0 - i32.load offset=24 - local.set $12 - block $label$22 ;; label = @5 - local.get $0 - i32.load offset=12 - local.tee $4 - local.get $0 - i32.eq - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 16 - i32.add - local.tee $5 - i32.const 4 - i32.add - local.tee $8 - i32.load - local.tee $4 - if ;; label = @8 - local.get $8 - local.set $5 - else - local.get $5 - i32.load - local.tee $4 - i32.eqz - if ;; label = @9 - block ;; label = @10 - i32.const 0 - local.set $7 - br 5 (;@5;) - end - end - end - loop $label$27 ;; label = @8 - local.get $4 - i32.const 20 - i32.add - local.tee $8 - i32.load - local.tee $10 - if ;; label = @9 - block ;; label = @10 - local.get $10 - local.set $4 - local.get $8 - local.set $5 - br 2 (;@8;) - end - end - local.get $4 - i32.const 16 - i32.add - local.tee $8 - i32.load - local.tee $10 - if ;; label = @9 - block ;; label = @10 - local.get $10 - local.set $4 - local.get $8 - local.set $5 - br 2 (;@8;) - end - end - end - local.get $5 - local.get $11 - i32.lt_u - if ;; label = @8 - call $fimport$8 - else - block ;; label = @9 - local.get $5 - i32.const 0 - i32.store - local.get $4 - local.set $7 - end - end - end - else - block ;; label = @7 - local.get $0 - i32.load offset=8 - local.tee $5 - local.get $11 - i32.lt_u - if ;; label = @8 - call $fimport$8 - end - local.get $5 - i32.const 12 - i32.add - local.tee $8 - i32.load - local.get $0 - i32.ne - if ;; label = @8 - call $fimport$8 - end - local.get $4 - i32.const 8 - i32.add - local.tee $10 - i32.load - local.get $0 - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $8 - local.get $4 - i32.store - local.get $10 - local.get $5 - i32.store - local.get $4 - local.set $7 - end - else - call $fimport$8 - end - end - end - end - local.get $12 - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $0 - i32.load offset=28 - local.tee $4 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - local.tee $5 - i32.load - i32.eq - if ;; label = @7 - block ;; label = @8 - local.get $5 - local.get $7 - i32.store - local.get $7 - i32.eqz - if ;; label = @9 - block ;; label = @10 - i32.const 4180 - i32.const 4180 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 8 (;@2;) - end - end - end - else - block ;; label = @8 - local.get $12 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$8 - end - local.get $12 - i32.const 16 - i32.add - local.tee $4 - i32.load - local.get $0 - i32.eq - if ;; label = @9 - local.get $4 - local.get $7 - i32.store - else - local.get $12 - local.get $7 - i32.store offset=20 - end - local.get $7 - i32.eqz - if ;; label = @9 - block ;; label = @10 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 8 (;@2;) - end - end - end - end - local.get $7 - i32.const 4192 - i32.load - local.tee $5 - i32.lt_u - if ;; label = @7 - call $fimport$8 - end - local.get $7 - local.get $12 - i32.store offset=24 - local.get $0 - i32.const 16 - i32.add - local.tee $8 - i32.load - local.tee $4 - if ;; label = @7 - local.get $4 - local.get $5 - i32.lt_u - if ;; label = @8 - call $fimport$8 - else - block ;; label = @9 - local.get $7 - local.get $4 - i32.store offset=16 - local.get $4 - local.get $7 - i32.store offset=24 - end - end - end - local.get $8 - i32.load offset=4 - local.tee $4 - if ;; label = @7 - local.get $4 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @8 - call $fimport$8 - else - block ;; label = @9 - local.get $7 - local.get $4 - i32.store offset=20 - local.get $4 - local.get $7 - i32.store offset=24 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - else - block ;; label = @8 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - end - else - block ;; label = @6 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - end - end - end - local.get $3 - local.get $6 - i32.ge_u - if ;; label = @2 - call $fimport$8 - end - local.get $6 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 1 - i32.and - i32.eqz - if ;; label = @2 - call $fimport$8 - end - local.get $0 - i32.const 2 - i32.and - if ;; label = @2 - block ;; label = @3 - local.get $1 - local.get $0 - i32.const -2 - i32.and - i32.store - local.get $3 - local.get $2 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $2 - i32.add - local.get $2 - i32.store - end - else - block ;; label = @3 - local.get $6 - i32.const 4200 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 4188 - i32.const 4188 - i32.load - local.get $2 - i32.add - local.tee $0 - i32.store - i32.const 4200 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - i32.const 4196 - i32.load - i32.ne - if ;; label = @6 - return - end - i32.const 4196 - i32.const 0 - i32.store - i32.const 4184 - i32.const 0 - i32.store - return - end - end - local.get $6 - i32.const 4196 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 4184 - i32.const 4184 - i32.load - local.get $2 - i32.add - local.tee $0 - i32.store - i32.const 4196 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $0 - i32.add - local.get $0 - i32.store - return - end - end - local.get $0 - i32.const -8 - i32.and - local.get $2 - i32.add - local.set $5 - local.get $0 - i32.const 3 - i32.shr_u - local.set $4 - block $label$61 ;; label = @4 - local.get $0 - i32.const 256 - i32.lt_u - if ;; label = @5 - block ;; label = @6 - local.get $6 - i32.load offset=12 - local.set $2 - local.get $6 - i32.load offset=8 - local.tee $1 - local.get $4 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.tee $0 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $1 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$8 - end - local.get $1 - i32.load offset=12 - local.get $6 - i32.ne - if ;; label = @9 - call $fimport$8 - end - end - end - local.get $2 - local.get $1 - i32.eq - if ;; label = @7 - block ;; label = @8 - i32.const 4176 - i32.const 4176 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 4 (;@4;) - end - end - local.get $2 - local.get $0 - i32.eq - if ;; label = @7 - local.get $2 - i32.const 8 - i32.add - local.set $14 - else - block ;; label = @8 - local.get $2 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$8 - end - local.get $2 - i32.const 8 - i32.add - local.tee $0 - i32.load - local.get $6 - i32.eq - if ;; label = @9 - local.get $0 - local.set $14 - else - call $fimport$8 - end - end - end - local.get $1 - local.get $2 - i32.store offset=12 - local.get $14 - local.get $1 - i32.store - end - else - block ;; label = @6 - local.get $6 - i32.load offset=24 - local.set $7 - block $label$73 ;; label = @7 - local.get $6 - i32.load offset=12 - local.tee $0 - local.get $6 - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $6 - i32.const 16 - i32.add - local.tee $2 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - if ;; label = @10 - local.get $1 - local.set $2 - else - local.get $2 - i32.load - local.tee $0 - i32.eqz - if ;; label = @11 - block ;; label = @12 - i32.const 0 - local.set $9 - br 5 (;@7;) - end - end - end - loop $label$78 ;; label = @10 - local.get $0 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $4 - if ;; label = @11 - block ;; label = @12 - local.get $4 - local.set $0 - local.get $1 - local.set $2 - br 2 (;@10;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $4 - if ;; label = @11 - block ;; label = @12 - local.get $4 - local.set $0 - local.get $1 - local.set $2 - br 2 (;@10;) - end - end - end - local.get $2 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$8 - else - block ;; label = @11 - local.get $2 - i32.const 0 - i32.store - local.get $0 - local.set $9 - end - end - end - else - block ;; label = @9 - local.get $6 - i32.load offset=8 - local.tee $2 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$8 - end - local.get $2 - i32.const 12 - i32.add - local.tee $1 - i32.load - local.get $6 - i32.ne - if ;; label = @10 - call $fimport$8 - end - local.get $0 - i32.const 8 - i32.add - local.tee $4 - i32.load - local.get $6 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $1 - local.get $0 - i32.store - local.get $4 - local.get $2 - i32.store - local.get $0 - local.set $9 - end - else - call $fimport$8 - end - end - end - end - local.get $7 - if ;; label = @7 - block ;; label = @8 - local.get $6 - local.get $6 - i32.load offset=28 - local.tee $0 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - local.tee $2 - i32.load - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $2 - local.get $9 - i32.store - local.get $9 - i32.eqz - if ;; label = @11 - block ;; label = @12 - i32.const 4180 - i32.const 4180 - i32.load - i32.const 1 - local.get $0 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 8 (;@4;) - end - end - end - else - block ;; label = @10 - local.get $7 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$8 - end - local.get $7 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $6 - i32.eq - if ;; label = @11 - local.get $0 - local.get $9 - i32.store - else - local.get $7 - local.get $9 - i32.store offset=20 - end - local.get $9 - i32.eqz - br_if 6 (;@4;) - end - end - local.get $9 - i32.const 4192 - i32.load - local.tee $2 - i32.lt_u - if ;; label = @9 - call $fimport$8 - end - local.get $9 - local.get $7 - i32.store offset=24 - local.get $6 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - if ;; label = @9 - local.get $0 - local.get $2 - i32.lt_u - if ;; label = @10 - call $fimport$8 - else - block ;; label = @11 - local.get $9 - local.get $0 - i32.store offset=16 - local.get $0 - local.get $9 - i32.store offset=24 - end - end - end - local.get $1 - i32.load offset=4 - local.tee $0 - if ;; label = @9 - local.get $0 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$8 - else - block ;; label = @11 - local.get $9 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $9 - i32.store offset=24 - end - end - end - end - end - end - end - end - local.get $3 - local.get $5 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $5 - i32.add - local.get $5 - i32.store - local.get $3 - i32.const 4196 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 4184 - local.get $5 - i32.store - return - end - else - local.get $5 - local.set $2 - end - end - end - local.get $2 - i32.const 3 - i32.shr_u - local.set $1 - local.get $2 - i32.const 256 - i32.lt_u - if ;; label = @2 - block ;; label = @3 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 4216 - i32.add - local.set $0 - i32.const 4176 - i32.load - local.tee $2 - i32.const 1 - local.get $1 - i32.shl - local.tee $1 - i32.and - if ;; label = @4 - local.get $0 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $1 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @5 - call $fimport$8 - else - block ;; label = @6 - local.get $2 - local.set $15 - local.get $1 - local.set $13 - end - end - else - block ;; label = @5 - i32.const 4176 - local.get $2 - local.get $1 - i32.or - i32.store - local.get $0 - i32.const 8 - i32.add - local.set $15 - local.get $0 - local.set $13 - end - end - local.get $15 - local.get $3 - i32.store - local.get $13 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $13 - i32.store offset=8 - local.get $3 - local.get $0 - i32.store offset=12 - return - end - end - local.get $2 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @2 - local.get $2 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @3 - i32.const 31 - else - local.get $2 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $4 - local.get $0 - i32.or - local.get $1 - local.get $4 - i32.shl - local.tee $0 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $1 - i32.or - i32.sub - local.get $0 - local.get $1 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $1 - i32.const 2 - i32.shl - i32.const 4480 - i32.add - local.set $0 - local.get $3 - local.get $1 - i32.store offset=28 - local.get $3 - i32.const 0 - i32.store offset=20 - local.get $3 - i32.const 0 - i32.store offset=16 - block $label$113 ;; label = @2 - i32.const 4180 - i32.load - local.tee $4 - i32.const 1 - local.get $1 - i32.shl - local.tee $5 - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $0 - i32.load - local.set $0 - i32.const 25 - local.get $1 - i32.const 1 - i32.shr_u - i32.sub - local.set $4 - local.get $2 - local.get $1 - i32.const 31 - i32.eq - if (result i32) ;; label = @5 - i32.const 0 - else - local.get $4 - end - i32.shl - local.set $1 - block $label$117 ;; label = @5 - block $label$118 ;; label = @6 - block $label$119 ;; label = @7 - loop $label$120 ;; label = @8 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $2 - i32.eq - br_if 2 (;@6;) - local.get $1 - i32.const 1 - i32.shl - local.set $4 - local.get $0 - i32.const 16 - i32.add - local.get $1 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $1 - i32.load - local.tee $5 - i32.eqz - br_if 1 (;@7;) - local.get $4 - local.set $1 - local.get $5 - local.set $0 - br 0 (;@8;) - end - end - local.get $1 - i32.const 4192 - i32.load - i32.lt_u - if ;; label = @7 - call $fimport$8 - else - block ;; label = @8 - local.get $1 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.store offset=24 - local.get $3 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $3 - i32.store offset=8 - br 6 (;@2;) - end - end - br 1 (;@5;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $2 - i32.const 4192 - i32.load - local.tee $4 - i32.ge_u - local.get $0 - local.get $4 - i32.ge_u - i32.and - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.get $3 - i32.store offset=12 - local.get $1 - local.get $3 - i32.store - local.get $3 - local.get $2 - i32.store offset=8 - local.get $3 - local.get $0 - i32.store offset=12 - local.get $3 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$8 - end - end - end - else - block ;; label = @4 - i32.const 4180 - local.get $4 - local.get $5 - i32.or - i32.store - local.get $0 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.store offset=24 - local.get $3 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $3 - i32.store offset=8 - end - end - end - i32.const 4208 - i32.const 4208 - i32.load - i32.const -1 - i32.add - local.tee $0 - i32.store - local.get $0 - if ;; label = @2 - return - else - i32.const 4632 - local.set $0 - end - loop $label$128 ;; label = @2 - local.get $0 - i32.load - local.tee $2 - i32.const 8 - i32.add - local.set $0 - local.get $2 - br_if 0 (;@2;) - end - i32.const 4208 - i32.const -1 - i32.store - end - ) - (func $39 (;52;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.eqz - if ;; label = @2 - i32.const 1 - local.set $0 - end - loop $label$3 ;; label = @2 - block $label$4 ;; label = @3 - local.get $0 - call $37 - local.tee $1 - if ;; label = @4 - block ;; label = @5 - local.get $1 - local.set $0 - br 2 (;@3;) - end - end - call $43 - local.tee $1 - if ;; label = @4 - block ;; label = @5 - local.get $1 - i32.const 0 - i32.and - i32.const 8 - i32.add - call_indirect (type $1) - br 3 (;@2;) - end - else - i32.const 0 - local.set $0 - end - end - end - local.get $0 - end - ) - (func $40 (;53;) (type $2) (param $0 i32) (result i32) - local.get $0 - call $39 - ) - (func $41 (;54;) (type $3) (param $0 i32) - local.get $0 - call $38 - ) - (func $42 (;55;) (type $3) (param $0 i32) - local.get $0 - call $41 - ) - (func $43 (;56;) (type $4) (result i32) - (local $0 i32) - block $label$1 (result i32) ;; label = @1 - i32.const 4672 - i32.const 4672 - i32.load - local.tee $0 - i32.const 0 - i32.add - i32.store - local.get $0 - end - ) - (func $44 (;57;) (type $1) - nop - ) - (func $45 (;58;) (type $2) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$0 - i32.load - local.tee $2 - local.get $0 - i32.const 15 - i32.add - i32.const -16 - i32.and - local.tee $0 - i32.add - local.set $1 - local.get $0 - i32.const 0 - i32.gt_s - local.get $1 - local.get $2 - i32.lt_s - i32.and - local.get $1 - i32.const 0 - i32.lt_s - i32.or - if ;; label = @2 - block ;; label = @3 - call $fimport$6 - drop - i32.const 12 - call $fimport$11 - i32.const -1 - return - end - end - global.get $global$0 - local.get $1 - i32.store - local.get $1 - call $fimport$5 - i32.gt_s - if ;; label = @2 - call $fimport$4 - i32.eqz - if ;; label = @3 - block ;; label = @4 - i32.const 12 - call $fimport$11 - global.get $global$0 - local.get $2 - i32.store - i32.const -1 - return - end - end - end - local.get $2 - end - ) - (func $46 (;59;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - local.get $2 - i32.add - local.set $4 - local.get $2 - i32.const 20 - i32.ge_s - if ;; label = @2 - block ;; label = @3 - local.get $1 - i32.const 255 - i32.and - local.set $1 - local.get $0 - i32.const 3 - i32.and - local.tee $3 - if ;; label = @4 - block ;; label = @5 - local.get $0 - i32.const 4 - i32.add - local.get $3 - i32.sub - local.set $3 - loop $label$4 ;; label = @6 - local.get $0 - local.get $3 - i32.lt_s - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.get $1 - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - br 2 (;@6;) - end - end - end - end - end - local.get $1 - local.get $1 - i32.const 8 - i32.shl - i32.or - local.get $1 - i32.const 16 - i32.shl - i32.or - local.get $1 - i32.const 24 - i32.shl - i32.or - local.set $3 - local.get $4 - i32.const -4 - i32.and - local.set $5 - loop $label$6 ;; label = @4 - local.get $0 - local.get $5 - i32.lt_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $3 - i32.store - local.get $0 - i32.const 4 - i32.add - local.set $0 - br 2 (;@4;) - end - end - end - end - end - loop $label$8 ;; label = @2 - local.get $0 - local.get $4 - i32.lt_s - if ;; label = @3 - block ;; label = @4 - local.get $0 - local.get $1 - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - br 2 (;@2;) - end - end - end - local.get $0 - local.get $2 - i32.sub - end - ) - (func $47 (;60;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - block $label$1 (result i32) ;; label = @1 - local.get $2 - i32.const 4096 - i32.ge_s - if ;; label = @2 - local.get $0 - local.get $1 - local.get $2 - call $fimport$12 - return - end - local.get $0 - local.set $3 - local.get $0 - i32.const 3 - i32.and - local.get $1 - i32.const 3 - i32.and - i32.eq - if ;; label = @2 - block ;; label = @3 - loop $label$4 ;; label = @4 - local.get $0 - i32.const 3 - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $2 - i32.eqz - if ;; label = @7 - local.get $3 - return - end - local.get $0 - local.get $1 - i32.load8_s - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - br 2 (;@4;) - end - end - end - loop $label$7 ;; label = @4 - local.get $2 - i32.const 4 - i32.ge_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $1 - i32.load - i32.store - local.get $0 - i32.const 4 - i32.add - local.set $0 - local.get $1 - i32.const 4 - i32.add - local.set $1 - local.get $2 - i32.const 4 - i32.sub - local.set $2 - br 2 (;@4;) - end - end - end - end - end - loop $label$9 ;; label = @2 - local.get $2 - i32.const 0 - i32.gt_s - if ;; label = @3 - block ;; label = @4 - local.get $0 - local.get $1 - i32.load8_s - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - br 2 (;@2;) - end - end - end - local.get $3 - end - ) - (func $48 (;61;) (type $4) (result i32) - i32.const 0 - ) - (func $49 (;62;) (type $6) (param $0 i32) (param $1 i32) (result i32) - local.get $1 - local.get $0 - i32.const 1 - i32.and - i32.const 0 - i32.add - call_indirect (type $2) - ) - (func $50 (;63;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - local.get $1 - local.get $2 - local.get $3 - local.get $0 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - ) - (func $51 (;64;) (type $5) (param $0 i32) (param $1 i32) - local.get $1 - local.get $0 - i32.const 1 - i32.and - i32.const 6 - i32.add - call_indirect (type $3) - ) - (func $52 (;65;) (type $3) (param $0 i32) - local.get $0 - i32.const 0 - i32.and - i32.const 8 - i32.add - call_indirect (type $1) - ) - (func $53 (;66;) (type $2) (param $0 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - i32.const 0 - call $fimport$3 - i32.const 0 - end - ) - (func $54 (;67;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - i32.const 1 - call $fimport$3 - i32.const 0 - end - ) - (func $55 (;68;) (type $3) (param $0 i32) - i32.const 2 - call $fimport$3 - ) - (func $56 (;69;) (type $1) - i32.const 3 - call $fimport$3 - ) - (global $global$0 (;5;) (mut i32) global.get $gimport$0) - (global $global$1 (;6;) (mut i32) global.get $gimport$1) - (global $global$2 (;7;) (mut i32) global.get $gimport$2) - (global $global$3 (;8;) (mut i32) i32.const 0) - (global $global$4 (;9;) (mut i32) i32.const 0) - (global $global$5 (;10;) (mut i32) i32.const 0) - (export "_sbrk" (func $45)) - (export "_free" (func $38)) - (export "_main" (func $7)) - (export "_pthread_self" (func $48)) - (export "_memset" (func $46)) - (export "_malloc" (func $37)) - (export "_memcpy" (func $47)) - (export "___errno_location" (func $12)) - (export "runPostSets" (func $44)) - (export "stackAlloc" (func $0)) - (export "stackSave" (func $1)) - (export "stackRestore" (func $2)) - (export "establishStackSpace" (func $3)) - (export "setThrew" (func $4)) - (export "setTempRet0" (func $5)) - (export "getTempRet0" (func $6)) - (export "dynCall_ii" (func $49)) - (export "dynCall_iiii" (func $50)) - (export "dynCall_vi" (func $51)) - (export "dynCall_v" (func $52)) - (elem (;0;) (global.get $gimport$19) func $53 $9 $54 $14 $10 $15 $55 $16 $56) - (data (;0;) (i32.const 1024) "&\02\00\00a\00\00\00q=\8a>\00\00\00\00c\00\00\00\8f\c2\f5=\00\00\00\00g\00\00\00\8f\c2\f5=\00\00\00\00t\00\00\00q=\8a>\00\00\00\00B\00\00\00\0a\d7\a3<\00\00\00\00D\00\00\00\0a\d7\a3<\00\00\00\00H\00\00\00\0a\d7\a3<\00\00\00\00K\00\00\00\0a\d7\a3<\00\00\00\00M\00\00\00\0a\d7\a3<\00\00\00\00N\00\00\00\0a\d7\a3<\00\00\00\00R\00\00\00\0a\d7\a3<\00\00\00\00S\00\00\00\0a\d7\a3<\00\00\00\00V\00\00\00\0a\d7\a3<\00\00\00\00W\00\00\00\0a\d7\a3<\00\00\00\00Y\00\00\00\0a\d7\a3<") - (data (;1;) (i32.const 1220) "a\00\00\00\e9\1c\9b>\00\00\00\00c\00\00\00r\bdJ>\00\00\00\00g\00\00\00\d7IJ>\00\00\00\00t\00\00\00r_\9a>") - (data (;2;) (i32.const 1280) "\04\05\00\00\05") - (data (;3;) (i32.const 1296) "\01") - (data (;4;) (i32.const 1320) "\01\00\00\00\02\00\00\00L\12\00\00\00\04") - (data (;5;) (i32.const 1344) "\01") - (data (;6;) (i32.const 1359) "\0a\ff\ff\ff\ff") - (data (;7;) (i32.const 1396) "*\00\00\00error: %d\0a\00GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA\00\11\00\0a\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\09\00\00\00\00\0b") - (data (;8;) (i32.const 1731) "\11\00\0f\0a\11\11\11\03\0a\07\00\01\13\09\0b\0b\00\00\09\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") - (data (;9;) (i32.const 1780) "\0b") - (data (;10;) (i32.const 1789) "\11\00\0a\0a\11\11\11\00\0a\00\00\02\00\09\0b\00\00\00\09\00\0b\00\00\0b") - (data (;11;) (i32.const 1838) "\0c") - (data (;12;) (i32.const 1850) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c") - (data (;13;) (i32.const 1896) "\0e") - (data (;14;) (i32.const 1908) "\0d\00\00\00\04\0d\00\00\00\00\09\0e\00\00\00\00\00\0e\00\00\0e") - (data (;15;) (i32.const 1954) "\10") - (data (;16;) (i32.const 1966) "\0f\00\00\00\00\0f\00\00\00\00\09\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") - (data (;17;) (i32.const 2021) "\12\00\00\00\12\12\12\00\00\00\00\00\00\09") - (data (;18;) (i32.const 2070) "\0b") - (data (;19;) (i32.const 2082) "\0a\00\00\00\00\0a\00\00\00\00\09\0b\00\00\00\00\00\0b\00\00\0b") - (data (;20;) (i32.const 2128) "\0c") - (data (;21;) (i32.const 2140) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\22\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\09\0a\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\5c]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") -) \ No newline at end of file diff --git a/cranelift/wasm/wasmtests/embenchen_ifs.wat b/cranelift/wasm/wasmtests/embenchen_ifs.wat deleted file mode 100644 index dc466513c7e6..000000000000 --- a/cranelift/wasm/wasmtests/embenchen_ifs.wat +++ /dev/null @@ -1,11505 +0,0 @@ -(module - (type $0 (;0;) (func (param i32 i32 i32) (result i32))) - (type $1 (;1;) (func (param i32) (result i32))) - (type $2 (;2;) (func (param i32))) - (type $3 (;3;) (func (result i32))) - (type $4 (;4;) (func (param i32 i32) (result i32))) - (type $5 (;5;) (func (param i32 i32))) - (type $6 (;6;) (func)) - (type $7 (;7;) (func (param i32 i32 i32 i32 i32) (result i32))) - (type $8 (;8;) (func (param i32 i32 i32))) - (type $9 (;9;) (func (param i64 i32) (result i32))) - (type $10 (;10;) (func (param i32 i32 i32 i32 i32))) - (type $11 (;11;) (func (param f64 i32) (result f64))) - (type $12 (;12;) (func (param i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory $16 (;0;) 2048 2048)) - (import "env" "table" (table $timport$17 (;0;) 8 8 funcref)) - (import "env" "DYNAMICTOP_PTR" (global $gimport$0 (;0;) i32)) - (import "env" "STACKTOP" (global $gimport$1 (;1;) i32)) - (import "env" "STACK_MAX" (global $gimport$2 (;2;) i32)) - (import "env" "memoryBase" (global $gimport$18 (;3;) i32)) - (import "env" "tableBase" (global $gimport$19 (;4;) i32)) - (import "env" "abort" (func $fimport$3 (;0;) (type $2))) - (import "env" "enlargeMemory" (func $fimport$4 (;1;) (type $3))) - (import "env" "getTotalMemory" (func $fimport$5 (;2;) (type $3))) - (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (;3;) (type $3))) - (import "env" "_pthread_cleanup_pop" (func $fimport$7 (;4;) (type $2))) - (import "env" "___syscall6" (func $fimport$8 (;5;) (type $4))) - (import "env" "_pthread_cleanup_push" (func $fimport$9 (;6;) (type $5))) - (import "env" "_abort" (func $fimport$10 (;7;) (type $6))) - (import "env" "___setErrNo" (func $fimport$11 (;8;) (type $2))) - (import "env" "_emscripten_memcpy_big" (func $fimport$12 (;9;) (type $0))) - (import "env" "___syscall54" (func $fimport$13 (;10;) (type $4))) - (import "env" "___syscall140" (func $fimport$14 (;11;) (type $4))) - (import "env" "___syscall146" (func $fimport$15 (;12;) (type $4))) - (func $0 (;13;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $1 - global.get $global$1 - local.get $0 - i32.add - global.set $global$1 - global.get $global$1 - i32.const 15 - i32.add - i32.const -16 - i32.and - global.set $global$1 - local.get $1 - end - ) - (func $1 (;14;) (type $3) (result i32) - global.get $global$1 - ) - (func $2 (;15;) (type $2) (param $0 i32) - local.get $0 - global.set $global$1 - ) - (func $3 (;16;) (type $5) (param $0 i32) (param $1 i32) - block $label$1 ;; label = @1 - local.get $0 - global.set $global$1 - local.get $1 - global.set $global$2 - end - ) - (func $4 (;17;) (type $5) (param $0 i32) (param $1 i32) - global.get $global$3 - i32.eqz - if ;; label = @1 - block ;; label = @2 - local.get $0 - global.set $global$3 - local.get $1 - global.set $global$4 - end - end - ) - (func $5 (;18;) (type $2) (param $0 i32) - local.get $0 - global.set $global$5 - ) - (func $6 (;19;) (type $3) (result i32) - global.get $global$5 - ) - (func $7 (;20;) (type $3) (result i32) - (local $0 i32) - block $label$1 (result i32) ;; label = @1 - i32.const 3584 - i32.const 3584 - i32.load - local.tee $0 - i32.const 1 - i32.add - i32.store - local.get $0 - i32.const 16384 - i32.and - end - ) - (func $8 (;21;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $4 - local.set $2 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $0 - i32.const 1 - i32.le_s - br_if 0 (;@3;) - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - block $label$6 ;; label = @6 - block $label$7 ;; label = @7 - block $label$8 ;; label = @8 - block $label$9 ;; label = @9 - block $label$10 ;; label = @10 - local.get $1 - i32.load offset=4 - i32.load8_s - local.tee $0 - i32.const 48 - i32.sub - br_table 5 (;@5;) 0 (;@10;) 2 (;@8;) 1 (;@9;) 3 (;@7;) 4 (;@6;) 6 (;@4;) - end - i32.const 75 - local.set $3 - br 7 (;@2;) - end - br 5 (;@3;) - end - i32.const 625 - local.set $3 - br 5 (;@2;) - end - i32.const 6250 - local.set $3 - br 4 (;@2;) - end - i32.const 12500 - local.set $3 - br 3 (;@2;) - end - local.get $4 - global.set $global$1 - i32.const 0 - return - end - local.get $2 - local.get $0 - i32.const -48 - i32.add - i32.store - i32.const 1140 - local.get $2 - call $34 - drop - local.get $4 - global.set $global$1 - i32.const -1 - return - end - i32.const 1250 - local.set $3 - end - i32.const 0 - local.set $1 - i32.const 0 - local.set $0 - loop $label$11 ;; label = @2 - i32.const 0 - local.set $2 - loop $label$12 ;; label = @3 - block $label$13 (result i32) ;; label = @4 - block $label$14 ;; label = @5 - call $7 - i32.eqz - br_if 0 (;@5;) - call $7 - i32.eqz - br_if 0 (;@5;) - local.get $0 - i32.const 17 - i32.add - br 1 (;@4;) - end - local.get $0 - i32.const 19 - i32.add - end - local.set $0 - block $label$15 ;; label = @4 - block $label$16 ;; label = @5 - call $7 - br_if 0 (;@5;) - call $7 - br_if 0 (;@5;) - br 1 (;@4;) - end - local.get $0 - i32.const 23 - i32.add - local.set $0 - end - local.get $2 - i32.const 1 - i32.add - local.tee $2 - local.get $3 - i32.lt_s - br_if 0 (;@3;) - end - local.get $1 - i32.const 1 - i32.add - local.tee $1 - i32.const 27000 - i32.ne - br_if 0 (;@2;) - end - i32.const 1152 - call $35 - drop - local.get $4 - global.set $global$1 - local.get $0 - end - ) - (func $9 (;22;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $1 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $1 - local.tee $2 - local.get $0 - i32.load offset=60 - i32.store - i32.const 6 - local.get $2 - call $fimport$8 - call $11 - local.set $0 - local.get $1 - global.set $global$1 - local.get $0 - end - ) - (func $10 (;23;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 32 - i32.add - global.set $global$1 - local.get $4 - local.tee $3 - local.get $0 - i32.load offset=60 - i32.store - local.get $3 - i32.const 0 - i32.store offset=4 - local.get $3 - local.get $1 - i32.store offset=8 - local.get $3 - local.get $4 - i32.const 20 - i32.add - local.tee $0 - i32.store offset=12 - local.get $3 - local.get $2 - i32.store offset=16 - i32.const 140 - local.get $3 - call $fimport$14 - call $11 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $0 - i32.const -1 - i32.store - i32.const -1 - end - else - local.get $0 - i32.load - end - local.set $0 - local.get $4 - global.set $global$1 - local.get $0 - end - ) - (func $11 (;24;) (type $1) (param $0 i32) (result i32) - local.get $0 - i32.const -4096 - i32.gt_u - if (result i32) ;; label = @1 - block (result i32) ;; label = @2 - call $12 - i32.const 0 - local.get $0 - i32.sub - i32.store - i32.const -1 - end - else - local.get $0 - end - ) - (func $12 (;25;) (type $3) (result i32) - i32.const 3632 - ) - (func $13 (;26;) (type $2) (param $0 i32) - nop - ) - (func $14 (;27;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 80 - i32.add - global.set $global$1 - local.get $4 - local.set $3 - local.get $4 - i32.const 12 - i32.add - local.set $5 - local.get $0 - i32.const 3 - i32.store offset=36 - local.get $0 - i32.load - i32.const 64 - i32.and - i32.eqz - if ;; label = @2 - block ;; label = @3 - local.get $3 - local.get $0 - i32.load offset=60 - i32.store - local.get $3 - i32.const 21505 - i32.store offset=4 - local.get $3 - local.get $5 - i32.store offset=8 - i32.const 54 - local.get $3 - call $fimport$13 - if ;; label = @4 - local.get $0 - i32.const -1 - i32.store8 offset=75 - end - end - end - local.get $0 - local.get $1 - local.get $2 - call $15 - local.set $0 - local.get $4 - global.set $global$1 - local.get $0 - end - ) - (func $15 (;28;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $8 - global.get $global$1 - i32.const 48 - i32.add - global.set $global$1 - local.get $8 - i32.const 16 - i32.add - local.set $9 - local.get $8 - local.set $10 - local.get $8 - i32.const 32 - i32.add - local.tee $3 - local.get $0 - i32.const 28 - i32.add - local.tee $6 - i32.load - local.tee $4 - i32.store - local.get $3 - local.get $0 - i32.const 20 - i32.add - local.tee $11 - i32.load - local.get $4 - i32.sub - local.tee $5 - i32.store offset=4 - local.get $3 - local.get $1 - i32.store offset=8 - local.get $3 - local.get $2 - i32.store offset=12 - local.get $0 - i32.const 60 - i32.add - local.set $13 - local.get $0 - i32.const 44 - i32.add - local.set $14 - local.get $3 - local.set $1 - i32.const 2 - local.set $4 - local.get $5 - local.get $2 - i32.add - local.set $12 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - loop $label$5 ;; label = @5 - i32.const 3588 - i32.load - if ;; label = @6 - block ;; label = @7 - i32.const 1 - local.get $0 - call $fimport$9 - local.get $10 - local.get $13 - i32.load - i32.store - local.get $10 - local.get $1 - i32.store offset=4 - local.get $10 - local.get $4 - i32.store offset=8 - i32.const 146 - local.get $10 - call $fimport$15 - call $11 - local.set $3 - i32.const 0 - call $fimport$7 - end - else - block ;; label = @7 - local.get $9 - local.get $13 - i32.load - i32.store - local.get $9 - local.get $1 - i32.store offset=4 - local.get $9 - local.get $4 - i32.store offset=8 - i32.const 146 - local.get $9 - call $fimport$15 - call $11 - local.set $3 - end - end - local.get $12 - local.get $3 - i32.eq - br_if 1 (;@4;) - local.get $3 - i32.const 0 - i32.lt_s - br_if 2 (;@3;) - local.get $3 - local.get $1 - i32.load offset=4 - local.tee $5 - i32.gt_u - if (result i32) ;; label = @6 - block (result i32) ;; label = @7 - local.get $6 - local.get $14 - i32.load - local.tee $7 - i32.store - local.get $11 - local.get $7 - i32.store - local.get $1 - i32.load offset=12 - local.set $7 - local.get $1 - i32.const 8 - i32.add - local.set $1 - local.get $4 - i32.const -1 - i32.add - local.set $4 - local.get $3 - local.get $5 - i32.sub - end - else - local.get $4 - i32.const 2 - i32.eq - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - local.get $6 - local.get $6 - i32.load - local.get $3 - i32.add - i32.store - local.get $5 - local.set $7 - i32.const 2 - local.set $4 - local.get $3 - end - else - block (result i32) ;; label = @8 - local.get $5 - local.set $7 - local.get $3 - end - end - end - local.set $5 - local.get $1 - local.get $1 - i32.load - local.get $5 - i32.add - i32.store - local.get $1 - local.get $7 - local.get $5 - i32.sub - i32.store offset=4 - local.get $12 - local.get $3 - i32.sub - local.set $12 - br 0 (;@5;) - end - end - local.get $0 - local.get $14 - i32.load - local.tee $1 - local.get $0 - i32.load offset=48 - i32.add - i32.store offset=16 - local.get $6 - local.get $1 - i32.store - local.get $11 - local.get $1 - i32.store - br 1 (;@2;) - end - local.get $0 - i32.const 0 - i32.store offset=16 - local.get $6 - i32.const 0 - i32.store - local.get $11 - i32.const 0 - i32.store - local.get $0 - local.get $0 - i32.load - i32.const 32 - i32.or - i32.store - local.get $4 - i32.const 2 - i32.eq - if (result i32) ;; label = @3 - i32.const 0 - else - local.get $2 - local.get $1 - i32.load offset=4 - i32.sub - end - local.set $2 - end - local.get $8 - global.set $global$1 - local.get $2 - end - ) - (func $16 (;29;) (type $2) (param $0 i32) - local.get $0 - i32.load offset=68 - i32.eqz - if ;; label = @1 - local.get $0 - call $13 - end - ) - (func $17 (;30;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $1 - i32.const 255 - i32.and - local.set $5 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - local.get $2 - i32.const 0 - i32.ne - local.tee $4 - local.get $0 - i32.const 3 - i32.and - i32.const 0 - i32.ne - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $1 - i32.const 255 - i32.and - local.set $4 - local.get $2 - local.set $3 - local.get $0 - local.set $2 - loop $label$6 ;; label = @7 - local.get $2 - i32.load8_s - local.get $4 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $3 - local.set $0 - br 6 (;@3;) - end - end - local.get $3 - i32.const -1 - i32.add - local.tee $3 - i32.const 0 - i32.ne - local.tee $0 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 3 - i32.and - i32.const 0 - i32.ne - i32.and - br_if 0 (;@7;) - br 3 (;@4;) - end - end - else - block ;; label = @6 - local.get $2 - local.set $3 - local.get $0 - local.set $2 - local.get $4 - local.set $0 - end - end - end - local.get $0 - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $0 - br 2 (;@3;) - end - else - i32.const 0 - local.set $0 - end - br 1 (;@2;) - end - local.get $2 - i32.load8_s - local.get $1 - i32.const 255 - i32.and - local.tee $1 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.ne - if ;; label = @3 - block ;; label = @4 - local.get $5 - i32.const 16843009 - i32.mul - local.set $3 - block $label$12 ;; label = @5 - block $label$13 ;; label = @6 - local.get $0 - i32.const 3 - i32.le_u - br_if 0 (;@6;) - loop $label$14 ;; label = @7 - local.get $2 - i32.load - local.get $3 - i32.xor - local.tee $4 - i32.const -2139062144 - i32.and - i32.const -2139062144 - i32.xor - local.get $4 - i32.const -16843009 - i32.add - i32.and - i32.eqz - if ;; label = @8 - block ;; label = @9 - local.get $2 - i32.const 4 - i32.add - local.set $2 - local.get $0 - i32.const -4 - i32.add - local.tee $0 - i32.const 3 - i32.gt_u - br_if 2 (;@7;) - br 3 (;@6;) - end - end - end - br 1 (;@5;) - end - local.get $0 - i32.eqz - if ;; label = @6 - block ;; label = @7 - i32.const 0 - local.set $0 - br 5 (;@2;) - end - end - end - loop $label$17 ;; label = @5 - local.get $2 - i32.load8_s - local.get $1 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eq - br_if 3 (;@2;) - local.get $2 - i32.const 1 - i32.add - local.set $2 - local.get $0 - i32.const -1 - i32.add - local.tee $0 - br_if 0 (;@5;) - i32.const 0 - local.set $0 - end - end - end - end - local.get $0 - if (result i32) ;; label = @2 - local.get $2 - else - i32.const 0 - end - end - ) - (func $18 (;31;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 224 - i32.add - global.set $global$1 - local.get $4 - i32.const 136 - i32.add - local.set $5 - local.get $4 - i32.const 80 - i32.add - local.tee $3 - i64.const 0 - i64.store align=4 - local.get $3 - i64.const 0 - i64.store offset=8 align=4 - local.get $3 - i64.const 0 - i64.store offset=16 align=4 - local.get $3 - i64.const 0 - i64.store offset=24 align=4 - local.get $3 - i64.const 0 - i64.store offset=32 align=4 - local.get $4 - i32.const 120 - i32.add - local.tee $6 - local.get $2 - i32.load - i32.store - i32.const 0 - local.get $1 - local.get $6 - local.get $4 - local.tee $2 - local.get $3 - call $19 - i32.const 0 - i32.lt_s - if ;; label = @2 - i32.const -1 - local.set $1 - else - block ;; label = @3 - local.get $0 - i32.load offset=76 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @4 - local.get $0 - call $20 - else - i32.const 0 - end - local.set $12 - local.get $0 - i32.load - local.set $7 - local.get $0 - i32.load8_s offset=74 - i32.const 1 - i32.lt_s - if ;; label = @4 - local.get $0 - local.get $7 - i32.const -33 - i32.and - i32.store - end - local.get $0 - i32.const 48 - i32.add - local.tee $8 - i32.load - if ;; label = @4 - local.get $0 - local.get $1 - local.get $6 - local.get $2 - local.get $3 - call $19 - local.set $1 - else - block ;; label = @5 - local.get $0 - i32.const 44 - i32.add - local.tee $9 - i32.load - local.set $10 - local.get $9 - local.get $5 - i32.store - local.get $0 - i32.const 28 - i32.add - local.tee $13 - local.get $5 - i32.store - local.get $0 - i32.const 20 - i32.add - local.tee $11 - local.get $5 - i32.store - local.get $8 - i32.const 80 - i32.store - local.get $0 - i32.const 16 - i32.add - local.tee $14 - local.get $5 - i32.const 80 - i32.add - i32.store - local.get $0 - local.get $1 - local.get $6 - local.get $2 - local.get $3 - call $19 - local.set $1 - local.get $10 - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 0 - i32.const 0 - local.get $0 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - drop - local.get $11 - i32.load - i32.eqz - if ;; label = @8 - i32.const -1 - local.set $1 - end - local.get $9 - local.get $10 - i32.store - local.get $8 - i32.const 0 - i32.store - local.get $14 - i32.const 0 - i32.store - local.get $13 - i32.const 0 - i32.store - local.get $11 - i32.const 0 - i32.store - end - end - end - end - local.get $0 - local.get $0 - i32.load - local.tee $2 - local.get $7 - i32.const 32 - i32.and - i32.or - i32.store - local.get $12 - if ;; label = @4 - local.get $0 - call $13 - end - local.get $2 - i32.const 32 - i32.and - if ;; label = @4 - i32.const -1 - local.set $1 - end - end - end - local.get $4 - global.set $global$1 - local.get $1 - end - ) - (func $19 (;32;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) - (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) (local $47 i32) (local $48 i32) (local $49 i32) (local $50 i64) (local $51 i64) (local $52 f64) (local $53 f64) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $23 - global.get $global$1 - i32.const 624 - i32.add - global.set $global$1 - local.get $23 - i32.const 16 - i32.add - local.set $20 - local.get $23 - local.set $16 - local.get $23 - i32.const 528 - i32.add - local.set $36 - local.get $0 - i32.const 0 - i32.ne - local.set $30 - local.get $23 - i32.const 536 - i32.add - local.tee $17 - i32.const 40 - i32.add - local.tee $21 - local.set $38 - local.get $17 - i32.const 39 - i32.add - local.set $39 - local.get $23 - i32.const 8 - i32.add - local.tee $37 - i32.const 4 - i32.add - local.set $42 - i32.const 0 - local.get $23 - i32.const 588 - i32.add - local.tee $19 - local.tee $27 - i32.sub - local.set $43 - local.get $23 - i32.const 576 - i32.add - local.tee $17 - i32.const 12 - i32.add - local.set $33 - local.get $17 - i32.const 11 - i32.add - local.set $40 - local.get $33 - local.tee $28 - local.get $27 - i32.sub - local.set $44 - i32.const -2 - local.get $27 - i32.sub - local.set $45 - local.get $28 - i32.const 2 - i32.add - local.set $46 - local.get $23 - i32.const 24 - i32.add - local.tee $47 - i32.const 288 - i32.add - local.set $48 - local.get $19 - i32.const 9 - i32.add - local.tee $31 - local.set $41 - local.get $19 - i32.const 8 - i32.add - local.set $34 - i32.const 0 - local.set $15 - i32.const 0 - local.set $10 - i32.const 0 - local.set $17 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - loop $label$4 ;; label = @4 - block $label$5 ;; label = @5 - local.get $15 - i32.const -1 - i32.gt_s - if ;; label = @6 - local.get $10 - i32.const 2147483647 - local.get $15 - i32.sub - i32.gt_s - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - call $12 - i32.const 75 - i32.store - i32.const -1 - end - else - local.get $10 - local.get $15 - i32.add - end - local.set $15 - end - local.get $1 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eqz - br_if 2 (;@3;) - local.get $1 - local.set $11 - block $label$9 ;; label = @6 - block $label$10 ;; label = @7 - loop $label$11 ;; label = @8 - block $label$12 ;; label = @9 - block $label$13 ;; label = @10 - block $label$14 ;; label = @11 - block $label$15 ;; label = @12 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 0 - i32.sub - br_table 1 (;@11;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 0 (;@12;) 2 (;@10;) - end - local.get $11 - local.set $5 - br 4 (;@7;) - end - local.get $11 - local.set $5 - br 1 (;@9;) - end - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.set $5 - br 1 (;@8;) - end - end - br 1 (;@6;) - end - loop $label$16 ;; label = @7 - local.get $5 - i32.load8_s offset=1 - i32.const 37 - i32.ne - br_if 1 (;@6;) - local.get $11 - i32.const 1 - i32.add - local.set $11 - local.get $5 - i32.const 2 - i32.add - local.tee $5 - i32.load8_s - i32.const 37 - i32.eq - br_if 0 (;@7;) - end - end - local.get $11 - local.get $1 - i32.sub - local.set $10 - local.get $30 - if ;; label = @6 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @7 - local.get $1 - local.get $10 - local.get $0 - call $21 - drop - end - end - local.get $10 - if ;; label = @6 - block ;; label = @7 - local.get $5 - local.set $1 - br 3 (;@4;) - end - end - local.get $5 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $10 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $9 - i32.const 10 - i32.lt_u - if (result i32) ;; label = @6 - block (result i32) ;; label = @7 - local.get $5 - i32.const 3 - i32.add - local.set $10 - local.get $5 - i32.load8_s offset=2 - i32.const 36 - i32.eq - local.tee $12 - if ;; label = @8 - local.get $10 - local.set $11 - end - local.get $12 - if ;; label = @8 - i32.const 1 - local.set $17 - end - local.get $11 - i32.load8_s - local.set $5 - local.get $12 - i32.eqz - if ;; label = @8 - i32.const -1 - local.set $9 - end - local.get $17 - end - else - block (result i32) ;; label = @7 - local.get $10 - local.set $5 - i32.const -1 - local.set $9 - local.get $17 - end - end - local.set $10 - block $label$25 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - local.tee $12 - i32.const 32 - i32.lt_u - if ;; label = @7 - block ;; label = @8 - i32.const 0 - local.set $17 - loop $label$27 ;; label = @9 - i32.const 1 - local.get $12 - i32.shl - i32.const 75913 - i32.and - i32.eqz - br_if 3 (;@6;) - i32.const 1 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - i32.shl - local.get $17 - i32.or - local.set $17 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - local.tee $12 - i32.const 32 - i32.lt_u - br_if 0 (;@9;) - end - end - else - i32.const 0 - local.set $17 - end - end - block $label$29 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 42 - i32.eq - if ;; label = @7 - block ;; label = @8 - block $label$31 (result i32) ;; label = @9 - block $label$32 ;; label = @10 - local.get $11 - i32.const 1 - i32.add - local.tee $7 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $12 - i32.const 10 - i32.ge_u - br_if 0 (;@10;) - local.get $11 - i32.load8_s offset=2 - i32.const 36 - i32.ne - br_if 0 (;@10;) - local.get $4 - local.get $12 - i32.const 2 - i32.shl - i32.add - i32.const 10 - i32.store - i32.const 1 - local.set $8 - local.get $3 - local.get $7 - i32.load8_s - i32.const -48 - i32.add - i32.const 3 - i32.shl - i32.add - i64.load - i32.wrap_i64 - local.set $10 - local.get $11 - i32.const 3 - i32.add - br 1 (;@9;) - end - local.get $10 - if ;; label = @10 - block ;; label = @11 - i32.const -1 - local.set $15 - br 6 (;@5;) - end - end - local.get $30 - i32.eqz - if ;; label = @10 - block ;; label = @11 - local.get $17 - local.set $12 - i32.const 0 - local.set $17 - local.get $7 - local.set $11 - i32.const 0 - local.set $10 - br 5 (;@6;) - end - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $11 - i32.load - local.set $10 - local.get $2 - local.get $11 - i32.const 4 - i32.add - i32.store - i32.const 0 - local.set $8 - local.get $7 - end - local.set $11 - local.get $17 - i32.const 8192 - i32.or - local.set $12 - i32.const 0 - local.get $10 - i32.sub - local.set $7 - local.get $11 - i32.load8_s - local.set $5 - local.get $10 - i32.const 0 - i32.lt_s - local.tee $6 - i32.eqz - if ;; label = @9 - local.get $17 - local.set $12 - end - local.get $8 - local.set $17 - local.get $6 - if ;; label = @9 - local.get $7 - local.set $10 - end - end - else - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $12 - i32.const 10 - i32.lt_u - if ;; label = @8 - block ;; label = @9 - i32.const 0 - local.set $7 - local.get $12 - local.set $5 - loop $label$39 ;; label = @10 - local.get $7 - i32.const 10 - i32.mul - local.get $5 - i32.add - local.set $7 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $12 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - br_if 0 (;@10;) - end - local.get $7 - i32.const 0 - i32.lt_s - if ;; label = @10 - block ;; label = @11 - i32.const -1 - local.set $15 - br 6 (;@5;) - end - else - block ;; label = @11 - local.get $12 - local.set $5 - local.get $17 - local.set $12 - local.get $10 - local.set $17 - local.get $7 - local.set $10 - end - end - end - else - block ;; label = @9 - local.get $17 - local.set $12 - local.get $10 - local.set $17 - i32.const 0 - local.set $10 - end - end - end - end - block $label$43 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 46 - i32.eq - if ;; label = @7 - block ;; label = @8 - local.get $11 - i32.const 1 - i32.add - local.tee $7 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 42 - i32.ne - if ;; label = @9 - block ;; label = @10 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - if ;; label = @11 - block ;; label = @12 - local.get $7 - local.set $11 - i32.const 0 - local.set $7 - end - else - block ;; label = @12 - i32.const 0 - local.set $5 - local.get $7 - local.set $11 - br 6 (;@6;) - end - end - loop $label$48 ;; label = @11 - local.get $7 - i32.const 10 - i32.mul - local.get $5 - i32.add - local.set $5 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - i32.const -48 - i32.add - local.tee $8 - i32.const 10 - i32.ge_u - br_if 5 (;@6;) - local.get $5 - local.set $7 - local.get $8 - local.set $5 - br 0 (;@11;) - end - end - end - local.get $11 - i32.const 2 - i32.add - local.tee $7 - i32.load8_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - if ;; label = @9 - local.get $11 - i32.load8_s offset=3 - i32.const 36 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.get $5 - i32.const 2 - i32.shl - i32.add - i32.const 10 - i32.store - local.get $3 - local.get $7 - i32.load8_s - i32.const -48 - i32.add - i32.const 3 - i32.shl - i32.add - i64.load - i32.wrap_i64 - local.set $5 - local.get $11 - i32.const 4 - i32.add - local.set $11 - br 5 (;@6;) - end - end - end - local.get $17 - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - end - local.get $30 - if (result i32) ;; label = @9 - block (result i32) ;; label = @10 - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $11 - i32.load - local.set $5 - local.get $2 - local.get $11 - i32.const 4 - i32.add - i32.store - local.get $7 - end - else - block (result i32) ;; label = @10 - i32.const 0 - local.set $5 - local.get $7 - end - end - local.set $11 - end - else - i32.const -1 - local.set $5 - end - end - local.get $11 - local.set $7 - i32.const 0 - local.set $8 - loop $label$55 ;; label = @6 - local.get $7 - i32.load8_s - i32.const -65 - i32.add - local.tee $6 - i32.const 57 - i32.gt_u - if ;; label = @7 - block ;; label = @8 - i32.const -1 - local.set $15 - br 3 (;@5;) - end - end - local.get $7 - i32.const 1 - i32.add - local.set $11 - local.get $8 - i32.const 58 - i32.mul - i32.const 1155 - i32.add - local.get $6 - i32.add - i32.load8_s - local.tee $13 - i32.const 255 - i32.and - local.tee $6 - i32.const -1 - i32.add - i32.const 8 - i32.lt_u - if ;; label = @7 - block ;; label = @8 - local.get $11 - local.set $7 - local.get $6 - local.set $8 - br 2 (;@6;) - end - end - end - local.get $13 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eqz - if ;; label = @6 - block ;; label = @7 - i32.const -1 - local.set $15 - br 2 (;@5;) - end - end - local.get $9 - i32.const -1 - i32.gt_s - local.set $14 - block $label$59 ;; label = @6 - block $label$60 ;; label = @7 - local.get $13 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 19 - i32.eq - if ;; label = @8 - local.get $14 - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - else - br 2 (;@7;) - end - else - block ;; label = @9 - local.get $14 - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.get $9 - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store - local.get $16 - local.get $3 - local.get $9 - i32.const 3 - i32.shl - i32.add - i64.load - i64.store - br 4 (;@7;) - end - end - local.get $30 - i32.eqz - if ;; label = @10 - block ;; label = @11 - i32.const 0 - local.set $15 - br 6 (;@5;) - end - end - local.get $16 - local.get $6 - local.get $2 - call $22 - end - end - br 1 (;@6;) - end - local.get $30 - i32.eqz - if ;; label = @7 - block ;; label = @8 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 4 (;@4;) - end - end - end - local.get $7 - i32.load8_s - local.tee $7 - i32.const -33 - i32.and - local.set $9 - local.get $8 - i32.const 0 - i32.ne - local.get $7 - i32.const 15 - i32.and - i32.const 3 - i32.eq - i32.and - i32.eqz - if ;; label = @6 - local.get $7 - local.set $9 - end - local.get $12 - i32.const -65537 - i32.and - local.set $7 - local.get $12 - i32.const 8192 - i32.and - if ;; label = @6 - local.get $7 - local.set $12 - end - block $label$70 ;; label = @6 - block $label$71 ;; label = @7 - block $label$72 ;; label = @8 - block $label$73 ;; label = @9 - block $label$74 ;; label = @10 - block $label$75 ;; label = @11 - block $label$76 ;; label = @12 - block $label$77 ;; label = @13 - block $label$78 ;; label = @14 - block $label$79 ;; label = @15 - block $label$80 ;; label = @16 - block $label$81 ;; label = @17 - block $label$82 ;; label = @18 - block $label$83 ;; label = @19 - block $label$84 ;; label = @20 - block $label$85 ;; label = @21 - block $label$86 ;; label = @22 - block $label$87 ;; label = @23 - block $label$88 ;; label = @24 - block $label$89 ;; label = @25 - local.get $9 - i32.const 65 - i32.sub - br_table 11 (;@14;) 12 (;@13;) 9 (;@16;) 12 (;@13;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 10 (;@15;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 11 (;@14;) 12 (;@13;) 6 (;@19;) 4 (;@21;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 4 (;@21;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 7 (;@18;) 0 (;@25;) 3 (;@22;) 1 (;@24;) 12 (;@13;) 12 (;@13;) 8 (;@17;) 12 (;@13;) 5 (;@20;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) - end - block $label$90 ;; label = @25 - block $label$91 ;; label = @26 - block $label$92 ;; label = @27 - block $label$93 ;; label = @28 - block $label$94 ;; label = @29 - block $label$95 ;; label = @30 - block $label$96 ;; label = @31 - block $label$97 ;; label = @32 - local.get $8 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 0 - i32.sub - br_table 0 (;@32;) 1 (;@31;) 2 (;@30;) 3 (;@29;) 4 (;@28;) 7 (;@25;) 5 (;@27;) 6 (;@26;) 7 (;@25;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 27 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 26 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i64.extend_i32_s - i64.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 25 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store16 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 24 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store8 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 23 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 22 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i64.extend_i32_s - i64.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 21 (;@4;) - end - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 20 (;@4;) - end - local.get $12 - i32.const 8 - i32.or - local.set $12 - local.get $5 - i32.const 8 - i32.le_u - if ;; label = @24 - i32.const 8 - local.set $5 - end - i32.const 120 - local.set $9 - br 11 (;@12;) - end - br 10 (;@12;) - end - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.eq - if ;; label = @22 - local.get $21 - local.set $7 - else - block ;; label = @23 - local.get $21 - local.set $1 - loop $label$101 ;; label = @24 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $50 - i64.const 7 - i64.and - i64.const 48 - i64.or - i64.store8 - local.get $50 - i64.const 3 - i64.shr_u - local.tee $50 - i64.const 0 - i64.ne - br_if 0 (;@24;) - local.get $1 - local.set $7 - end - end - end - local.get $12 - i32.const 8 - i32.and - if ;; label = @22 - block ;; label = @23 - local.get $38 - local.get $7 - i32.sub - local.tee $1 - i32.const 1 - i32.add - local.set $8 - local.get $5 - local.get $1 - i32.le_s - if ;; label = @24 - local.get $8 - local.set $5 - end - i32.const 0 - local.set $6 - i32.const 1635 - local.set $8 - br 16 (;@7;) - end - else - block ;; label = @23 - i32.const 0 - local.set $6 - i32.const 1635 - local.set $8 - br 16 (;@7;) - end - end - end - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.lt_s - if ;; label = @21 - block ;; label = @22 - local.get $16 - i64.const 0 - local.get $50 - i64.sub - local.tee $50 - i64.store - i32.const 1 - local.set $6 - i32.const 1635 - local.set $8 - br 11 (;@11;) - end - end - local.get $12 - i32.const 2048 - i32.and - if ;; label = @21 - block ;; label = @22 - i32.const 1 - local.set $6 - i32.const 1636 - local.set $8 - br 11 (;@11;) - end - else - block ;; label = @22 - local.get $12 - i32.const 1 - i32.and - local.tee $1 - local.set $6 - local.get $1 - if (result i32) ;; label = @23 - i32.const 1637 - else - i32.const 1635 - end - local.set $8 - br 11 (;@11;) - end - end - end - local.get $16 - i64.load - local.set $50 - i32.const 0 - local.set $6 - i32.const 1635 - local.set $8 - br 8 (;@11;) - end - local.get $39 - local.get $16 - i64.load - i64.store8 - local.get $39 - local.set $1 - local.get $7 - local.set $12 - i32.const 1 - local.set $7 - i32.const 0 - local.set $6 - i32.const 1635 - local.set $8 - local.get $21 - local.set $5 - br 12 (;@6;) - end - call $12 - i32.load - call $24 - local.set $1 - br 7 (;@10;) - end - local.get $16 - i32.load - local.tee $1 - i32.eqz - if ;; label = @17 - i32.const 1645 - local.set $1 - end - br 6 (;@10;) - end - local.get $37 - local.get $16 - i64.load - i64.store32 - local.get $42 - i32.const 0 - i32.store - local.get $16 - local.get $37 - i32.store - local.get $37 - local.set $7 - i32.const -1 - local.set $6 - br 6 (;@9;) - end - local.get $16 - i32.load - local.set $7 - local.get $5 - if ;; label = @15 - block ;; label = @16 - local.get $5 - local.set $6 - br 7 (;@9;) - end - else - block ;; label = @16 - local.get $0 - i32.const 32 - local.get $10 - i32.const 0 - local.get $12 - call $25 - i32.const 0 - local.set $1 - br 8 (;@8;) - end - end - end - local.get $16 - f64.load - local.set $52 - local.get $20 - i32.const 0 - i32.store - local.get $52 - i64.reinterpret_f64 - i64.const 0 - i64.lt_s - if (result i32) ;; label = @14 - block (result i32) ;; label = @15 - i32.const 1 - local.set $24 - local.get $52 - f64.neg - local.set $52 - i32.const 1652 - end - else - block (result i32) ;; label = @15 - local.get $12 - i32.const 1 - i32.and - local.set $1 - local.get $12 - i32.const 2048 - i32.and - if (result i32) ;; label = @16 - block (result i32) ;; label = @17 - i32.const 1 - local.set $24 - i32.const 1655 - end - else - block (result i32) ;; label = @17 - local.get $1 - local.set $24 - local.get $1 - if (result i32) ;; label = @18 - i32.const 1658 - else - i32.const 1653 - end - end - end - end - end - local.set $26 - block $label$119 ;; label = @14 - local.get $52 - i64.reinterpret_f64 - i64.const 9218868437227405312 - i64.and - i64.const 9218868437227405312 - i64.lt_u - if ;; label = @15 - block ;; label = @16 - local.get $52 - local.get $20 - call $27 - f64.const 0x1p+1 (;=2;) - f64.mul - local.tee $52 - f64.const 0x0p+0 (;=0;) - f64.ne - local.tee $1 - if ;; label = @17 - local.get $20 - local.get $20 - i32.load - i32.const -1 - i32.add - i32.store - end - local.get $9 - i32.const 32 - i32.or - local.tee $22 - i32.const 97 - i32.eq - if ;; label = @17 - block ;; label = @18 - local.get $26 - i32.const 9 - i32.add - local.set $1 - local.get $9 - i32.const 32 - i32.and - local.tee $6 - if ;; label = @19 - local.get $1 - local.set $26 - end - local.get $5 - i32.const 11 - i32.gt_u - i32.const 12 - local.get $5 - i32.sub - local.tee $1 - i32.eqz - i32.or - i32.eqz - if ;; label = @19 - block ;; label = @20 - f64.const 0x1p+3 (;=8;) - local.set $53 - loop $label$125 ;; label = @21 - local.get $53 - f64.const 0x1p+4 (;=16;) - f64.mul - local.set $53 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - br_if 0 (;@21;) - end - local.get $26 - i32.load8_s - i32.const 45 - i32.eq - if (result f64) ;; label = @21 - local.get $53 - local.get $52 - f64.neg - local.get $53 - f64.sub - f64.add - f64.neg - else - local.get $52 - local.get $53 - f64.add - local.get $53 - f64.sub - end - local.set $52 - end - end - i32.const 0 - local.get $20 - i32.load - local.tee $7 - i32.sub - local.set $1 - local.get $7 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @19 - local.get $1 - else - local.get $7 - end - i64.extend_i32_s - local.get $33 - call $23 - local.tee $1 - local.get $33 - i32.eq - if ;; label = @19 - block ;; label = @20 - local.get $40 - i32.const 48 - i32.store8 - local.get $40 - local.set $1 - end - end - local.get $24 - i32.const 2 - i32.or - local.set $13 - local.get $1 - i32.const -1 - i32.add - local.get $7 - i32.const 31 - i32.shr_s - i32.const 2 - i32.and - i32.const 43 - i32.add - i32.store8 - local.get $1 - i32.const -2 - i32.add - local.tee $8 - local.get $9 - i32.const 15 - i32.add - i32.store8 - local.get $5 - i32.const 1 - i32.lt_s - local.set $9 - local.get $12 - i32.const 8 - i32.and - i32.eqz - local.set $14 - local.get $19 - local.set $1 - loop $label$131 ;; label = @19 - local.get $1 - local.get $52 - i32.trunc_f64_s - local.tee $7 - i32.const 1619 - i32.add - i32.load8_u - local.get $6 - i32.or - i32.store8 - local.get $52 - local.get $7 - f64.convert_i32_s - f64.sub - f64.const 0x1p+4 (;=16;) - f64.mul - local.set $52 - block $label$132 (result i32) ;; label = @20 - local.get $1 - i32.const 1 - i32.add - local.tee $7 - local.get $27 - i32.sub - i32.const 1 - i32.eq - if (result i32) ;; label = @21 - block (result i32) ;; label = @22 - local.get $7 - local.get $14 - local.get $9 - local.get $52 - f64.const 0x0p+0 (;=0;) - f64.eq - i32.and - i32.and - br_if 2 (;@20;) - drop - local.get $7 - i32.const 46 - i32.store8 - local.get $1 - i32.const 2 - i32.add - end - else - local.get $7 - end - end - local.set $1 - local.get $52 - f64.const 0x0p+0 (;=0;) - f64.ne - br_if 0 (;@19;) - end - local.get $46 - local.get $5 - i32.add - local.get $8 - local.tee $7 - i32.sub - local.set $6 - local.get $44 - local.get $7 - i32.sub - local.get $1 - i32.add - local.set $9 - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - i32.const 0 - i32.ne - local.get $45 - local.get $1 - i32.add - local.get $5 - i32.lt_s - i32.and - if (result i32) ;; label = @19 - local.get $6 - else - local.get $9 - local.tee $6 - end - local.get $13 - i32.add - local.tee $5 - local.get $12 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $26 - local.get $13 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $5 - local.get $12 - i32.const 65536 - i32.xor - call $25 - local.get $1 - local.get $27 - i32.sub - local.set $1 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $19 - local.get $1 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $6 - local.get $1 - local.get $28 - local.get $7 - i32.sub - local.tee $1 - i32.add - i32.sub - i32.const 0 - i32.const 0 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $8 - local.get $1 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $5 - local.get $10 - i32.ge_s - if ;; label = @19 - local.get $5 - local.set $10 - end - br 4 (;@14;) - end - end - local.get $1 - if ;; label = @17 - block ;; label = @18 - local.get $20 - local.get $20 - i32.load - i32.const -28 - i32.add - local.tee $6 - i32.store - local.get $52 - f64.const 0x1p+28 (;=268435456;) - f64.mul - local.set $52 - end - else - local.get $20 - i32.load - local.set $6 - end - local.get $6 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @17 - local.get $47 - else - local.get $48 - end - local.tee $7 - local.set $8 - loop $label$145 ;; label = @17 - local.get $8 - local.get $52 - i32.trunc_f64_s - local.tee $1 - i32.store - local.get $8 - i32.const 4 - i32.add - local.set $8 - local.get $52 - local.get $1 - f64.convert_i32_u - f64.sub - f64.const 0x1.dcd65p+29 (;=1000000000;) - f64.mul - local.tee $52 - f64.const 0x0p+0 (;=0;) - f64.ne - br_if 0 (;@17;) - end - local.get $6 - i32.const 0 - i32.gt_s - if ;; label = @17 - block ;; label = @18 - local.get $7 - local.set $1 - loop $label$147 ;; label = @19 - local.get $6 - i32.const 29 - i32.gt_s - if (result i32) ;; label = @20 - i32.const 29 - else - local.get $6 - end - local.set $14 - block $label$150 ;; label = @20 - local.get $8 - i32.const -4 - i32.add - local.tee $6 - local.get $1 - i32.ge_u - if ;; label = @21 - block ;; label = @22 - local.get $14 - i64.extend_i32_u - local.set $50 - i32.const 0 - local.set $13 - loop $label$152 ;; label = @23 - local.get $6 - local.get $6 - i32.load - i64.extend_i32_u - local.get $50 - i64.shl - local.get $13 - i64.extend_i32_u - i64.add - local.tee $51 - i64.const 1000000000 - i64.rem_u - i64.store32 - local.get $51 - i64.const 1000000000 - i64.div_u - i32.wrap_i64 - local.set $13 - local.get $6 - i32.const -4 - i32.add - local.tee $6 - local.get $1 - i32.ge_u - br_if 0 (;@23;) - end - local.get $13 - i32.eqz - br_if 2 (;@20;) - local.get $1 - i32.const -4 - i32.add - local.tee $1 - local.get $13 - i32.store - end - end - end - loop $label$153 ;; label = @20 - local.get $8 - local.get $1 - i32.gt_u - if ;; label = @21 - local.get $8 - i32.const -4 - i32.add - local.tee $6 - i32.load - i32.eqz - if ;; label = @22 - block ;; label = @23 - local.get $6 - local.set $8 - br 3 (;@20;) - end - end - end - end - local.get $20 - local.get $20 - i32.load - local.get $14 - i32.sub - local.tee $6 - i32.store - local.get $6 - i32.const 0 - i32.gt_s - br_if 0 (;@19;) - end - end - else - local.get $7 - local.set $1 - end - local.get $5 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @17 - i32.const 6 - else - local.get $5 - end - local.set $18 - local.get $6 - i32.const 0 - i32.lt_s - if ;; label = @17 - block ;; label = @18 - local.get $18 - i32.const 25 - i32.add - i32.const 9 - i32.div_s - i32.const 1 - i32.add - local.set $14 - local.get $22 - i32.const 102 - i32.eq - local.set $25 - local.get $8 - local.set $5 - loop $label$160 ;; label = @19 - i32.const 0 - local.get $6 - i32.sub - local.tee $13 - i32.const 9 - i32.gt_s - if ;; label = @20 - i32.const 9 - local.set $13 - end - block $label$162 ;; label = @20 - local.get $1 - local.get $5 - i32.lt_u - if ;; label = @21 - block ;; label = @22 - i32.const 1 - local.get $13 - i32.shl - i32.const -1 - i32.add - local.set $29 - i32.const 1000000000 - local.get $13 - i32.shr_u - local.set $35 - i32.const 0 - local.set $6 - local.get $1 - local.set $8 - loop $label$164 ;; label = @23 - local.get $8 - local.get $8 - i32.load - local.tee $32 - local.get $13 - i32.shr_u - local.get $6 - i32.add - i32.store - local.get $32 - local.get $29 - i32.and - local.get $35 - i32.mul - local.set $6 - local.get $8 - i32.const 4 - i32.add - local.tee $8 - local.get $5 - i32.lt_u - br_if 0 (;@23;) - end - local.get $1 - i32.const 4 - i32.add - local.set $8 - local.get $1 - i32.load - i32.eqz - if ;; label = @23 - local.get $8 - local.set $1 - end - local.get $6 - i32.eqz - br_if 2 (;@20;) - local.get $5 - local.get $6 - i32.store - local.get $5 - i32.const 4 - i32.add - local.set $5 - end - else - block ;; label = @22 - local.get $1 - i32.const 4 - i32.add - local.set $8 - local.get $1 - i32.load - i32.eqz - if ;; label = @23 - local.get $8 - local.set $1 - end - end - end - end - local.get $25 - if (result i32) ;; label = @20 - local.get $7 - else - local.get $1 - end - local.tee $8 - local.get $14 - i32.const 2 - i32.shl - i32.add - local.set $6 - local.get $5 - local.get $8 - i32.sub - i32.const 2 - i32.shr_s - local.get $14 - i32.gt_s - if ;; label = @20 - local.get $6 - local.set $5 - end - local.get $20 - local.get $20 - i32.load - local.get $13 - i32.add - local.tee $6 - i32.store - local.get $6 - i32.const 0 - i32.lt_s - br_if 0 (;@19;) - local.get $5 - local.set $13 - end - end - else - local.get $8 - local.set $13 - end - local.get $7 - local.set $25 - block $label$172 ;; label = @17 - local.get $1 - local.get $13 - i32.lt_u - if ;; label = @18 - block ;; label = @19 - local.get $25 - local.get $1 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - local.set $5 - local.get $1 - i32.load - local.tee $6 - i32.const 10 - i32.lt_u - br_if 2 (;@17;) - i32.const 10 - local.set $8 - loop $label$174 ;; label = @20 - local.get $5 - i32.const 1 - i32.add - local.set $5 - local.get $6 - local.get $8 - i32.const 10 - i32.mul - local.tee $8 - i32.ge_u - br_if 0 (;@20;) - end - end - else - i32.const 0 - local.set $5 - end - end - local.get $22 - i32.const 103 - i32.eq - local.set $29 - local.get $18 - i32.const 0 - i32.ne - local.set $35 - local.get $18 - local.get $22 - i32.const 102 - i32.ne - if (result i32) ;; label = @17 - local.get $5 - else - i32.const 0 - end - i32.sub - local.get $35 - local.get $29 - i32.and - i32.const 31 - i32.shl - i32.const 31 - i32.shr_s - i32.add - local.tee $8 - local.get $13 - local.get $25 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - i32.const -9 - i32.add - i32.lt_s - if ;; label = @17 - block ;; label = @18 - local.get $8 - i32.const 9216 - i32.add - local.tee $14 - i32.const 9 - i32.rem_s - i32.const 1 - i32.add - local.tee $8 - i32.const 9 - i32.lt_s - if ;; label = @19 - block ;; label = @20 - i32.const 10 - local.set $6 - loop $label$180 ;; label = @21 - local.get $6 - i32.const 10 - i32.mul - local.set $6 - local.get $8 - i32.const 1 - i32.add - local.tee $8 - i32.const 9 - i32.ne - br_if 0 (;@21;) - end - end - else - i32.const 10 - local.set $6 - end - local.get $7 - i32.const 4 - i32.add - local.get $14 - i32.const 9 - i32.div_s - i32.const -1024 - i32.add - i32.const 2 - i32.shl - i32.add - local.tee $8 - i32.load - local.tee $22 - local.get $6 - i32.rem_u - local.set $14 - block $label$182 ;; label = @19 - local.get $8 - i32.const 4 - i32.add - local.get $13 - i32.eq - local.tee $32 - local.get $14 - i32.eqz - i32.and - i32.eqz - if ;; label = @20 - block ;; label = @21 - local.get $14 - local.get $6 - i32.const 2 - i32.div_s - local.tee $49 - i32.lt_u - if (result f64) ;; label = @22 - f64.const 0x1p-1 (;=0.5;) - else - local.get $32 - local.get $14 - local.get $49 - i32.eq - i32.and - if (result f64) ;; label = @23 - f64.const 0x1p+0 (;=1;) - else - f64.const 0x1.8p+0 (;=1.5;) - end - end - local.set $52 - local.get $22 - local.get $6 - i32.div_u - i32.const 1 - i32.and - if (result f64) ;; label = @22 - f64.const 0x1.0000000000001p+53 (;=9007199254740994;) - else - f64.const 0x1p+53 (;=9007199254740992;) - end - local.set $53 - block $label$190 ;; label = @22 - local.get $24 - if ;; label = @23 - block ;; label = @24 - local.get $26 - i32.load8_s - i32.const 45 - i32.ne - br_if 2 (;@22;) - local.get $53 - f64.neg - local.set $53 - local.get $52 - f64.neg - local.set $52 - end - end - end - local.get $8 - local.get $22 - local.get $14 - i32.sub - local.tee $14 - i32.store - local.get $53 - local.get $52 - f64.add - local.get $53 - f64.eq - br_if 2 (;@19;) - local.get $8 - local.get $14 - local.get $6 - i32.add - local.tee $5 - i32.store - local.get $5 - i32.const 999999999 - i32.gt_u - if ;; label = @22 - loop $label$193 ;; label = @23 - local.get $8 - i32.const 0 - i32.store - local.get $8 - i32.const -4 - i32.add - local.tee $8 - local.get $1 - i32.lt_u - if ;; label = @24 - local.get $1 - i32.const -4 - i32.add - local.tee $1 - i32.const 0 - i32.store - end - local.get $8 - local.get $8 - i32.load - i32.const 1 - i32.add - local.tee $5 - i32.store - local.get $5 - i32.const 999999999 - i32.gt_u - br_if 0 (;@23;) - end - end - local.get $25 - local.get $1 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - local.set $5 - local.get $1 - i32.load - local.tee $14 - i32.const 10 - i32.lt_u - br_if 2 (;@19;) - i32.const 10 - local.set $6 - loop $label$195 ;; label = @22 - local.get $5 - i32.const 1 - i32.add - local.set $5 - local.get $14 - local.get $6 - i32.const 10 - i32.mul - local.tee $6 - i32.ge_u - br_if 0 (;@22;) - end - end - end - end - local.get $1 - local.set $14 - local.get $5 - local.set $6 - local.get $13 - local.get $8 - i32.const 4 - i32.add - local.tee $8 - i32.le_u - if ;; label = @19 - local.get $13 - local.set $8 - end - end - else - block ;; label = @18 - local.get $1 - local.set $14 - local.get $5 - local.set $6 - local.get $13 - local.set $8 - end - end - i32.const 0 - local.get $6 - i32.sub - local.set $32 - loop $label$198 ;; label = @17 - block $label$199 ;; label = @18 - local.get $8 - local.get $14 - i32.le_u - if ;; label = @19 - block ;; label = @20 - i32.const 0 - local.set $22 - br 2 (;@18;) - end - end - local.get $8 - i32.const -4 - i32.add - local.tee $1 - i32.load - if ;; label = @19 - i32.const 1 - local.set $22 - else - block ;; label = @20 - local.get $1 - local.set $8 - br 3 (;@17;) - end - end - end - end - block $label$203 ;; label = @17 - local.get $29 - if ;; label = @18 - block ;; label = @19 - local.get $35 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $18 - i32.add - local.tee $1 - local.get $6 - i32.gt_s - local.get $6 - i32.const -5 - i32.gt_s - i32.and - if (result i32) ;; label = @20 - block (result i32) ;; label = @21 - local.get $9 - i32.const -1 - i32.add - local.set $5 - local.get $1 - i32.const -1 - i32.add - local.get $6 - i32.sub - end - else - block (result i32) ;; label = @21 - local.get $9 - i32.const -2 - i32.add - local.set $5 - local.get $1 - i32.const -1 - i32.add - end - end - local.set $1 - local.get $12 - i32.const 8 - i32.and - local.tee $13 - br_if 2 (;@17;) - block $label$207 ;; label = @20 - local.get $22 - if ;; label = @21 - block ;; label = @22 - local.get $8 - i32.const -4 - i32.add - i32.load - local.tee $18 - i32.eqz - if ;; label = @23 - block ;; label = @24 - i32.const 9 - local.set $9 - br 4 (;@20;) - end - end - local.get $18 - i32.const 10 - i32.rem_u - if ;; label = @23 - block ;; label = @24 - i32.const 0 - local.set $9 - br 4 (;@20;) - end - else - block ;; label = @24 - i32.const 10 - local.set $13 - i32.const 0 - local.set $9 - end - end - loop $label$212 ;; label = @23 - local.get $9 - i32.const 1 - i32.add - local.set $9 - local.get $18 - local.get $13 - i32.const 10 - i32.mul - local.tee $13 - i32.rem_u - i32.eqz - br_if 0 (;@23;) - end - end - else - i32.const 9 - local.set $9 - end - end - local.get $8 - local.get $25 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - i32.const -9 - i32.add - local.set $18 - local.get $5 - i32.const 32 - i32.or - i32.const 102 - i32.eq - if ;; label = @20 - block ;; label = @21 - i32.const 0 - local.set $13 - local.get $1 - local.get $18 - local.get $9 - i32.sub - local.tee $9 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @22 - i32.const 0 - local.tee $9 - else - local.get $9 - end - i32.ge_s - if ;; label = @22 - local.get $9 - local.set $1 - end - end - else - block ;; label = @21 - i32.const 0 - local.set $13 - local.get $1 - local.get $18 - local.get $6 - i32.add - local.get $9 - i32.sub - local.tee $9 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @22 - i32.const 0 - local.tee $9 - else - local.get $9 - end - i32.ge_s - if ;; label = @22 - local.get $9 - local.set $1 - end - end - end - end - else - block ;; label = @19 - local.get $12 - i32.const 8 - i32.and - local.set $13 - local.get $18 - local.set $1 - local.get $9 - local.set $5 - end - end - end - local.get $5 - i32.const 32 - i32.or - i32.const 102 - i32.eq - local.tee $25 - if ;; label = @17 - block ;; label = @18 - i32.const 0 - local.set $9 - local.get $6 - i32.const 0 - i32.le_s - if ;; label = @19 - i32.const 0 - local.set $6 - end - end - else - block ;; label = @18 - local.get $28 - local.get $6 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @19 - local.get $32 - else - local.get $6 - end - i64.extend_i32_s - local.get $33 - call $23 - local.tee $9 - i32.sub - i32.const 2 - i32.lt_s - if ;; label = @19 - loop $label$229 ;; label = @20 - local.get $9 - i32.const -1 - i32.add - local.tee $9 - i32.const 48 - i32.store8 - local.get $28 - local.get $9 - i32.sub - i32.const 2 - i32.lt_s - br_if 0 (;@20;) - end - end - local.get $9 - i32.const -1 - i32.add - local.get $6 - i32.const 31 - i32.shr_s - i32.const 2 - i32.and - i32.const 43 - i32.add - i32.store8 - local.get $9 - i32.const -2 - i32.add - local.tee $6 - local.get $5 - i32.store8 - local.get $6 - local.set $9 - local.get $28 - local.get $6 - i32.sub - local.set $6 - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $24 - i32.const 1 - i32.add - local.get $1 - i32.add - local.get $1 - local.get $13 - i32.or - local.tee $29 - i32.const 0 - i32.ne - i32.add - local.get $6 - i32.add - local.tee $18 - local.get $12 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - local.get $26 - local.get $24 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $18 - local.get $12 - i32.const 65536 - i32.xor - call $25 - block $label$231 ;; label = @17 - local.get $25 - if ;; label = @18 - block ;; label = @19 - local.get $14 - local.get $7 - i32.gt_u - if (result i32) ;; label = @20 - local.get $7 - else - local.get $14 - end - local.tee $9 - local.set $6 - loop $label$235 ;; label = @20 - local.get $6 - i32.load - i64.extend_i32_u - local.get $31 - call $23 - local.set $5 - block $label$236 ;; label = @21 - local.get $6 - local.get $9 - i32.eq - if ;; label = @22 - block ;; label = @23 - local.get $5 - local.get $31 - i32.ne - br_if 2 (;@21;) - local.get $34 - i32.const 48 - i32.store8 - local.get $34 - local.set $5 - end - else - block ;; label = @23 - local.get $5 - local.get $19 - i32.le_u - br_if 2 (;@21;) - local.get $19 - i32.const 48 - local.get $5 - local.get $27 - i32.sub - call $41 - drop - loop $label$239 ;; label = @24 - local.get $5 - i32.const -1 - i32.add - local.tee $5 - local.get $19 - i32.gt_u - br_if 0 (;@24;) - end - end - end - end - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @21 - local.get $5 - local.get $41 - local.get $5 - i32.sub - local.get $0 - call $21 - drop - end - local.get $6 - i32.const 4 - i32.add - local.tee $5 - local.get $7 - i32.le_u - if ;; label = @21 - block ;; label = @22 - local.get $5 - local.set $6 - br 2 (;@20;) - end - end - end - block $label$242 ;; label = @20 - local.get $29 - if ;; label = @21 - block ;; label = @22 - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@20;) - i32.const 1687 - i32.const 1 - local.get $0 - call $21 - drop - end - end - end - local.get $1 - i32.const 0 - i32.gt_s - local.get $5 - local.get $8 - i32.lt_u - i32.and - if ;; label = @20 - loop $label$245 ;; label = @21 - local.get $5 - i32.load - i64.extend_i32_u - local.get $31 - call $23 - local.tee $7 - local.get $19 - i32.gt_u - if ;; label = @22 - block ;; label = @23 - local.get $19 - i32.const 48 - local.get $7 - local.get $27 - i32.sub - call $41 - drop - loop $label$247 ;; label = @24 - local.get $7 - i32.const -1 - i32.add - local.tee $7 - local.get $19 - i32.gt_u - br_if 0 (;@24;) - end - end - end - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @22 - local.get $7 - local.get $1 - i32.const 9 - i32.gt_s - if (result i32) ;; label = @23 - i32.const 9 - else - local.get $1 - end - local.get $0 - call $21 - drop - end - local.get $1 - i32.const -9 - i32.add - local.set $7 - local.get $1 - i32.const 9 - i32.gt_s - local.get $5 - i32.const 4 - i32.add - local.tee $5 - local.get $8 - i32.lt_u - i32.and - if ;; label = @22 - block ;; label = @23 - local.get $7 - local.set $1 - br 2 (;@21;) - end - else - local.get $7 - local.set $1 - end - end - end - local.get $0 - i32.const 48 - local.get $1 - i32.const 9 - i32.add - i32.const 9 - i32.const 0 - call $25 - end - else - block ;; label = @19 - local.get $14 - i32.const 4 - i32.add - local.set $5 - local.get $22 - i32.eqz - if ;; label = @20 - local.get $5 - local.set $8 - end - local.get $1 - i32.const -1 - i32.gt_s - if ;; label = @20 - block ;; label = @21 - local.get $13 - i32.eqz - local.set $13 - local.get $14 - local.set $7 - local.get $1 - local.set $5 - loop $label$256 ;; label = @22 - local.get $7 - i32.load - i64.extend_i32_u - local.get $31 - call $23 - local.tee $1 - local.get $31 - i32.eq - if ;; label = @23 - block ;; label = @24 - local.get $34 - i32.const 48 - i32.store8 - local.get $34 - local.set $1 - end - end - block $label$258 ;; label = @23 - local.get $7 - local.get $14 - i32.eq - if ;; label = @24 - block ;; label = @25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @26 - local.get $1 - i32.const 1 - local.get $0 - call $21 - drop - end - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $13 - local.get $5 - i32.const 1 - i32.lt_s - i32.and - br_if 2 (;@23;) - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@23;) - i32.const 1687 - i32.const 1 - local.get $0 - call $21 - drop - end - else - block ;; label = @25 - local.get $1 - local.get $19 - i32.le_u - br_if 2 (;@23;) - local.get $19 - i32.const 48 - local.get $1 - local.get $43 - i32.add - call $41 - drop - loop $label$262 ;; label = @26 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $19 - i32.gt_u - br_if 0 (;@26;) - end - end - end - end - local.get $41 - local.get $1 - i32.sub - local.set $6 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @23 - local.get $1 - local.get $5 - local.get $6 - i32.gt_s - if (result i32) ;; label = @24 - local.get $6 - else - local.get $5 - end - local.get $0 - call $21 - drop - end - local.get $7 - i32.const 4 - i32.add - local.tee $7 - local.get $8 - i32.lt_u - local.get $5 - local.get $6 - i32.sub - local.tee $5 - i32.const -1 - i32.gt_s - i32.and - br_if 0 (;@22;) - local.get $5 - local.set $1 - end - end - end - local.get $0 - i32.const 48 - local.get $1 - i32.const 18 - i32.add - i32.const 18 - i32.const 0 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@17;) - local.get $9 - local.get $28 - local.get $9 - i32.sub - local.get $0 - call $21 - drop - end - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $18 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $18 - local.get $10 - i32.ge_s - if ;; label = @17 - local.get $18 - local.set $10 - end - end - else - block ;; label = @16 - local.get $0 - i32.const 32 - local.get $10 - local.get $52 - local.get $52 - f64.ne - i32.const 0 - i32.or - local.tee $6 - if (result i32) ;; label = @17 - i32.const 0 - local.tee $24 - else - local.get $24 - end - i32.const 3 - i32.add - local.tee $8 - local.get $7 - call $25 - local.get $0 - i32.load - local.tee $1 - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - block ;; label = @18 - local.get $26 - local.get $24 - local.get $0 - call $21 - drop - local.get $0 - i32.load - local.set $1 - end - end - local.get $9 - i32.const 32 - i32.and - i32.const 0 - i32.ne - local.tee $5 - if (result i32) ;; label = @17 - i32.const 1671 - else - i32.const 1675 - end - local.set $7 - local.get $5 - if (result i32) ;; label = @17 - i32.const 1679 - else - i32.const 1683 - end - local.set $5 - local.get $6 - i32.eqz - if ;; label = @17 - local.get $7 - local.set $5 - end - local.get $1 - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - local.get $5 - i32.const 3 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $8 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $8 - local.get $10 - i32.ge_s - if ;; label = @17 - local.get $8 - local.set $10 - end - end - end - end - local.get $11 - local.set $1 - br 9 (;@4;) - end - local.get $5 - local.set $7 - i32.const 0 - local.set $6 - i32.const 1635 - local.set $8 - local.get $21 - local.set $5 - br 6 (;@6;) - end - local.get $9 - i32.const 32 - i32.and - local.set $7 - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.eq - if (result i32) ;; label = @12 - block (result i32) ;; label = @13 - i64.const 0 - local.set $50 - local.get $21 - end - else - block (result i32) ;; label = @13 - local.get $21 - local.set $1 - loop $label$280 ;; label = @14 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $50 - i32.wrap_i64 - i32.const 15 - i32.and - i32.const 1619 - i32.add - i32.load8_u - local.get $7 - i32.or - i32.store8 - local.get $50 - i64.const 4 - i64.shr_u - local.tee $50 - i64.const 0 - i64.ne - br_if 0 (;@14;) - end - local.get $16 - i64.load - local.set $50 - local.get $1 - end - end - local.set $7 - local.get $9 - i32.const 4 - i32.shr_s - i32.const 1635 - i32.add - local.set $8 - local.get $12 - i32.const 8 - i32.and - i32.eqz - local.get $50 - i64.const 0 - i64.eq - i32.or - local.tee $1 - if ;; label = @12 - i32.const 1635 - local.set $8 - end - local.get $1 - if (result i32) ;; label = @12 - i32.const 0 - else - i32.const 2 - end - local.set $6 - br 4 (;@7;) - end - local.get $50 - local.get $21 - call $23 - local.set $7 - br 3 (;@7;) - end - local.get $1 - i32.const 0 - local.get $5 - call $17 - local.tee $13 - i32.eqz - local.set $14 - local.get $13 - local.get $1 - i32.sub - local.set $8 - local.get $1 - local.get $5 - i32.add - local.set $9 - local.get $7 - local.set $12 - local.get $14 - if (result i32) ;; label = @10 - local.get $5 - else - local.get $8 - end - local.set $7 - i32.const 0 - local.set $6 - i32.const 1635 - local.set $8 - local.get $14 - if (result i32) ;; label = @10 - local.get $9 - else - local.get $13 - end - local.set $5 - br 3 (;@6;) - end - i32.const 0 - local.set $1 - i32.const 0 - local.set $5 - local.get $7 - local.set $8 - loop $label$288 ;; label = @9 - block $label$289 ;; label = @10 - local.get $8 - i32.load - local.tee $9 - i32.eqz - br_if 0 (;@10;) - local.get $36 - local.get $9 - call $26 - local.tee $5 - i32.const 0 - i32.lt_s - local.get $5 - local.get $6 - local.get $1 - i32.sub - i32.gt_u - i32.or - br_if 0 (;@10;) - local.get $8 - i32.const 4 - i32.add - local.set $8 - local.get $6 - local.get $5 - local.get $1 - i32.add - local.tee $1 - i32.gt_u - br_if 1 (;@9;) - end - end - local.get $5 - i32.const 0 - i32.lt_s - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $1 - local.get $12 - call $25 - local.get $1 - if ;; label = @9 - block ;; label = @10 - i32.const 0 - local.set $5 - loop $label$292 ;; label = @11 - local.get $7 - i32.load - local.tee $8 - i32.eqz - br_if 3 (;@8;) - local.get $36 - local.get $8 - call $26 - local.tee $8 - local.get $5 - i32.add - local.tee $5 - local.get $1 - i32.gt_s - br_if 3 (;@8;) - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @12 - local.get $36 - local.get $8 - local.get $0 - call $21 - drop - end - local.get $7 - i32.const 4 - i32.add - local.set $7 - local.get $5 - local.get $1 - i32.lt_u - br_if 0 (;@11;) - br 3 (;@8;) - end - end - else - block ;; label = @10 - i32.const 0 - local.set $1 - br 2 (;@8;) - end - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $1 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $10 - local.get $1 - i32.le_s - if ;; label = @8 - local.get $1 - local.set $10 - end - local.get $11 - local.set $1 - br 3 (;@4;) - end - local.get $12 - i32.const -65537 - i32.and - local.set $1 - local.get $5 - i32.const -1 - i32.gt_s - if ;; label = @7 - local.get $1 - local.set $12 - end - local.get $5 - local.get $16 - i64.load - i64.const 0 - i64.ne - local.tee $9 - i32.or - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - local.get $7 - local.set $1 - local.get $5 - local.get $9 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $38 - local.get $7 - i32.sub - i32.add - local.tee $7 - i32.gt_s - if ;; label = @9 - local.get $5 - local.set $7 - end - local.get $21 - end - else - block (result i32) ;; label = @8 - local.get $21 - local.set $1 - i32.const 0 - local.set $7 - local.get $21 - end - end - local.set $5 - end - local.get $0 - i32.const 32 - local.get $10 - local.get $7 - local.get $5 - local.get $1 - i32.sub - local.tee $9 - i32.lt_s - if (result i32) ;; label = @6 - local.get $9 - local.tee $7 - else - local.get $7 - end - local.get $6 - i32.add - local.tee $5 - i32.lt_s - if (result i32) ;; label = @6 - local.get $5 - local.tee $10 - else - local.get $10 - end - local.get $5 - local.get $12 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @6 - local.get $8 - local.get $6 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $5 - local.get $12 - i32.const 65536 - i32.xor - call $25 - local.get $0 - i32.const 48 - local.get $7 - local.get $9 - i32.const 0 - call $25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @6 - local.get $1 - local.get $9 - local.get $0 - call $21 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - local.get $12 - i32.const 8192 - i32.xor - call $25 - local.get $11 - local.set $1 - br 1 (;@4;) - end - end - br 1 (;@2;) - end - local.get $0 - i32.eqz - if ;; label = @3 - local.get $17 - if ;; label = @4 - block ;; label = @5 - i32.const 1 - local.set $0 - loop $label$308 ;; label = @6 - local.get $4 - local.get $0 - i32.const 2 - i32.shl - i32.add - i32.load - local.tee $1 - if ;; label = @7 - block ;; label = @8 - local.get $3 - local.get $0 - i32.const 3 - i32.shl - i32.add - local.get $1 - local.get $2 - call $22 - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.const 10 - i32.lt_s - br_if 2 (;@6;) - i32.const 1 - local.set $15 - br 6 (;@2;) - end - end - end - loop $label$310 ;; label = @6 - local.get $4 - local.get $0 - i32.const 2 - i32.shl - i32.add - i32.load - if ;; label = @7 - block ;; label = @8 - i32.const -1 - local.set $15 - br 6 (;@2;) - end - end - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.const 10 - i32.lt_s - br_if 0 (;@6;) - i32.const 1 - local.set $15 - end - end - else - i32.const 0 - local.set $15 - end - end - end - local.get $23 - global.set $global$1 - local.get $15 - end - ) - (func $20 (;33;) (type $1) (param $0 i32) (result i32) - i32.const 0 - ) - (func $21 (;34;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) - block $label$1 (result i32) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $2 - i32.const 16 - i32.add - local.tee $4 - i32.load - local.tee $3 - br_if 0 (;@3;) - local.get $2 - call $30 - if ;; label = @4 - i32.const 0 - local.set $3 - else - block ;; label = @5 - local.get $4 - i32.load - local.set $3 - br 2 (;@3;) - end - end - br 1 (;@2;) - end - local.get $3 - local.get $2 - i32.const 20 - i32.add - local.tee $5 - i32.load - local.tee $4 - i32.sub - local.get $1 - i32.lt_u - if ;; label = @3 - block ;; label = @4 - local.get $2 - local.get $0 - local.get $1 - local.get $2 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - local.set $3 - br 2 (;@2;) - end - end - block $label$7 (result i32) ;; label = @3 - local.get $2 - i32.load8_s offset=75 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @4 - block (result i32) ;; label = @5 - local.get $1 - local.set $3 - loop $label$9 ;; label = @6 - i32.const 0 - local.get $3 - i32.eqz - br_if 3 (;@3;) - drop - local.get $0 - local.get $3 - i32.const -1 - i32.add - local.tee $6 - i32.add - i32.load8_s - i32.const 10 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $6 - local.set $3 - br 2 (;@6;) - end - end - end - local.get $2 - local.get $0 - local.get $3 - local.get $2 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - local.get $3 - i32.lt_u - br_if 3 (;@2;) - local.get $5 - i32.load - local.set $4 - local.get $1 - local.get $3 - i32.sub - local.set $1 - local.get $0 - local.get $3 - i32.add - local.set $0 - local.get $3 - end - else - i32.const 0 - end - end - local.set $2 - local.get $4 - local.get $0 - local.get $1 - call $42 - drop - local.get $5 - local.get $5 - i32.load - local.get $1 - i32.add - i32.store - local.get $2 - local.get $1 - i32.add - local.set $3 - end - local.get $3 - end - ) - (func $22 (;35;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) (local $4 i64) (local $5 f64) - block $label$1 ;; label = @1 - local.get $1 - i32.const 20 - i32.le_u - if ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - block $label$6 ;; label = @6 - block $label$7 ;; label = @7 - block $label$8 ;; label = @8 - block $label$9 ;; label = @9 - block $label$10 ;; label = @10 - block $label$11 ;; label = @11 - block $label$12 ;; label = @12 - block $label$13 ;; label = @13 - local.get $1 - i32.const 9 - i32.sub - br_table 0 (;@13;) 1 (;@12;) 2 (;@11;) 3 (;@10;) 4 (;@9;) 5 (;@8;) 6 (;@7;) 7 (;@6;) 8 (;@5;) 9 (;@4;) 10 (;@3;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.store - br 11 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i64.extend_i32_s - i64.store - br 10 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i64.extend_i32_u - i64.store - br 9 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - i64.load - local.set $4 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $4 - i64.store - br 8 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 65535 - i32.and - i32.const 16 - i32.shl - i32.const 16 - i32.shr_s - i64.extend_i32_s - i64.store - br 7 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 65535 - i32.and - i64.extend_i32_u - i64.store - br 6 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i64.extend_i32_s - i64.store - br 5 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 255 - i32.and - i64.extend_i32_u - i64.store - br 4 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - f64.load - local.set $5 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $5 - f64.store - br 3 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - f64.load - local.set $5 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $5 - f64.store - end - end - end - ) - (func $23 (;36;) (type $9) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i64) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.wrap_i64 - local.set $2 - local.get $0 - i64.const 4294967295 - i64.gt_u - if ;; label = @2 - block ;; label = @3 - loop $label$3 ;; label = @4 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $0 - i64.const 10 - i64.rem_u - i64.const 48 - i64.or - i64.store8 - local.get $0 - i64.const 10 - i64.div_u - local.set $4 - local.get $0 - i64.const 42949672959 - i64.gt_u - if ;; label = @5 - block ;; label = @6 - local.get $4 - local.set $0 - br 2 (;@4;) - end - end - end - local.get $4 - i32.wrap_i64 - local.set $2 - end - end - local.get $2 - if ;; label = @2 - loop $label$6 ;; label = @3 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $2 - i32.const 10 - i32.rem_u - i32.const 48 - i32.or - i32.store8 - local.get $2 - i32.const 10 - i32.div_u - local.set $3 - local.get $2 - i32.const 10 - i32.ge_u - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $2 - br 2 (;@3;) - end - end - end - end - local.get $1 - end - ) - (func $24 (;37;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - i32.const 0 - local.set $1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - loop $label$5 ;; label = @5 - local.get $1 - i32.const 1689 - i32.add - i32.load8_u - local.get $0 - i32.eq - br_if 1 (;@4;) - local.get $1 - i32.const 1 - i32.add - local.tee $1 - i32.const 87 - i32.ne - br_if 0 (;@5;) - i32.const 87 - local.set $1 - i32.const 1777 - local.set $0 - br 2 (;@3;) - end - end - local.get $1 - if ;; label = @4 - block ;; label = @5 - i32.const 1777 - local.set $0 - br 2 (;@3;) - end - else - i32.const 1777 - local.set $0 - end - br 1 (;@2;) - end - loop $label$8 ;; label = @3 - local.get $0 - local.set $2 - loop $label$9 ;; label = @4 - local.get $2 - i32.const 1 - i32.add - local.set $0 - local.get $2 - i32.load8_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.set $2 - br 2 (;@4;) - end - end - end - local.get $1 - i32.const -1 - i32.add - local.tee $1 - br_if 0 (;@3;) - end - end - local.get $0 - end - ) - (func $25 (;38;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) - (local $5 i32) (local $6 i32) (local $7 i32) - block $label$1 ;; label = @1 - global.get $global$1 - local.set $7 - global.get $global$1 - i32.const 256 - i32.add - global.set $global$1 - local.get $7 - local.set $6 - block $label$2 ;; label = @2 - local.get $2 - local.get $3 - i32.gt_s - local.get $4 - i32.const 73728 - i32.and - i32.eqz - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $6 - local.get $1 - local.get $2 - local.get $3 - i32.sub - local.tee $5 - i32.const 256 - i32.gt_u - if (result i32) ;; label = @5 - i32.const 256 - else - local.get $5 - end - call $41 - drop - local.get $0 - i32.load - local.tee $1 - i32.const 32 - i32.and - i32.eqz - local.set $4 - local.get $5 - i32.const 255 - i32.gt_u - if ;; label = @5 - block ;; label = @6 - loop $label$7 ;; label = @7 - local.get $4 - if ;; label = @8 - block ;; label = @9 - local.get $6 - i32.const 256 - local.get $0 - call $21 - drop - local.get $0 - i32.load - local.set $1 - end - end - local.get $1 - i32.const 32 - i32.and - i32.eqz - local.set $4 - local.get $5 - i32.const -256 - i32.add - local.tee $5 - i32.const 255 - i32.gt_u - br_if 0 (;@7;) - end - local.get $4 - i32.eqz - br_if 4 (;@2;) - local.get $2 - local.get $3 - i32.sub - i32.const 255 - i32.and - local.set $5 - end - else - local.get $4 - i32.eqz - br_if 3 (;@2;) - end - local.get $6 - local.get $5 - local.get $0 - call $21 - drop - end - end - end - local.get $7 - global.set $global$1 - end - ) - (func $26 (;39;) (type $4) (param $0 i32) (param $1 i32) (result i32) - local.get $0 - if (result i32) ;; label = @1 - local.get $0 - local.get $1 - i32.const 0 - call $29 - else - i32.const 0 - end - ) - (func $27 (;40;) (type $11) (param $0 f64) (param $1 i32) (result f64) - local.get $0 - local.get $1 - call $28 - ) - (func $28 (;41;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (local $2 i64) (local $3 i64) - block $label$1 (result f64) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - local.get $0 - i64.reinterpret_f64 - local.tee $2 - i64.const 52 - i64.shr_u - local.tee $3 - i32.wrap_i64 - i32.const 65535 - i32.and - i32.const 2047 - i32.and - i32.const 16 - i32.shl - i32.const 16 - i32.shr_s - i32.const 0 - i32.sub - br_table 0 (;@5;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 1 (;@4;) 2 (;@3;) - end - local.get $1 - local.get $0 - f64.const 0x0p+0 (;=0;) - f64.ne - if (result i32) ;; label = @5 - block (result i32) ;; label = @6 - local.get $0 - f64.const 0x1p+64 (;=18446744073709552000;) - f64.mul - local.get $1 - call $28 - local.set $0 - local.get $1 - i32.load - i32.const -64 - i32.add - end - else - i32.const 0 - end - i32.store - br 2 (;@2;) - end - br 1 (;@2;) - end - local.get $1 - local.get $3 - i32.wrap_i64 - i32.const 2047 - i32.and - i32.const -1022 - i32.add - i32.store - local.get $2 - i64.const -9218868437227405313 - i64.and - i64.const 4602678819172646912 - i64.or - f64.reinterpret_i64 - local.set $0 - end - local.get $0 - end - ) - (func $29 (;42;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $1 - i32.const 128 - i32.lt_u - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.store8 - i32.const 1 - br 4 (;@1;) - end - end - local.get $1 - i32.const 2048 - i32.lt_u - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 192 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - i32.const 2 - br 4 (;@1;) - end - end - local.get $1 - i32.const 55296 - i32.lt_u - local.get $1 - i32.const -8192 - i32.and - i32.const 57344 - i32.eq - i32.or - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.const 12 - i32.shr_u - i32.const 224 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=2 - i32.const 3 - br 4 (;@1;) - end - end - local.get $1 - i32.const -65536 - i32.add - i32.const 1048576 - i32.lt_u - if (result i32) ;; label = @4 - block (result i32) ;; label = @5 - local.get $0 - local.get $1 - i32.const 18 - i32.shr_u - i32.const 240 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 12 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=2 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=3 - i32.const 4 - end - else - block (result i32) ;; label = @5 - call $12 - i32.const 84 - i32.store - i32.const -1 - end - end - end - else - i32.const 1 - end - end - ) - (func $30 (;43;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.const 74 - i32.add - local.tee $2 - i32.load8_s - local.set $1 - local.get $2 - local.get $1 - i32.const 255 - i32.add - local.get $1 - i32.or - i32.store8 - local.get $0 - i32.load - local.tee $1 - i32.const 8 - i32.and - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $0 - local.get $1 - i32.const 32 - i32.or - i32.store - i32.const -1 - end - else - block (result i32) ;; label = @3 - local.get $0 - i32.const 0 - i32.store offset=8 - local.get $0 - i32.const 0 - i32.store offset=4 - local.get $0 - local.get $0 - i32.load offset=44 - local.tee $1 - i32.store offset=28 - local.get $0 - local.get $1 - i32.store offset=20 - local.get $0 - local.get $1 - local.get $0 - i32.load offset=48 - i32.add - i32.store offset=16 - i32.const 0 - end - end - local.tee $0 - end - ) - (func $31 (;44;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) - block $label$1 (result i32) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $0 - local.tee $2 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get $2 - local.set $1 - loop $label$4 ;; label = @4 - local.get $0 - i32.load8_s - i32.eqz - if ;; label = @5 - block ;; label = @6 - local.get $1 - local.set $0 - br 4 (;@2;) - end - end - local.get $0 - i32.const 1 - i32.add - local.tee $0 - local.tee $1 - i32.const 3 - i32.and - br_if 0 (;@4;) - br 1 (;@3;) - end - end - loop $label$6 ;; label = @3 - local.get $0 - i32.const 4 - i32.add - local.set $1 - local.get $0 - i32.load - local.tee $3 - i32.const -2139062144 - i32.and - i32.const -2139062144 - i32.xor - local.get $3 - i32.const -16843009 - i32.add - i32.and - i32.eqz - if ;; label = @4 - block ;; label = @5 - local.get $1 - local.set $0 - br 2 (;@3;) - end - end - end - local.get $3 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - if ;; label = @3 - loop $label$9 ;; label = @4 - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.load8_s - br_if 0 (;@4;) - end - end - end - local.get $0 - local.get $2 - i32.sub - end - ) - (func $32 (;45;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $3 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $3 - local.tee $4 - local.get $1 - i32.const 255 - i32.and - local.tee $7 - i32.store8 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $0 - i32.const 16 - i32.add - local.tee $2 - i32.load - local.tee $5 - br_if 0 (;@3;) - local.get $0 - call $30 - if ;; label = @4 - i32.const -1 - local.set $1 - else - block ;; label = @5 - local.get $2 - i32.load - local.set $5 - br 2 (;@3;) - end - end - br 1 (;@2;) - end - local.get $0 - i32.const 20 - i32.add - local.tee $2 - i32.load - local.tee $6 - local.get $5 - i32.lt_u - if ;; label = @3 - local.get $1 - i32.const 255 - i32.and - local.tee $1 - local.get $0 - i32.load8_s offset=75 - i32.ne - if ;; label = @4 - block ;; label = @5 - local.get $2 - local.get $6 - i32.const 1 - i32.add - i32.store - local.get $6 - local.get $7 - i32.store8 - br 3 (;@2;) - end - end - end - local.get $0 - local.get $4 - i32.const 1 - local.get $0 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - i32.const 1 - i32.eq - if (result i32) ;; label = @3 - local.get $4 - i32.load8_u - else - i32.const -1 - end - local.set $1 - end - local.get $3 - global.set $global$1 - local.get $1 - end - ) - (func $33 (;46;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $2 - local.get $1 - i32.mul - local.set $4 - local.get $3 - i32.load offset=76 - i32.const -1 - i32.gt_s - if ;; label = @2 - block ;; label = @3 - local.get $3 - call $20 - i32.eqz - local.set $5 - local.get $0 - local.get $4 - local.get $3 - call $21 - local.set $0 - local.get $5 - i32.eqz - if ;; label = @4 - local.get $3 - call $13 - end - end - else - local.get $0 - local.get $4 - local.get $3 - call $21 - local.set $0 - end - local.get $0 - local.get $4 - i32.ne - if ;; label = @2 - local.get $0 - local.get $1 - i32.div_u - local.set $2 - end - local.get $2 - end - ) - (func $34 (;47;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $2 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $2 - local.tee $3 - local.get $1 - i32.store - i32.const 1024 - i32.load - local.get $0 - local.get $3 - call $18 - local.set $0 - local.get $2 - global.set $global$1 - local.get $0 - end - ) - (func $35 (;48;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) - block $label$1 (result i32) ;; label = @1 - i32.const 1024 - i32.load - local.tee $1 - i32.load offset=76 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @2 - local.get $1 - call $20 - else - i32.const 0 - end - local.set $2 - block $label$4 (result i32) ;; label = @2 - local.get $0 - local.get $1 - call $36 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @3 - i32.const 1 - else - block (result i32) ;; label = @4 - local.get $1 - i32.load8_s offset=75 - i32.const 10 - i32.ne - if ;; label = @5 - local.get $1 - i32.const 20 - i32.add - local.tee $3 - i32.load - local.tee $0 - local.get $1 - i32.load offset=16 - i32.lt_u - if ;; label = @6 - block ;; label = @7 - local.get $3 - local.get $0 - i32.const 1 - i32.add - i32.store - local.get $0 - i32.const 10 - i32.store8 - i32.const 0 - br 5 (;@2;) - end - end - end - local.get $1 - i32.const 10 - call $32 - i32.const 0 - i32.lt_s - end - end - end - local.set $0 - local.get $2 - if ;; label = @2 - local.get $1 - call $13 - end - local.get $0 - i32.const 31 - i32.shl - i32.const 31 - i32.shr_s - end - ) - (func $36 (;49;) (type $4) (param $0 i32) (param $1 i32) (result i32) - local.get $0 - local.get $0 - call $31 - i32.const 1 - local.get $1 - call $33 - i32.const -1 - i32.add - ) - (func $37 (;50;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $14 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $14 - local.set $18 - block $label$2 ;; label = @2 - local.get $0 - i32.const 245 - i32.lt_u - if ;; label = @3 - block ;; label = @4 - local.get $0 - i32.const 11 - i32.add - i32.const -8 - i32.and - local.set $3 - i32.const 3636 - i32.load - local.tee $8 - local.get $0 - i32.const 11 - i32.lt_u - if (result i32) ;; label = @5 - i32.const 16 - local.tee $3 - else - local.get $3 - end - i32.const 3 - i32.shr_u - local.tee $2 - i32.shr_u - local.tee $0 - i32.const 3 - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $0 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $2 - i32.add - local.tee $5 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.tee $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $7 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.set $4 - local.get $2 - local.get $4 - i32.eq - if ;; label = @7 - i32.const 3636 - local.get $8 - i32.const 1 - local.get $5 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - else - block ;; label = @8 - local.get $4 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $4 - i32.const 12 - i32.add - local.tee $0 - i32.load - local.get $7 - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $0 - local.get $2 - i32.store - local.get $3 - local.get $4 - i32.store - end - else - call $fimport$10 - end - end - end - local.get $7 - local.get $5 - i32.const 3 - i32.shl - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $7 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - local.get $14 - global.set $global$1 - local.get $1 - return - end - end - local.get $3 - i32.const 3644 - i32.load - local.tee $16 - i32.gt_u - if ;; label = @5 - block ;; label = @6 - local.get $0 - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.get $2 - i32.shl - i32.const 2 - local.get $2 - i32.shl - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.or - i32.and - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $5 - local.get $0 - local.get $5 - i32.shr_u - local.tee $2 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $5 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - i32.add - local.tee $11 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.tee $4 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $9 - i32.const 8 - i32.add - local.tee $5 - i32.load - local.set $12 - local.get $4 - local.get $12 - i32.eq - if ;; label = @9 - i32.const 3636 - local.get $8 - i32.const 1 - local.get $11 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $7 - i32.store - else - block ;; label = @10 - local.get $12 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$10 - end - local.get $12 - i32.const 12 - i32.add - local.tee $0 - i32.load - local.get $9 - i32.eq - if ;; label = @11 - block ;; label = @12 - local.get $0 - local.get $4 - i32.store - local.get $2 - local.get $12 - i32.store - local.get $8 - local.set $7 - end - else - call $fimport$10 - end - end - end - local.get $9 - local.get $3 - i32.const 3 - i32.or - i32.store offset=4 - local.get $9 - local.get $3 - i32.add - local.tee $4 - local.get $11 - i32.const 3 - i32.shl - local.get $3 - i32.sub - local.tee $11 - i32.const 1 - i32.or - i32.store offset=4 - local.get $4 - local.get $11 - i32.add - local.get $11 - i32.store - local.get $16 - if ;; label = @9 - block ;; label = @10 - i32.const 3656 - i32.load - local.set $9 - local.get $16 - i32.const 3 - i32.shr_u - local.tee $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.set $2 - local.get $7 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @11 - local.get $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $0 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @12 - call $fimport$10 - else - block ;; label = @13 - local.get $3 - local.set $6 - local.get $0 - local.set $1 - end - end - else - block ;; label = @12 - i32.const 3636 - local.get $7 - local.get $0 - i32.or - i32.store - local.get $2 - i32.const 8 - i32.add - local.set $6 - local.get $2 - local.set $1 - end - end - local.get $6 - local.get $9 - i32.store - local.get $1 - local.get $9 - i32.store offset=12 - local.get $9 - local.get $1 - i32.store offset=8 - local.get $9 - local.get $2 - i32.store offset=12 - end - end - i32.const 3644 - local.get $11 - i32.store - i32.const 3656 - local.get $4 - i32.store - local.get $14 - global.set $global$1 - local.get $5 - return - end - end - i32.const 3640 - i32.load - local.tee $6 - if ;; label = @7 - block ;; label = @8 - local.get $6 - i32.const 0 - local.get $6 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $2 - local.get $0 - local.get $2 - i32.shr_u - local.tee $1 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $2 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - i32.add - i32.const 2 - i32.shl - i32.const 3940 - i32.add - i32.load - local.tee $2 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.sub - local.set $9 - local.get $2 - local.set $1 - loop $label$25 ;; label = @9 - block $label$26 ;; label = @10 - local.get $1 - i32.load offset=16 - local.tee $0 - i32.eqz - if ;; label = @11 - local.get $1 - i32.load offset=20 - local.tee $0 - i32.eqz - br_if 1 (;@10;) - end - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.sub - local.tee $1 - local.get $9 - i32.lt_u - local.tee $7 - if ;; label = @11 - local.get $1 - local.set $9 - end - local.get $0 - local.set $1 - local.get $7 - if ;; label = @11 - local.get $0 - local.set $2 - end - br 1 (;@9;) - end - end - local.get $2 - i32.const 3652 - i32.load - local.tee $12 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $2 - local.get $2 - local.get $3 - i32.add - local.tee $13 - i32.ge_u - if ;; label = @9 - call $fimport$10 - end - local.get $2 - i32.load offset=24 - local.set $15 - block $label$32 ;; label = @9 - local.get $2 - i32.load offset=12 - local.tee $0 - local.get $2 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $2 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @12 - local.get $2 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @13 - block ;; label = @14 - i32.const 0 - local.set $4 - br 5 (;@9;) - end - end - end - loop $label$36 ;; label = @12 - local.get $0 - i32.const 20 - i32.add - local.tee $11 - i32.load - local.tee $7 - if ;; label = @13 - block ;; label = @14 - local.get $7 - local.set $0 - local.get $11 - local.set $1 - br 2 (;@12;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $11 - i32.load - local.tee $7 - if ;; label = @13 - block ;; label = @14 - local.get $7 - local.set $0 - local.get $11 - local.set $1 - br 2 (;@12;) - end - end - end - local.get $1 - local.get $12 - i32.lt_u - if ;; label = @12 - call $fimport$10 - else - block ;; label = @13 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $4 - end - end - end - else - block ;; label = @11 - local.get $2 - i32.load offset=8 - local.tee $11 - local.get $12 - i32.lt_u - if ;; label = @12 - call $fimport$10 - end - local.get $11 - i32.const 12 - i32.add - local.tee $7 - i32.load - local.get $2 - i32.ne - if ;; label = @12 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $2 - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $7 - local.get $0 - i32.store - local.get $1 - local.get $11 - i32.store - local.get $0 - local.set $4 - end - else - call $fimport$10 - end - end - end - end - block $label$46 ;; label = @9 - local.get $15 - if ;; label = @10 - block ;; label = @11 - local.get $2 - local.get $2 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $0 - local.get $4 - i32.store - local.get $4 - i32.eqz - if ;; label = @14 - block ;; label = @15 - i32.const 3640 - local.get $6 - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 6 (;@9;) - end - end - end - else - block ;; label = @13 - local.get $15 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$10 - end - local.get $15 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $2 - i32.eq - if ;; label = @14 - local.get $0 - local.get $4 - i32.store - else - local.get $15 - local.get $4 - i32.store offset=20 - end - local.get $4 - i32.eqz - br_if 4 (;@9;) - end - end - local.get $4 - i32.const 3652 - i32.load - local.tee $0 - i32.lt_u - if ;; label = @12 - call $fimport$10 - end - local.get $4 - local.get $15 - i32.store offset=24 - local.get $2 - i32.load offset=16 - local.tee $1 - if ;; label = @12 - local.get $1 - local.get $0 - i32.lt_u - if ;; label = @13 - call $fimport$10 - else - block ;; label = @14 - local.get $4 - local.get $1 - i32.store offset=16 - local.get $1 - local.get $4 - i32.store offset=24 - end - end - end - local.get $2 - i32.load offset=20 - local.tee $0 - if ;; label = @12 - local.get $0 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @13 - call $fimport$10 - else - block ;; label = @14 - local.get $4 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $4 - i32.store offset=24 - end - end - end - end - end - end - local.get $9 - i32.const 16 - i32.lt_u - if ;; label = @9 - block ;; label = @10 - local.get $2 - local.get $9 - local.get $3 - i32.add - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - else - block ;; label = @10 - local.get $2 - local.get $3 - i32.const 3 - i32.or - i32.store offset=4 - local.get $13 - local.get $9 - i32.const 1 - i32.or - i32.store offset=4 - local.get $13 - local.get $9 - i32.add - local.get $9 - i32.store - local.get $16 - if ;; label = @11 - block ;; label = @12 - i32.const 3656 - i32.load - local.set $7 - local.get $16 - i32.const 3 - i32.shr_u - local.tee $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.set $3 - local.get $8 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @13 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$10 - else - block ;; label = @15 - local.get $1 - local.set $10 - local.get $0 - local.set $5 - end - end - else - block ;; label = @14 - i32.const 3636 - local.get $8 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $10 - local.get $3 - local.set $5 - end - end - local.get $10 - local.get $7 - i32.store - local.get $5 - local.get $7 - i32.store offset=12 - local.get $7 - local.get $5 - i32.store offset=8 - local.get $7 - local.get $3 - i32.store offset=12 - end - end - i32.const 3644 - local.get $9 - i32.store - i32.const 3656 - local.get $13 - i32.store - end - end - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - else - local.get $3 - local.set $0 - end - end - else - local.get $3 - local.set $0 - end - end - else - local.get $0 - i32.const -65 - i32.gt_u - if ;; label = @4 - i32.const -1 - local.set $0 - else - block ;; label = @5 - local.get $0 - i32.const 11 - i32.add - local.tee $0 - i32.const -8 - i32.and - local.set $7 - i32.const 3640 - i32.load - local.tee $5 - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @8 - local.get $7 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @9 - i32.const 31 - else - local.get $7 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $3 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $3 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.set $17 - i32.const 0 - local.get $7 - i32.sub - local.set $3 - block $label$78 ;; label = @8 - block $label$79 ;; label = @9 - block $label$80 ;; label = @10 - local.get $17 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - i32.load - local.tee $1 - if ;; label = @11 - block ;; label = @12 - i32.const 25 - local.get $17 - i32.const 1 - i32.shr_u - i32.sub - local.set $0 - i32.const 0 - local.set $4 - local.get $7 - local.get $17 - i32.const 31 - i32.eq - if (result i32) ;; label = @13 - i32.const 0 - else - local.get $0 - end - i32.shl - local.set $10 - i32.const 0 - local.set $0 - loop $label$84 ;; label = @13 - local.get $1 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.sub - local.tee $6 - local.get $3 - i32.lt_u - if ;; label = @14 - local.get $6 - if ;; label = @15 - block ;; label = @16 - local.get $6 - local.set $3 - local.get $1 - local.set $0 - end - else - block ;; label = @16 - i32.const 0 - local.set $3 - local.get $1 - local.set $0 - br 7 (;@9;) - end - end - end - local.get $1 - i32.load offset=20 - local.tee $19 - i32.eqz - local.get $19 - local.get $1 - i32.const 16 - i32.add - local.get $10 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - i32.load - local.tee $6 - i32.eq - i32.or - if (result i32) ;; label = @14 - local.get $4 - else - local.get $19 - end - local.set $1 - local.get $10 - local.get $6 - i32.eqz - local.tee $4 - i32.const 1 - i32.and - i32.const 1 - i32.xor - i32.shl - local.set $10 - local.get $4 - if ;; label = @14 - block ;; label = @15 - local.get $1 - local.set $4 - local.get $0 - local.set $1 - br 5 (;@10;) - end - else - block ;; label = @15 - local.get $1 - local.set $4 - local.get $6 - local.set $1 - br 2 (;@13;) - end - end - end - end - else - block ;; label = @12 - i32.const 0 - local.set $4 - i32.const 0 - local.set $1 - end - end - end - local.get $4 - i32.eqz - local.get $1 - i32.eqz - i32.and - if (result i32) ;; label = @10 - block (result i32) ;; label = @11 - local.get $5 - i32.const 2 - local.get $17 - i32.shl - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.or - i32.and - local.tee $0 - i32.eqz - if ;; label = @12 - block ;; label = @13 - local.get $7 - local.set $0 - br 11 (;@2;) - end - end - local.get $0 - i32.const 0 - local.get $0 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $10 - local.get $0 - local.get $10 - i32.shr_u - local.tee $4 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $10 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - i32.add - i32.const 2 - i32.shl - i32.const 3940 - i32.add - i32.load - end - else - local.get $4 - end - local.tee $0 - br_if 0 (;@9;) - local.get $1 - local.set $4 - br 1 (;@8;) - end - loop $label$96 ;; label = @9 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.sub - local.tee $4 - local.get $3 - i32.lt_u - local.tee $10 - if ;; label = @10 - local.get $4 - local.set $3 - end - local.get $10 - if ;; label = @10 - local.get $0 - local.set $1 - end - local.get $0 - i32.load offset=16 - local.tee $4 - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.set $0 - br 2 (;@9;) - end - end - local.get $0 - i32.load offset=20 - local.tee $0 - br_if 0 (;@9;) - local.get $1 - local.set $4 - end - end - local.get $4 - if ;; label = @8 - local.get $3 - i32.const 3644 - i32.load - local.get $7 - i32.sub - i32.lt_u - if ;; label = @9 - block ;; label = @10 - local.get $4 - i32.const 3652 - i32.load - local.tee $12 - i32.lt_u - if ;; label = @11 - call $fimport$10 - end - local.get $4 - local.get $4 - local.get $7 - i32.add - local.tee $6 - i32.ge_u - if ;; label = @11 - call $fimport$10 - end - local.get $4 - i32.load offset=24 - local.set $10 - block $label$104 ;; label = @11 - local.get $4 - i32.load offset=12 - local.tee $0 - local.get $4 - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $4 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @14 - local.get $4 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @15 - block ;; label = @16 - i32.const 0 - local.set $13 - br 5 (;@11;) - end - end - end - loop $label$108 ;; label = @14 - local.get $0 - i32.const 20 - i32.add - local.tee $9 - i32.load - local.tee $11 - if ;; label = @15 - block ;; label = @16 - local.get $11 - local.set $0 - local.get $9 - local.set $1 - br 2 (;@14;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $9 - i32.load - local.tee $11 - if ;; label = @15 - block ;; label = @16 - local.get $11 - local.set $0 - local.get $9 - local.set $1 - br 2 (;@14;) - end - end - end - local.get $1 - local.get $12 - i32.lt_u - if ;; label = @14 - call $fimport$10 - else - block ;; label = @15 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $13 - end - end - end - else - block ;; label = @13 - local.get $4 - i32.load offset=8 - local.tee $9 - local.get $12 - i32.lt_u - if ;; label = @14 - call $fimport$10 - end - local.get $9 - i32.const 12 - i32.add - local.tee $11 - i32.load - local.get $4 - i32.ne - if ;; label = @14 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $4 - i32.eq - if ;; label = @14 - block ;; label = @15 - local.get $11 - local.get $0 - i32.store - local.get $1 - local.get $9 - i32.store - local.get $0 - local.set $13 - end - else - call $fimport$10 - end - end - end - end - block $label$118 ;; label = @11 - local.get $10 - if ;; label = @12 - block ;; label = @13 - local.get $4 - local.get $4 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @14 - block ;; label = @15 - local.get $0 - local.get $13 - i32.store - local.get $13 - i32.eqz - if ;; label = @16 - block ;; label = @17 - i32.const 3640 - local.get $5 - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $2 - i32.store - br 6 (;@11;) - end - end - end - else - block ;; label = @15 - local.get $10 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @16 - call $fimport$10 - end - local.get $10 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @16 - local.get $0 - local.get $13 - i32.store - else - local.get $10 - local.get $13 - i32.store offset=20 - end - local.get $13 - i32.eqz - if ;; label = @16 - block ;; label = @17 - local.get $5 - local.set $2 - br 6 (;@11;) - end - end - end - end - local.get $13 - i32.const 3652 - i32.load - local.tee $0 - i32.lt_u - if ;; label = @14 - call $fimport$10 - end - local.get $13 - local.get $10 - i32.store offset=24 - local.get $4 - i32.load offset=16 - local.tee $1 - if ;; label = @14 - local.get $1 - local.get $0 - i32.lt_u - if ;; label = @15 - call $fimport$10 - else - block ;; label = @16 - local.get $13 - local.get $1 - i32.store offset=16 - local.get $1 - local.get $13 - i32.store offset=24 - end - end - end - local.get $4 - i32.load offset=20 - local.tee $0 - if ;; label = @14 - local.get $0 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @15 - call $fimport$10 - else - block ;; label = @16 - local.get $13 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $13 - i32.store offset=24 - local.get $5 - local.set $2 - end - end - else - local.get $5 - local.set $2 - end - end - else - local.get $5 - local.set $2 - end - end - block $label$136 ;; label = @11 - local.get $3 - i32.const 16 - i32.lt_u - if ;; label = @12 - block ;; label = @13 - local.get $4 - local.get $3 - local.get $7 - i32.add - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $4 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - else - block ;; label = @13 - local.get $4 - local.get $7 - i32.const 3 - i32.or - i32.store offset=4 - local.get $6 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $3 - i32.add - local.get $3 - i32.store - local.get $3 - i32.const 3 - i32.shr_u - local.set $0 - local.get $3 - i32.const 256 - i32.lt_u - if ;; label = @14 - block ;; label = @15 - local.get $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.set $3 - i32.const 3636 - i32.load - local.tee $1 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @16 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @17 - call $fimport$10 - else - block ;; label = @18 - local.get $1 - local.set $16 - local.get $0 - local.set $8 - end - end - else - block ;; label = @17 - i32.const 3636 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $16 - local.get $3 - local.set $8 - end - end - local.get $16 - local.get $6 - i32.store - local.get $8 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $8 - i32.store offset=8 - local.get $6 - local.get $3 - i32.store offset=12 - br 4 (;@11;) - end - end - local.get $3 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @14 - local.get $3 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @15 - i32.const 31 - else - local.get $3 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $5 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $5 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $5 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - local.set $1 - local.get $6 - local.get $5 - i32.store offset=28 - local.get $6 - i32.const 16 - i32.add - local.tee $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store - local.get $2 - i32.const 1 - local.get $5 - i32.shl - local.tee $0 - i32.and - i32.eqz - if ;; label = @14 - block ;; label = @15 - i32.const 3640 - local.get $2 - local.get $0 - i32.or - i32.store - local.get $1 - local.get $6 - i32.store - local.get $6 - local.get $1 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 4 (;@11;) - end - end - local.get $1 - i32.load - local.set $0 - i32.const 25 - local.get $5 - i32.const 1 - i32.shr_u - i32.sub - local.set $1 - local.get $3 - local.get $5 - i32.const 31 - i32.eq - if (result i32) ;; label = @14 - i32.const 0 - else - local.get $1 - end - i32.shl - local.set $5 - block $label$151 ;; label = @14 - block $label$152 ;; label = @15 - block $label$153 ;; label = @16 - loop $label$154 ;; label = @17 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.eq - br_if 2 (;@15;) - local.get $5 - i32.const 1 - i32.shl - local.set $2 - local.get $0 - i32.const 16 - i32.add - local.get $5 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $5 - i32.load - local.tee $1 - i32.eqz - br_if 1 (;@16;) - local.get $2 - local.set $5 - local.get $1 - local.set $0 - br 0 (;@17;) - end - end - local.get $5 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @16 - call $fimport$10 - else - block ;; label = @17 - local.get $5 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 6 (;@11;) - end - end - br 1 (;@14;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $2 - i32.const 3652 - i32.load - local.tee $1 - i32.ge_u - local.get $0 - local.get $1 - i32.ge_u - i32.and - if ;; label = @15 - block ;; label = @16 - local.get $2 - local.get $6 - i32.store offset=12 - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $2 - i32.store offset=8 - local.get $6 - local.get $0 - i32.store offset=12 - local.get $6 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - end - end - local.get $14 - global.set $global$1 - local.get $4 - i32.const 8 - i32.add - return - end - else - local.get $7 - local.set $0 - end - else - local.get $7 - local.set $0 - end - end - else - local.get $7 - local.set $0 - end - end - end - end - end - i32.const 3644 - i32.load - local.tee $1 - local.get $0 - i32.ge_u - if ;; label = @2 - block ;; label = @3 - i32.const 3656 - i32.load - local.set $2 - local.get $1 - local.get $0 - i32.sub - local.tee $3 - i32.const 15 - i32.gt_u - if ;; label = @4 - block ;; label = @5 - i32.const 3656 - local.get $2 - local.get $0 - i32.add - local.tee $1 - i32.store - i32.const 3644 - local.get $3 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $1 - local.get $3 - i32.add - local.get $3 - i32.store - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - end - else - block ;; label = @5 - i32.const 3644 - i32.const 0 - i32.store - i32.const 3656 - i32.const 0 - i32.store - local.get $2 - local.get $1 - i32.const 3 - i32.or - i32.store offset=4 - local.get $2 - local.get $1 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - end - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - i32.const 3648 - i32.load - local.tee $10 - local.get $0 - i32.gt_u - if ;; label = @2 - block ;; label = @3 - i32.const 3648 - local.get $10 - local.get $0 - i32.sub - local.tee $3 - i32.store - i32.const 3660 - i32.const 3660 - i32.load - local.tee $2 - local.get $0 - i32.add - local.tee $1 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - i32.const 4108 - i32.load - if (result i32) ;; label = @2 - i32.const 4116 - i32.load - else - block (result i32) ;; label = @3 - i32.const 4116 - i32.const 4096 - i32.store - i32.const 4112 - i32.const 4096 - i32.store - i32.const 4120 - i32.const -1 - i32.store - i32.const 4124 - i32.const -1 - i32.store - i32.const 4128 - i32.const 0 - i32.store - i32.const 4080 - i32.const 0 - i32.store - local.get $18 - local.get $18 - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - local.tee $1 - i32.store - i32.const 4108 - local.get $1 - i32.store - i32.const 4096 - end - end - local.tee $1 - local.get $0 - i32.const 47 - i32.add - local.tee $13 - i32.add - local.tee $8 - i32.const 0 - local.get $1 - i32.sub - local.tee $4 - i32.and - local.tee $6 - local.get $0 - i32.le_u - if ;; label = @2 - block ;; label = @3 - local.get $14 - global.set $global$1 - i32.const 0 - return - end - end - i32.const 4076 - i32.load - local.tee $2 - if ;; label = @2 - i32.const 4068 - i32.load - local.tee $3 - local.get $6 - i32.add - local.tee $1 - local.get $3 - i32.le_u - local.get $1 - local.get $2 - i32.gt_u - i32.or - if ;; label = @3 - block ;; label = @4 - local.get $14 - global.set $global$1 - i32.const 0 - return - end - end - end - local.get $0 - i32.const 48 - i32.add - local.set $7 - block $label$171 ;; label = @2 - block $label$172 ;; label = @3 - i32.const 4080 - i32.load - i32.const 4 - i32.and - i32.eqz - if ;; label = @4 - block ;; label = @5 - block $label$174 ;; label = @6 - block $label$175 ;; label = @7 - block $label$176 ;; label = @8 - i32.const 3660 - i32.load - local.tee $3 - i32.eqz - br_if 0 (;@8;) - i32.const 4084 - local.set $2 - loop $label$177 ;; label = @9 - block $label$178 ;; label = @10 - local.get $2 - i32.load - local.tee $1 - local.get $3 - i32.le_u - if ;; label = @11 - local.get $1 - local.get $2 - i32.const 4 - i32.add - local.tee $5 - i32.load - i32.add - local.get $3 - i32.gt_u - br_if 1 (;@10;) - end - local.get $2 - i32.load offset=8 - local.tee $1 - i32.eqz - br_if 2 (;@8;) - local.get $1 - local.set $2 - br 1 (;@9;) - end - end - local.get $8 - local.get $10 - i32.sub - local.get $4 - i32.and - local.tee $3 - i32.const 2147483647 - i32.lt_u - if ;; label = @9 - local.get $3 - call $40 - local.tee $1 - local.get $2 - i32.load - local.get $5 - i32.load - i32.add - i32.eq - if ;; label = @10 - local.get $1 - i32.const -1 - i32.ne - br_if 7 (;@3;) - else - block ;; label = @11 - local.get $1 - local.set $2 - local.get $3 - local.set $1 - br 4 (;@7;) - end - end - end - br 2 (;@6;) - end - i32.const 0 - call $40 - local.tee $1 - i32.const -1 - i32.ne - if ;; label = @8 - block ;; label = @9 - i32.const 4112 - i32.load - local.tee $2 - i32.const -1 - i32.add - local.tee $5 - local.get $1 - local.tee $3 - i32.add - i32.const 0 - local.get $2 - i32.sub - i32.and - local.get $3 - i32.sub - local.set $2 - local.get $5 - local.get $3 - i32.and - if (result i32) ;; label = @10 - local.get $2 - else - i32.const 0 - end - local.get $6 - i32.add - local.tee $3 - i32.const 4068 - i32.load - local.tee $5 - i32.add - local.set $4 - local.get $3 - local.get $0 - i32.gt_u - local.get $3 - i32.const 2147483647 - i32.lt_u - i32.and - if ;; label = @10 - block ;; label = @11 - i32.const 4076 - i32.load - local.tee $2 - if ;; label = @12 - local.get $4 - local.get $5 - i32.le_u - local.get $4 - local.get $2 - i32.gt_u - i32.or - br_if 6 (;@6;) - end - local.get $3 - call $40 - local.tee $2 - local.get $1 - i32.eq - br_if 8 (;@3;) - local.get $3 - local.set $1 - br 4 (;@7;) - end - end - end - end - br 1 (;@6;) - end - i32.const 0 - local.get $1 - i32.sub - local.set $5 - local.get $7 - local.get $1 - i32.gt_u - local.get $1 - i32.const 2147483647 - i32.lt_u - local.get $2 - i32.const -1 - i32.ne - i32.and - i32.and - if ;; label = @7 - local.get $13 - local.get $1 - i32.sub - i32.const 4116 - i32.load - local.tee $3 - i32.add - i32.const 0 - local.get $3 - i32.sub - i32.and - local.tee $3 - i32.const 2147483647 - i32.lt_u - if ;; label = @8 - local.get $3 - call $40 - i32.const -1 - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $5 - call $40 - drop - br 4 (;@6;) - end - else - local.get $3 - local.get $1 - i32.add - local.set $3 - end - else - local.get $1 - local.set $3 - end - else - local.get $1 - local.set $3 - end - local.get $2 - i32.const -1 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $2 - local.set $1 - br 5 (;@3;) - end - end - end - i32.const 4080 - i32.const 4080 - i32.load - i32.const 4 - i32.or - i32.store - end - end - local.get $6 - i32.const 2147483647 - i32.lt_u - if ;; label = @4 - local.get $6 - call $40 - local.tee $1 - i32.const 0 - call $40 - local.tee $3 - i32.lt_u - local.get $1 - i32.const -1 - i32.ne - local.get $3 - i32.const -1 - i32.ne - i32.and - i32.and - if ;; label = @5 - local.get $3 - local.get $1 - i32.sub - local.tee $3 - local.get $0 - i32.const 40 - i32.add - i32.gt_u - br_if 2 (;@3;) - end - end - br 1 (;@2;) - end - i32.const 4068 - i32.const 4068 - i32.load - local.get $3 - i32.add - local.tee $2 - i32.store - local.get $2 - i32.const 4072 - i32.load - i32.gt_u - if ;; label = @3 - i32.const 4072 - local.get $2 - i32.store - end - block $label$198 ;; label = @3 - i32.const 3660 - i32.load - local.tee $8 - if ;; label = @4 - block ;; label = @5 - i32.const 4084 - local.set $2 - block $label$200 ;; label = @6 - block $label$201 ;; label = @7 - loop $label$202 ;; label = @8 - local.get $1 - local.get $2 - i32.load - local.tee $4 - local.get $2 - i32.const 4 - i32.add - local.tee $7 - i32.load - local.tee $5 - i32.add - i32.eq - br_if 1 (;@7;) - local.get $2 - i32.load offset=8 - local.tee $2 - br_if 0 (;@8;) - end - br 1 (;@6;) - end - local.get $2 - i32.load offset=12 - i32.const 8 - i32.and - i32.eqz - if ;; label = @7 - local.get $8 - local.get $1 - i32.lt_u - local.get $8 - local.get $4 - i32.ge_u - i32.and - if ;; label = @8 - block ;; label = @9 - local.get $7 - local.get $5 - local.get $3 - i32.add - i32.store - i32.const 3648 - i32.load - local.set $5 - i32.const 0 - local.get $8 - i32.const 8 - i32.add - local.tee $2 - i32.sub - i32.const 7 - i32.and - local.set $1 - i32.const 3660 - local.get $8 - local.get $2 - i32.const 7 - i32.and - if (result i32) ;; label = @10 - local.get $1 - else - i32.const 0 - local.tee $1 - end - i32.add - local.tee $2 - i32.store - i32.const 3648 - local.get $3 - local.get $1 - i32.sub - local.get $5 - i32.add - local.tee $1 - i32.store - local.get $2 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $1 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 3664 - i32.const 4124 - i32.load - i32.store - br 6 (;@3;) - end - end - end - end - local.get $1 - i32.const 3652 - i32.load - local.tee $2 - i32.lt_u - if ;; label = @6 - block ;; label = @7 - i32.const 3652 - local.get $1 - i32.store - local.get $1 - local.set $2 - end - end - local.get $1 - local.get $3 - i32.add - local.set $10 - i32.const 4084 - local.set $5 - block $label$208 ;; label = @6 - block $label$209 ;; label = @7 - loop $label$210 ;; label = @8 - local.get $5 - i32.load - local.get $10 - i32.eq - br_if 1 (;@7;) - local.get $5 - i32.load offset=8 - local.tee $5 - br_if 0 (;@8;) - i32.const 4084 - local.set $5 - end - br 1 (;@6;) - end - local.get $5 - i32.load offset=12 - i32.const 8 - i32.and - if ;; label = @7 - i32.const 4084 - local.set $5 - else - block ;; label = @8 - local.get $5 - local.get $1 - i32.store - local.get $5 - i32.const 4 - i32.add - local.tee $5 - local.get $5 - i32.load - local.get $3 - i32.add - i32.store - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $4 - i32.sub - i32.const 7 - i32.and - local.set $7 - i32.const 0 - local.get $10 - i32.const 8 - i32.add - local.tee $5 - i32.sub - i32.const 7 - i32.and - local.set $3 - local.get $1 - local.get $4 - i32.const 7 - i32.and - if (result i32) ;; label = @9 - local.get $7 - else - i32.const 0 - end - i32.add - local.tee $13 - local.get $0 - i32.add - local.set $6 - local.get $10 - local.get $5 - i32.const 7 - i32.and - if (result i32) ;; label = @9 - local.get $3 - else - i32.const 0 - end - i32.add - local.tee $4 - local.get $13 - i32.sub - local.get $0 - i32.sub - local.set $7 - local.get $13 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - block $label$217 ;; label = @9 - local.get $4 - local.get $8 - i32.eq - if ;; label = @10 - block ;; label = @11 - i32.const 3648 - i32.const 3648 - i32.load - local.get $7 - i32.add - local.tee $0 - i32.store - i32.const 3660 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - end - else - block ;; label = @11 - local.get $4 - i32.const 3656 - i32.load - i32.eq - if ;; label = @12 - block ;; label = @13 - i32.const 3644 - i32.const 3644 - i32.load - local.get $7 - i32.add - local.tee $0 - i32.store - i32.const 3656 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $0 - i32.add - local.get $0 - i32.store - br 4 (;@9;) - end - end - local.get $4 - i32.load offset=4 - local.tee $0 - i32.const 3 - i32.and - i32.const 1 - i32.eq - if (result i32) ;; label = @12 - block (result i32) ;; label = @13 - local.get $0 - i32.const -8 - i32.and - local.set $11 - local.get $0 - i32.const 3 - i32.shr_u - local.set $1 - block $label$222 ;; label = @14 - local.get $0 - i32.const 256 - i32.lt_u - if ;; label = @15 - block ;; label = @16 - local.get $4 - i32.load offset=12 - local.set $5 - block $label$224 ;; label = @17 - local.get $4 - i32.load offset=8 - local.tee $3 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.tee $0 - i32.ne - if ;; label = @18 - block ;; label = @19 - local.get $3 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $3 - i32.load offset=12 - local.get $4 - i32.eq - br_if 2 (;@17;) - call $fimport$10 - end - end - end - local.get $5 - local.get $3 - i32.eq - if ;; label = @17 - block ;; label = @18 - i32.const 3636 - i32.const 3636 - i32.load - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 4 (;@14;) - end - end - block $label$228 ;; label = @17 - local.get $5 - local.get $0 - i32.eq - if ;; label = @18 - local.get $5 - i32.const 8 - i32.add - local.set $20 - else - block ;; label = @19 - local.get $5 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $5 - i32.const 8 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - block ;; label = @21 - local.get $0 - local.set $20 - br 4 (;@17;) - end - end - call $fimport$10 - end - end - end - local.get $3 - local.get $5 - i32.store offset=12 - local.get $20 - local.get $3 - i32.store - end - else - block ;; label = @16 - local.get $4 - i32.load offset=24 - local.set $8 - block $label$234 ;; label = @17 - local.get $4 - i32.load offset=12 - local.tee $0 - local.get $4 - i32.eq - if ;; label = @18 - block ;; label = @19 - local.get $4 - i32.const 16 - i32.add - local.tee $3 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @20 - local.get $3 - i32.load - local.tee $0 - if ;; label = @21 - local.get $3 - local.set $1 - else - block ;; label = @22 - i32.const 0 - local.set $12 - br 5 (;@17;) - end - end - end - loop $label$239 ;; label = @20 - local.get $0 - i32.const 20 - i32.add - local.tee $5 - i32.load - local.tee $3 - if ;; label = @21 - block ;; label = @22 - local.get $3 - local.set $0 - local.get $5 - local.set $1 - br 2 (;@20;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $5 - i32.load - local.tee $3 - if ;; label = @21 - block ;; label = @22 - local.get $3 - local.set $0 - local.get $5 - local.set $1 - br 2 (;@20;) - end - end - end - local.get $1 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - else - block ;; label = @21 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $12 - end - end - end - else - block ;; label = @19 - local.get $4 - i32.load offset=8 - local.tee $5 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $5 - i32.const 12 - i32.add - local.tee $3 - i32.load - local.get $4 - i32.ne - if ;; label = @20 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - block ;; label = @21 - local.get $3 - local.get $0 - i32.store - local.get $1 - local.get $5 - i32.store - local.get $0 - local.set $12 - end - else - call $fimport$10 - end - end - end - end - local.get $8 - i32.eqz - br_if 2 (;@14;) - block $label$249 ;; label = @17 - local.get $4 - local.get $4 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @18 - block ;; label = @19 - local.get $0 - local.get $12 - i32.store - local.get $12 - br_if 2 (;@17;) - i32.const 3640 - i32.const 3640 - i32.load - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 5 (;@14;) - end - else - block ;; label = @19 - local.get $8 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $8 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - local.get $0 - local.get $12 - i32.store - else - local.get $8 - local.get $12 - i32.store offset=20 - end - local.get $12 - i32.eqz - br_if 5 (;@14;) - end - end - end - local.get $12 - i32.const 3652 - i32.load - local.tee $1 - i32.lt_u - if ;; label = @17 - call $fimport$10 - end - local.get $12 - local.get $8 - i32.store offset=24 - local.get $4 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.tee $3 - if ;; label = @17 - local.get $3 - local.get $1 - i32.lt_u - if ;; label = @18 - call $fimport$10 - else - block ;; label = @19 - local.get $12 - local.get $3 - i32.store offset=16 - local.get $3 - local.get $12 - i32.store offset=24 - end - end - end - local.get $0 - i32.load offset=4 - local.tee $0 - i32.eqz - br_if 2 (;@14;) - local.get $0 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @17 - call $fimport$10 - else - block ;; label = @18 - local.get $12 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $12 - i32.store offset=24 - end - end - end - end - end - local.get $11 - local.get $7 - i32.add - local.set $7 - local.get $4 - local.get $11 - i32.add - end - else - local.get $4 - end - local.tee $0 - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const -2 - i32.and - i32.store - local.get $6 - local.get $7 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $7 - i32.add - local.get $7 - i32.store - local.get $7 - i32.const 3 - i32.shr_u - local.set $0 - local.get $7 - i32.const 256 - i32.lt_u - if ;; label = @12 - block ;; label = @13 - local.get $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.set $3 - block $label$263 ;; label = @14 - i32.const 3636 - i32.load - local.tee $1 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @15 - block ;; label = @16 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 3652 - i32.load - i32.ge_u - if ;; label = @17 - block ;; label = @18 - local.get $1 - local.set $21 - local.get $0 - local.set $9 - br 4 (;@14;) - end - end - call $fimport$10 - end - else - block ;; label = @16 - i32.const 3636 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $21 - local.get $3 - local.set $9 - end - end - end - local.get $21 - local.get $6 - i32.store - local.get $9 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $9 - i32.store offset=8 - local.get $6 - local.get $3 - i32.store offset=12 - br 4 (;@9;) - end - end - block $label$267 (result i32) ;; label = @12 - local.get $7 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @13 - block (result i32) ;; label = @14 - i32.const 31 - local.get $7 - i32.const 16777215 - i32.gt_u - br_if 2 (;@12;) - drop - local.get $7 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $3 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $3 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - end - local.tee $2 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - local.set $3 - local.get $6 - local.get $2 - i32.store offset=28 - local.get $6 - i32.const 16 - i32.add - local.tee $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store - i32.const 3640 - i32.load - local.tee $1 - i32.const 1 - local.get $2 - i32.shl - local.tee $0 - i32.and - i32.eqz - if ;; label = @12 - block ;; label = @13 - i32.const 3640 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $3 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 4 (;@9;) - end - end - local.get $3 - i32.load - local.set $0 - i32.const 25 - local.get $2 - i32.const 1 - i32.shr_u - i32.sub - local.set $1 - local.get $7 - local.get $2 - i32.const 31 - i32.eq - if (result i32) ;; label = @12 - i32.const 0 - else - local.get $1 - end - i32.shl - local.set $2 - block $label$273 ;; label = @12 - block $label$274 ;; label = @13 - block $label$275 ;; label = @14 - loop $label$276 ;; label = @15 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.eq - br_if 2 (;@13;) - local.get $2 - i32.const 1 - i32.shl - local.set $3 - local.get $0 - i32.const 16 - i32.add - local.get $2 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $2 - i32.load - local.tee $1 - i32.eqz - br_if 1 (;@14;) - local.get $3 - local.set $2 - local.get $1 - local.set $0 - br 0 (;@15;) - end - end - local.get $2 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$10 - else - block ;; label = @15 - local.get $2 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 6 (;@9;) - end - end - br 1 (;@12;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $2 - i32.const 3652 - i32.load - local.tee $1 - i32.ge_u - local.get $0 - local.get $1 - i32.ge_u - i32.and - if ;; label = @13 - block ;; label = @14 - local.get $2 - local.get $6 - i32.store offset=12 - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $2 - i32.store offset=8 - local.get $6 - local.get $0 - i32.store offset=12 - local.get $6 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - end - end - local.get $14 - global.set $global$1 - local.get $13 - i32.const 8 - i32.add - return - end - end - end - loop $label$281 ;; label = @6 - block $label$282 ;; label = @7 - local.get $5 - i32.load - local.tee $2 - local.get $8 - i32.le_u - if ;; label = @8 - local.get $2 - local.get $5 - i32.load offset=4 - i32.add - local.tee $13 - local.get $8 - i32.gt_u - br_if 1 (;@7;) - end - local.get $5 - i32.load offset=8 - local.set $5 - br 1 (;@6;) - end - end - i32.const 0 - local.get $13 - i32.const -47 - i32.add - local.tee $7 - i32.const 8 - i32.add - local.tee $5 - i32.sub - i32.const 7 - i32.and - local.set $2 - local.get $7 - local.get $5 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $2 - else - i32.const 0 - end - i32.add - local.tee $2 - local.get $8 - i32.const 16 - i32.add - local.tee $12 - i32.lt_u - if (result i32) ;; label = @6 - local.get $8 - else - local.get $2 - end - local.tee $7 - i32.const 8 - i32.add - local.set $10 - local.get $7 - i32.const 24 - i32.add - local.set $5 - local.get $3 - i32.const -40 - i32.add - local.set $9 - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $4 - i32.sub - i32.const 7 - i32.and - local.set $2 - i32.const 3660 - local.get $1 - local.get $4 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $2 - else - i32.const 0 - local.tee $2 - end - i32.add - local.tee $4 - i32.store - i32.const 3648 - local.get $9 - local.get $2 - i32.sub - local.tee $2 - i32.store - local.get $4 - local.get $2 - i32.const 1 - i32.or - i32.store offset=4 - local.get $4 - local.get $2 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 3664 - i32.const 4124 - i32.load - i32.store - local.get $7 - i32.const 4 - i32.add - local.tee $2 - i32.const 27 - i32.store - local.get $10 - i32.const 4084 - i64.load align=4 - i64.store align=4 - local.get $10 - i32.const 4092 - i64.load align=4 - i64.store offset=8 align=4 - i32.const 4084 - local.get $1 - i32.store - i32.const 4088 - local.get $3 - i32.store - i32.const 4096 - i32.const 0 - i32.store - i32.const 4092 - local.get $10 - i32.store - local.get $5 - local.set $1 - loop $label$290 ;; label = @6 - local.get $1 - i32.const 4 - i32.add - local.tee $1 - i32.const 7 - i32.store - local.get $1 - i32.const 4 - i32.add - local.get $13 - i32.lt_u - br_if 0 (;@6;) - end - local.get $7 - local.get $8 - i32.ne - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.get $2 - i32.load - i32.const -2 - i32.and - i32.store - local.get $8 - local.get $7 - local.get $8 - i32.sub - local.tee $4 - i32.const 1 - i32.or - i32.store offset=4 - local.get $7 - local.get $4 - i32.store - local.get $4 - i32.const 3 - i32.shr_u - local.set $1 - local.get $4 - i32.const 256 - i32.lt_u - if ;; label = @8 - block ;; label = @9 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.set $2 - i32.const 3636 - i32.load - local.tee $3 - i32.const 1 - local.get $1 - i32.shl - local.tee $1 - i32.and - if ;; label = @10 - local.get $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $1 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$10 - else - block ;; label = @12 - local.get $3 - local.set $15 - local.get $1 - local.set $11 - end - end - else - block ;; label = @11 - i32.const 3636 - local.get $3 - local.get $1 - i32.or - i32.store - local.get $2 - i32.const 8 - i32.add - local.set $15 - local.get $2 - local.set $11 - end - end - local.get $15 - local.get $8 - i32.store - local.get $11 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $11 - i32.store offset=8 - local.get $8 - local.get $2 - i32.store offset=12 - br 6 (;@3;) - end - end - local.get $4 - i32.const 8 - i32.shr_u - local.tee $1 - if (result i32) ;; label = @8 - local.get $4 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @9 - i32.const 31 - else - local.get $4 - i32.const 14 - local.get $1 - local.get $1 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $2 - i32.shl - local.tee $3 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $1 - local.get $2 - i32.or - local.get $3 - local.get $1 - i32.shl - local.tee $3 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $1 - i32.or - i32.sub - local.get $3 - local.get $1 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $1 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $1 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $5 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - local.set $2 - local.get $8 - local.get $5 - i32.store offset=28 - local.get $8 - i32.const 0 - i32.store offset=20 - local.get $12 - i32.const 0 - i32.store - i32.const 3640 - i32.load - local.tee $3 - i32.const 1 - local.get $5 - i32.shl - local.tee $1 - i32.and - i32.eqz - if ;; label = @8 - block ;; label = @9 - i32.const 3640 - local.get $3 - local.get $1 - i32.or - i32.store - local.get $2 - local.get $8 - i32.store - local.get $8 - local.get $2 - i32.store offset=24 - local.get $8 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $8 - i32.store offset=8 - br 6 (;@3;) - end - end - local.get $2 - i32.load - local.set $1 - i32.const 25 - local.get $5 - i32.const 1 - i32.shr_u - i32.sub - local.set $3 - local.get $4 - local.get $5 - i32.const 31 - i32.eq - if (result i32) ;; label = @8 - i32.const 0 - else - local.get $3 - end - i32.shl - local.set $5 - block $label$304 ;; label = @8 - block $label$305 ;; label = @9 - block $label$306 ;; label = @10 - loop $label$307 ;; label = @11 - local.get $1 - i32.load offset=4 - i32.const -8 - i32.and - local.get $4 - i32.eq - br_if 2 (;@9;) - local.get $5 - i32.const 1 - i32.shl - local.set $2 - local.get $1 - i32.const 16 - i32.add - local.get $5 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $5 - i32.load - local.tee $3 - i32.eqz - br_if 1 (;@10;) - local.get $2 - local.set $5 - local.get $3 - local.set $1 - br 0 (;@11;) - end - end - local.get $5 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $5 - local.get $8 - i32.store - local.get $8 - local.get $1 - i32.store offset=24 - local.get $8 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $8 - i32.store offset=8 - br 8 (;@3;) - end - end - br 1 (;@8;) - end - local.get $1 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $5 - i32.const 3652 - i32.load - local.tee $3 - i32.ge_u - local.get $1 - local.get $3 - i32.ge_u - i32.and - if ;; label = @9 - block ;; label = @10 - local.get $5 - local.get $8 - i32.store offset=12 - local.get $2 - local.get $8 - i32.store - local.get $8 - local.get $5 - i32.store offset=8 - local.get $8 - local.get $1 - i32.store offset=12 - local.get $8 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - end - end - else - block ;; label = @5 - i32.const 3652 - i32.load - local.tee $2 - i32.eqz - local.get $1 - local.get $2 - i32.lt_u - i32.or - if ;; label = @6 - i32.const 3652 - local.get $1 - i32.store - end - i32.const 4084 - local.get $1 - i32.store - i32.const 4088 - local.get $3 - i32.store - i32.const 4096 - i32.const 0 - i32.store - i32.const 3672 - i32.const 4108 - i32.load - i32.store - i32.const 3668 - i32.const -1 - i32.store - i32.const 0 - local.set $2 - loop $label$314 ;; label = @6 - local.get $2 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.tee $5 - local.get $5 - i32.store offset=12 - local.get $5 - local.get $5 - i32.store offset=8 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 32 - i32.ne - br_if 0 (;@6;) - end - local.get $3 - i32.const -40 - i32.add - local.set $5 - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $2 - i32.sub - i32.const 7 - i32.and - local.set $3 - i32.const 3660 - local.get $1 - local.get $2 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $3 - else - i32.const 0 - end - local.tee $1 - i32.add - local.tee $3 - i32.store - i32.const 3648 - local.get $5 - local.get $1 - i32.sub - local.tee $1 - i32.store - local.get $3 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $1 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 3664 - i32.const 4124 - i32.load - i32.store - end - end - end - i32.const 3648 - i32.load - local.tee $1 - local.get $0 - i32.gt_u - if ;; label = @3 - block ;; label = @4 - i32.const 3648 - local.get $1 - local.get $0 - i32.sub - local.tee $3 - i32.store - i32.const 3660 - i32.const 3660 - i32.load - local.tee $2 - local.get $0 - i32.add - local.tee $1 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - end - call $12 - i32.const 12 - i32.store - local.get $14 - global.set $global$1 - i32.const 0 - end - ) - (func $38 (;51;) (type $2) (param $0 i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) - block $label$1 ;; label = @1 - local.get $0 - i32.eqz - if ;; label = @2 - return - end - local.get $0 - i32.const -8 - i32.add - local.tee $1 - i32.const 3652 - i32.load - local.tee $11 - i32.lt_u - if ;; label = @2 - call $fimport$10 - end - local.get $0 - i32.const -4 - i32.add - i32.load - local.tee $0 - i32.const 3 - i32.and - local.tee $8 - i32.const 1 - i32.eq - if ;; label = @2 - call $fimport$10 - end - local.get $1 - local.get $0 - i32.const -8 - i32.and - local.tee $4 - i32.add - local.set $6 - block $label$5 ;; label = @2 - local.get $0 - i32.const 1 - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $1 - local.set $3 - local.get $4 - local.set $2 - end - else - block ;; label = @4 - local.get $8 - i32.eqz - if ;; label = @5 - return - end - local.get $1 - i32.const 0 - local.get $1 - i32.load - local.tee $8 - i32.sub - i32.add - local.tee $0 - local.get $11 - i32.lt_u - if ;; label = @5 - call $fimport$10 - end - local.get $8 - local.get $4 - i32.add - local.set $1 - local.get $0 - i32.const 3656 - i32.load - i32.eq - if ;; label = @5 - block ;; label = @6 - local.get $6 - i32.const 4 - i32.add - local.tee $2 - i32.load - local.tee $3 - i32.const 3 - i32.and - i32.const 3 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 6 (;@2;) - end - end - i32.const 3644 - local.get $1 - i32.store - local.get $2 - local.get $3 - i32.const -2 - i32.and - i32.store - local.get $0 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $0 - local.get $1 - i32.add - local.get $1 - i32.store - return - end - end - local.get $8 - i32.const 3 - i32.shr_u - local.set $10 - local.get $8 - i32.const 256 - i32.lt_u - if ;; label = @5 - block ;; label = @6 - local.get $0 - i32.load offset=12 - local.set $3 - local.get $0 - i32.load offset=8 - local.tee $4 - local.get $10 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.tee $2 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $4 - local.get $11 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $4 - i32.load offset=12 - local.get $0 - i32.ne - if ;; label = @9 - call $fimport$10 - end - end - end - local.get $3 - local.get $4 - i32.eq - if ;; label = @7 - block ;; label = @8 - i32.const 3636 - i32.const 3636 - i32.load - i32.const 1 - local.get $10 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 6 (;@2;) - end - end - local.get $3 - local.get $2 - i32.eq - if ;; label = @7 - local.get $3 - i32.const 8 - i32.add - local.set $5 - else - block ;; label = @8 - local.get $3 - local.get $11 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $3 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.get $0 - i32.eq - if ;; label = @9 - local.get $2 - local.set $5 - else - call $fimport$10 - end - end - end - local.get $4 - local.get $3 - i32.store offset=12 - local.get $5 - local.get $4 - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 4 (;@2;) - end - end - local.get $0 - i32.load offset=24 - local.set $12 - block $label$22 ;; label = @5 - local.get $0 - i32.load offset=12 - local.tee $4 - local.get $0 - i32.eq - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 16 - i32.add - local.tee $5 - i32.const 4 - i32.add - local.tee $8 - i32.load - local.tee $4 - if ;; label = @8 - local.get $8 - local.set $5 - else - local.get $5 - i32.load - local.tee $4 - i32.eqz - if ;; label = @9 - block ;; label = @10 - i32.const 0 - local.set $7 - br 5 (;@5;) - end - end - end - loop $label$27 ;; label = @8 - local.get $4 - i32.const 20 - i32.add - local.tee $8 - i32.load - local.tee $10 - if ;; label = @9 - block ;; label = @10 - local.get $10 - local.set $4 - local.get $8 - local.set $5 - br 2 (;@8;) - end - end - local.get $4 - i32.const 16 - i32.add - local.tee $8 - i32.load - local.tee $10 - if ;; label = @9 - block ;; label = @10 - local.get $10 - local.set $4 - local.get $8 - local.set $5 - br 2 (;@8;) - end - end - end - local.get $5 - local.get $11 - i32.lt_u - if ;; label = @8 - call $fimport$10 - else - block ;; label = @9 - local.get $5 - i32.const 0 - i32.store - local.get $4 - local.set $7 - end - end - end - else - block ;; label = @7 - local.get $0 - i32.load offset=8 - local.tee $5 - local.get $11 - i32.lt_u - if ;; label = @8 - call $fimport$10 - end - local.get $5 - i32.const 12 - i32.add - local.tee $8 - i32.load - local.get $0 - i32.ne - if ;; label = @8 - call $fimport$10 - end - local.get $4 - i32.const 8 - i32.add - local.tee $10 - i32.load - local.get $0 - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $8 - local.get $4 - i32.store - local.get $10 - local.get $5 - i32.store - local.get $4 - local.set $7 - end - else - call $fimport$10 - end - end - end - end - local.get $12 - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $0 - i32.load offset=28 - local.tee $4 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - local.tee $5 - i32.load - i32.eq - if ;; label = @7 - block ;; label = @8 - local.get $5 - local.get $7 - i32.store - local.get $7 - i32.eqz - if ;; label = @9 - block ;; label = @10 - i32.const 3640 - i32.const 3640 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 8 (;@2;) - end - end - end - else - block ;; label = @8 - local.get $12 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $12 - i32.const 16 - i32.add - local.tee $4 - i32.load - local.get $0 - i32.eq - if ;; label = @9 - local.get $4 - local.get $7 - i32.store - else - local.get $12 - local.get $7 - i32.store offset=20 - end - local.get $7 - i32.eqz - if ;; label = @9 - block ;; label = @10 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 8 (;@2;) - end - end - end - end - local.get $7 - i32.const 3652 - i32.load - local.tee $5 - i32.lt_u - if ;; label = @7 - call $fimport$10 - end - local.get $7 - local.get $12 - i32.store offset=24 - local.get $0 - i32.const 16 - i32.add - local.tee $8 - i32.load - local.tee $4 - if ;; label = @7 - local.get $4 - local.get $5 - i32.lt_u - if ;; label = @8 - call $fimport$10 - else - block ;; label = @9 - local.get $7 - local.get $4 - i32.store offset=16 - local.get $4 - local.get $7 - i32.store offset=24 - end - end - end - local.get $8 - i32.load offset=4 - local.tee $4 - if ;; label = @7 - local.get $4 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @8 - call $fimport$10 - else - block ;; label = @9 - local.get $7 - local.get $4 - i32.store offset=20 - local.get $4 - local.get $7 - i32.store offset=24 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - else - block ;; label = @8 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - end - else - block ;; label = @6 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - end - end - end - local.get $3 - local.get $6 - i32.ge_u - if ;; label = @2 - call $fimport$10 - end - local.get $6 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 1 - i32.and - i32.eqz - if ;; label = @2 - call $fimport$10 - end - local.get $0 - i32.const 2 - i32.and - if ;; label = @2 - block ;; label = @3 - local.get $1 - local.get $0 - i32.const -2 - i32.and - i32.store - local.get $3 - local.get $2 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $2 - i32.add - local.get $2 - i32.store - end - else - block ;; label = @3 - local.get $6 - i32.const 3660 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 3648 - i32.const 3648 - i32.load - local.get $2 - i32.add - local.tee $0 - i32.store - i32.const 3660 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - i32.const 3656 - i32.load - i32.ne - if ;; label = @6 - return - end - i32.const 3656 - i32.const 0 - i32.store - i32.const 3644 - i32.const 0 - i32.store - return - end - end - local.get $6 - i32.const 3656 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 3644 - i32.const 3644 - i32.load - local.get $2 - i32.add - local.tee $0 - i32.store - i32.const 3656 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $0 - i32.add - local.get $0 - i32.store - return - end - end - local.get $0 - i32.const -8 - i32.and - local.get $2 - i32.add - local.set $5 - local.get $0 - i32.const 3 - i32.shr_u - local.set $4 - block $label$61 ;; label = @4 - local.get $0 - i32.const 256 - i32.lt_u - if ;; label = @5 - block ;; label = @6 - local.get $6 - i32.load offset=12 - local.set $2 - local.get $6 - i32.load offset=8 - local.tee $1 - local.get $4 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.tee $0 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $1 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $1 - i32.load offset=12 - local.get $6 - i32.ne - if ;; label = @9 - call $fimport$10 - end - end - end - local.get $2 - local.get $1 - i32.eq - if ;; label = @7 - block ;; label = @8 - i32.const 3636 - i32.const 3636 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 4 (;@4;) - end - end - local.get $2 - local.get $0 - i32.eq - if ;; label = @7 - local.get $2 - i32.const 8 - i32.add - local.set $14 - else - block ;; label = @8 - local.get $2 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $2 - i32.const 8 - i32.add - local.tee $0 - i32.load - local.get $6 - i32.eq - if ;; label = @9 - local.get $0 - local.set $14 - else - call $fimport$10 - end - end - end - local.get $1 - local.get $2 - i32.store offset=12 - local.get $14 - local.get $1 - i32.store - end - else - block ;; label = @6 - local.get $6 - i32.load offset=24 - local.set $7 - block $label$73 ;; label = @7 - local.get $6 - i32.load offset=12 - local.tee $0 - local.get $6 - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $6 - i32.const 16 - i32.add - local.tee $2 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - if ;; label = @10 - local.get $1 - local.set $2 - else - local.get $2 - i32.load - local.tee $0 - i32.eqz - if ;; label = @11 - block ;; label = @12 - i32.const 0 - local.set $9 - br 5 (;@7;) - end - end - end - loop $label$78 ;; label = @10 - local.get $0 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $4 - if ;; label = @11 - block ;; label = @12 - local.get $4 - local.set $0 - local.get $1 - local.set $2 - br 2 (;@10;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $4 - if ;; label = @11 - block ;; label = @12 - local.get $4 - local.set $0 - local.get $1 - local.set $2 - br 2 (;@10;) - end - end - end - local.get $2 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $2 - i32.const 0 - i32.store - local.get $0 - local.set $9 - end - end - end - else - block ;; label = @9 - local.get $6 - i32.load offset=8 - local.tee $2 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - end - local.get $2 - i32.const 12 - i32.add - local.tee $1 - i32.load - local.get $6 - i32.ne - if ;; label = @10 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $4 - i32.load - local.get $6 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $1 - local.get $0 - i32.store - local.get $4 - local.get $2 - i32.store - local.get $0 - local.set $9 - end - else - call $fimport$10 - end - end - end - end - local.get $7 - if ;; label = @7 - block ;; label = @8 - local.get $6 - local.get $6 - i32.load offset=28 - local.tee $0 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - local.tee $2 - i32.load - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $2 - local.get $9 - i32.store - local.get $9 - i32.eqz - if ;; label = @11 - block ;; label = @12 - i32.const 3640 - i32.const 3640 - i32.load - i32.const 1 - local.get $0 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 8 (;@4;) - end - end - end - else - block ;; label = @10 - local.get $7 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$10 - end - local.get $7 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $6 - i32.eq - if ;; label = @11 - local.get $0 - local.get $9 - i32.store - else - local.get $7 - local.get $9 - i32.store offset=20 - end - local.get $9 - i32.eqz - br_if 6 (;@4;) - end - end - local.get $9 - i32.const 3652 - i32.load - local.tee $2 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $9 - local.get $7 - i32.store offset=24 - local.get $6 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - if ;; label = @9 - local.get $0 - local.get $2 - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $9 - local.get $0 - i32.store offset=16 - local.get $0 - local.get $9 - i32.store offset=24 - end - end - end - local.get $1 - i32.load offset=4 - local.tee $0 - if ;; label = @9 - local.get $0 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $9 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $9 - i32.store offset=24 - end - end - end - end - end - end - end - end - local.get $3 - local.get $5 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $5 - i32.add - local.get $5 - i32.store - local.get $3 - i32.const 3656 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 3644 - local.get $5 - i32.store - return - end - else - local.get $5 - local.set $2 - end - end - end - local.get $2 - i32.const 3 - i32.shr_u - local.set $1 - local.get $2 - i32.const 256 - i32.lt_u - if ;; label = @2 - block ;; label = @3 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3676 - i32.add - local.set $0 - i32.const 3636 - i32.load - local.tee $2 - i32.const 1 - local.get $1 - i32.shl - local.tee $1 - i32.and - if ;; label = @4 - local.get $0 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $1 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @5 - call $fimport$10 - else - block ;; label = @6 - local.get $2 - local.set $15 - local.get $1 - local.set $13 - end - end - else - block ;; label = @5 - i32.const 3636 - local.get $2 - local.get $1 - i32.or - i32.store - local.get $0 - i32.const 8 - i32.add - local.set $15 - local.get $0 - local.set $13 - end - end - local.get $15 - local.get $3 - i32.store - local.get $13 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $13 - i32.store offset=8 - local.get $3 - local.get $0 - i32.store offset=12 - return - end - end - local.get $2 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @2 - local.get $2 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @3 - i32.const 31 - else - local.get $2 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $4 - local.get $0 - i32.or - local.get $1 - local.get $4 - i32.shl - local.tee $0 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $1 - i32.or - i32.sub - local.get $0 - local.get $1 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $1 - i32.const 2 - i32.shl - i32.const 3940 - i32.add - local.set $0 - local.get $3 - local.get $1 - i32.store offset=28 - local.get $3 - i32.const 0 - i32.store offset=20 - local.get $3 - i32.const 0 - i32.store offset=16 - block $label$113 ;; label = @2 - i32.const 3640 - i32.load - local.tee $4 - i32.const 1 - local.get $1 - i32.shl - local.tee $5 - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $0 - i32.load - local.set $0 - i32.const 25 - local.get $1 - i32.const 1 - i32.shr_u - i32.sub - local.set $4 - local.get $2 - local.get $1 - i32.const 31 - i32.eq - if (result i32) ;; label = @5 - i32.const 0 - else - local.get $4 - end - i32.shl - local.set $1 - block $label$117 ;; label = @5 - block $label$118 ;; label = @6 - block $label$119 ;; label = @7 - loop $label$120 ;; label = @8 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $2 - i32.eq - br_if 2 (;@6;) - local.get $1 - i32.const 1 - i32.shl - local.set $4 - local.get $0 - i32.const 16 - i32.add - local.get $1 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $1 - i32.load - local.tee $5 - i32.eqz - br_if 1 (;@7;) - local.get $4 - local.set $1 - local.get $5 - local.set $0 - br 0 (;@8;) - end - end - local.get $1 - i32.const 3652 - i32.load - i32.lt_u - if ;; label = @7 - call $fimport$10 - else - block ;; label = @8 - local.get $1 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.store offset=24 - local.get $3 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $3 - i32.store offset=8 - br 6 (;@2;) - end - end - br 1 (;@5;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $2 - i32.const 3652 - i32.load - local.tee $4 - i32.ge_u - local.get $0 - local.get $4 - i32.ge_u - i32.and - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.get $3 - i32.store offset=12 - local.get $1 - local.get $3 - i32.store - local.get $3 - local.get $2 - i32.store offset=8 - local.get $3 - local.get $0 - i32.store offset=12 - local.get $3 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - else - block ;; label = @4 - i32.const 3640 - local.get $4 - local.get $5 - i32.or - i32.store - local.get $0 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.store offset=24 - local.get $3 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $3 - i32.store offset=8 - end - end - end - i32.const 3668 - i32.const 3668 - i32.load - i32.const -1 - i32.add - local.tee $0 - i32.store - local.get $0 - if ;; label = @2 - return - else - i32.const 4092 - local.set $0 - end - loop $label$128 ;; label = @2 - local.get $0 - i32.load - local.tee $2 - i32.const 8 - i32.add - local.set $0 - local.get $2 - br_if 0 (;@2;) - end - i32.const 3668 - i32.const -1 - i32.store - end - ) - (func $39 (;52;) (type $6) - nop - ) - (func $40 (;53;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$0 - i32.load - local.tee $2 - local.get $0 - i32.const 15 - i32.add - i32.const -16 - i32.and - local.tee $0 - i32.add - local.set $1 - local.get $0 - i32.const 0 - i32.gt_s - local.get $1 - local.get $2 - i32.lt_s - i32.and - local.get $1 - i32.const 0 - i32.lt_s - i32.or - if ;; label = @2 - block ;; label = @3 - call $fimport$6 - drop - i32.const 12 - call $fimport$11 - i32.const -1 - return - end - end - global.get $global$0 - local.get $1 - i32.store - local.get $1 - call $fimport$5 - i32.gt_s - if ;; label = @2 - call $fimport$4 - i32.eqz - if ;; label = @3 - block ;; label = @4 - i32.const 12 - call $fimport$11 - global.get $global$0 - local.get $2 - i32.store - i32.const -1 - return - end - end - end - local.get $2 - end - ) - (func $41 (;54;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - local.get $2 - i32.add - local.set $4 - local.get $2 - i32.const 20 - i32.ge_s - if ;; label = @2 - block ;; label = @3 - local.get $1 - i32.const 255 - i32.and - local.set $1 - local.get $0 - i32.const 3 - i32.and - local.tee $3 - if ;; label = @4 - block ;; label = @5 - local.get $0 - i32.const 4 - i32.add - local.get $3 - i32.sub - local.set $3 - loop $label$4 ;; label = @6 - local.get $0 - local.get $3 - i32.lt_s - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.get $1 - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - br 2 (;@6;) - end - end - end - end - end - local.get $1 - local.get $1 - i32.const 8 - i32.shl - i32.or - local.get $1 - i32.const 16 - i32.shl - i32.or - local.get $1 - i32.const 24 - i32.shl - i32.or - local.set $3 - local.get $4 - i32.const -4 - i32.and - local.set $5 - loop $label$6 ;; label = @4 - local.get $0 - local.get $5 - i32.lt_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $3 - i32.store - local.get $0 - i32.const 4 - i32.add - local.set $0 - br 2 (;@4;) - end - end - end - end - end - loop $label$8 ;; label = @2 - local.get $0 - local.get $4 - i32.lt_s - if ;; label = @3 - block ;; label = @4 - local.get $0 - local.get $1 - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - br 2 (;@2;) - end - end - end - local.get $0 - local.get $2 - i32.sub - end - ) - (func $42 (;55;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - block $label$1 (result i32) ;; label = @1 - local.get $2 - i32.const 4096 - i32.ge_s - if ;; label = @2 - local.get $0 - local.get $1 - local.get $2 - call $fimport$12 - return - end - local.get $0 - local.set $3 - local.get $0 - i32.const 3 - i32.and - local.get $1 - i32.const 3 - i32.and - i32.eq - if ;; label = @2 - block ;; label = @3 - loop $label$4 ;; label = @4 - local.get $0 - i32.const 3 - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $2 - i32.eqz - if ;; label = @7 - local.get $3 - return - end - local.get $0 - local.get $1 - i32.load8_s - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - br 2 (;@4;) - end - end - end - loop $label$7 ;; label = @4 - local.get $2 - i32.const 4 - i32.ge_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $1 - i32.load - i32.store - local.get $0 - i32.const 4 - i32.add - local.set $0 - local.get $1 - i32.const 4 - i32.add - local.set $1 - local.get $2 - i32.const 4 - i32.sub - local.set $2 - br 2 (;@4;) - end - end - end - end - end - loop $label$9 ;; label = @2 - local.get $2 - i32.const 0 - i32.gt_s - if ;; label = @3 - block ;; label = @4 - local.get $0 - local.get $1 - i32.load8_s - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - br 2 (;@2;) - end - end - end - local.get $3 - end - ) - (func $43 (;56;) (type $3) (result i32) - i32.const 0 - ) - (func $44 (;57;) (type $4) (param $0 i32) (param $1 i32) (result i32) - local.get $1 - local.get $0 - i32.const 1 - i32.and - i32.const 0 - i32.add - call_indirect (type $1) - ) - (func $45 (;58;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - local.get $1 - local.get $2 - local.get $3 - local.get $0 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - ) - (func $46 (;59;) (type $5) (param $0 i32) (param $1 i32) - local.get $1 - local.get $0 - i32.const 1 - i32.and - i32.const 6 - i32.add - call_indirect (type $2) - ) - (func $47 (;60;) (type $1) (param $0 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - i32.const 0 - call $fimport$3 - i32.const 0 - end - ) - (func $48 (;61;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - i32.const 1 - call $fimport$3 - i32.const 0 - end - ) - (func $49 (;62;) (type $2) (param $0 i32) - i32.const 2 - call $fimport$3 - ) - (global $global$0 (;5;) (mut i32) global.get $gimport$0) - (global $global$1 (;6;) (mut i32) global.get $gimport$1) - (global $global$2 (;7;) (mut i32) global.get $gimport$2) - (global $global$3 (;8;) (mut i32) i32.const 0) - (global $global$4 (;9;) (mut i32) i32.const 0) - (global $global$5 (;10;) (mut i32) i32.const 0) - (export "_sbrk" (func $40)) - (export "_free" (func $38)) - (export "_main" (func $8)) - (export "_pthread_self" (func $43)) - (export "_memset" (func $41)) - (export "_malloc" (func $37)) - (export "_memcpy" (func $42)) - (export "___errno_location" (func $12)) - (export "runPostSets" (func $39)) - (export "stackAlloc" (func $0)) - (export "stackSave" (func $1)) - (export "stackRestore" (func $2)) - (export "establishStackSpace" (func $3)) - (export "setThrew" (func $4)) - (export "setTempRet0" (func $5)) - (export "getTempRet0" (func $6)) - (export "dynCall_ii" (func $44)) - (export "dynCall_iiii" (func $45)) - (export "dynCall_vi" (func $46)) - (elem (;0;) (global.get $gimport$19) func $47 $9 $48 $14 $10 $15 $49 $16) - (data (;0;) (i32.const 1024) "\04\04\00\00\05") - (data (;1;) (i32.const 1040) "\01") - (data (;2;) (i32.const 1064) "\01\00\00\00\02\00\00\00,\10\00\00\00\04") - (data (;3;) (i32.const 1088) "\01") - (data (;4;) (i32.const 1103) "\0a\ff\ff\ff\ff") - (data (;5;) (i32.const 1140) "error: %d\5cn\00ok\00\11\00\0a\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\09\00\00\00\00\0b") - (data (;6;) (i32.const 1187) "\11\00\0f\0a\11\11\11\03\0a\07\00\01\13\09\0b\0b\00\00\09\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") - (data (;7;) (i32.const 1236) "\0b") - (data (;8;) (i32.const 1245) "\11\00\0a\0a\11\11\11\00\0a\00\00\02\00\09\0b\00\00\00\09\00\0b\00\00\0b") - (data (;9;) (i32.const 1294) "\0c") - (data (;10;) (i32.const 1306) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c") - (data (;11;) (i32.const 1352) "\0e") - (data (;12;) (i32.const 1364) "\0d\00\00\00\04\0d\00\00\00\00\09\0e\00\00\00\00\00\0e\00\00\0e") - (data (;13;) (i32.const 1410) "\10") - (data (;14;) (i32.const 1422) "\0f\00\00\00\00\0f\00\00\00\00\09\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") - (data (;15;) (i32.const 1477) "\12\00\00\00\12\12\12\00\00\00\00\00\00\09") - (data (;16;) (i32.const 1526) "\0b") - (data (;17;) (i32.const 1538) "\0a\00\00\00\00\0a\00\00\00\00\09\0b\00\00\00\00\00\0b\00\00\0b") - (data (;18;) (i32.const 1584) "\0c") - (data (;19;) (i32.const 1596) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\22\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\09\0a\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\5c]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") -) \ No newline at end of file diff --git a/cranelift/wasm/wasmtests/embenchen_primes.wat b/cranelift/wasm/wasmtests/embenchen_primes.wat deleted file mode 100644 index 1bdf443ae581..000000000000 --- a/cranelift/wasm/wasmtests/embenchen_primes.wat +++ /dev/null @@ -1,11185 +0,0 @@ -(module - (type $0 (;0;) (func (param i32 i32 i32) (result i32))) - (type $1 (;1;) (func (param i32) (result i32))) - (type $2 (;2;) (func (param i32))) - (type $3 (;3;) (func (result i32))) - (type $4 (;4;) (func (param i32 i32) (result i32))) - (type $5 (;5;) (func (param i32 i32))) - (type $6 (;6;) (func)) - (type $7 (;7;) (func (param i32 i32 i32 i32 i32) (result i32))) - (type $8 (;8;) (func (param i32 i32 i32))) - (type $9 (;9;) (func (param i64 i32) (result i32))) - (type $10 (;10;) (func (param i32 i32 i32 i32 i32))) - (type $11 (;11;) (func (param f64 i32) (result f64))) - (type $12 (;12;) (func (param i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory $16 (;0;) 2048 2048)) - (import "env" "table" (table $timport$17 (;0;) 8 8 funcref)) - (import "env" "DYNAMICTOP_PTR" (global $gimport$0 (;0;) i32)) - (import "env" "STACKTOP" (global $gimport$1 (;1;) i32)) - (import "env" "STACK_MAX" (global $gimport$2 (;2;) i32)) - (import "env" "memoryBase" (global $gimport$18 (;3;) i32)) - (import "env" "tableBase" (global $gimport$19 (;4;) i32)) - (import "env" "abort" (func $fimport$3 (;0;) (type $2))) - (import "env" "enlargeMemory" (func $fimport$4 (;1;) (type $3))) - (import "env" "getTotalMemory" (func $fimport$5 (;2;) (type $3))) - (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (;3;) (type $3))) - (import "env" "_pthread_cleanup_pop" (func $fimport$7 (;4;) (type $2))) - (import "env" "___syscall6" (func $fimport$8 (;5;) (type $4))) - (import "env" "_pthread_cleanup_push" (func $fimport$9 (;6;) (type $5))) - (import "env" "_abort" (func $fimport$10 (;7;) (type $6))) - (import "env" "___setErrNo" (func $fimport$11 (;8;) (type $2))) - (import "env" "_emscripten_memcpy_big" (func $fimport$12 (;9;) (type $0))) - (import "env" "___syscall54" (func $fimport$13 (;10;) (type $4))) - (import "env" "___syscall140" (func $fimport$14 (;11;) (type $4))) - (import "env" "___syscall146" (func $fimport$15 (;12;) (type $4))) - (func $0 (;13;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $1 - global.get $global$1 - local.get $0 - i32.add - global.set $global$1 - global.get $global$1 - i32.const 15 - i32.add - i32.const -16 - i32.and - global.set $global$1 - local.get $1 - end - ) - (func $1 (;14;) (type $3) (result i32) - global.get $global$1 - ) - (func $2 (;15;) (type $2) (param $0 i32) - local.get $0 - global.set $global$1 - ) - (func $3 (;16;) (type $5) (param $0 i32) (param $1 i32) - block $label$1 ;; label = @1 - local.get $0 - global.set $global$1 - local.get $1 - global.set $global$2 - end - ) - (func $4 (;17;) (type $5) (param $0 i32) (param $1 i32) - global.get $global$3 - i32.eqz - if ;; label = @1 - block ;; label = @2 - local.get $0 - global.set $global$3 - local.get $1 - global.set $global$4 - end - end - ) - (func $5 (;18;) (type $2) (param $0 i32) - local.get $0 - global.set $global$5 - ) - (func $6 (;19;) (type $3) (result i32) - global.get $global$5 - ) - (func $7 (;20;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 f32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $3 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $3 - i32.const 8 - i32.add - local.set $5 - local.get $3 - local.set $2 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $0 - i32.const 1 - i32.le_s - br_if 0 (;@3;) - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - block $label$6 ;; label = @6 - block $label$7 ;; label = @7 - block $label$8 ;; label = @8 - block $label$9 ;; label = @9 - block $label$10 ;; label = @10 - local.get $1 - i32.load offset=4 - i32.load8_s - local.tee $0 - i32.const 48 - i32.sub - br_table 5 (;@5;) 0 (;@10;) 2 (;@8;) 1 (;@9;) 3 (;@7;) 4 (;@6;) 6 (;@4;) - end - i32.const 33000 - local.set $4 - br 7 (;@2;) - end - br 5 (;@3;) - end - i32.const 130000 - local.set $4 - br 5 (;@2;) - end - i32.const 610000 - local.set $4 - br 4 (;@2;) - end - i32.const 1010000 - local.set $4 - br 3 (;@2;) - end - local.get $3 - global.set $global$1 - i32.const 0 - return - end - local.get $2 - local.get $0 - i32.const -48 - i32.add - i32.store - i32.const 1140 - local.get $2 - call $30 - drop - local.get $3 - global.set $global$1 - i32.const -1 - return - end - i32.const 220000 - local.set $4 - end - i32.const 2 - local.set $1 - i32.const 0 - local.set $0 - loop $label$11 ;; label = @2 - block $label$12 ;; label = @3 - block $label$13 ;; label = @4 - local.get $1 - f32.convert_i32_s - f32.sqrt - local.tee $6 - f32.const 0x1p+1 (;=2;) - f32.gt - i32.eqz - br_if 0 (;@4;) - i32.const 2 - local.set $2 - loop $label$14 ;; label = @5 - local.get $1 - local.get $2 - i32.rem_s - i32.eqz - br_if 2 (;@3;) - local.get $2 - i32.const 1 - i32.add - local.tee $2 - f32.convert_i32_s - local.get $6 - f32.lt - br_if 0 (;@5;) - br 1 (;@4;) - end - end - local.get $0 - i32.const 1 - i32.add - local.set $0 - end - local.get $1 - i32.const 1 - i32.add - local.set $2 - local.get $0 - local.get $4 - i32.lt_s - if ;; label = @3 - block ;; label = @4 - local.get $2 - local.set $1 - br 2 (;@2;) - end - end - end - local.get $5 - local.get $1 - i32.store - i32.const 1152 - local.get $5 - call $30 - drop - local.get $3 - global.set $global$1 - i32.const 0 - end - ) - (func $8 (;21;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $1 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $1 - local.tee $2 - local.get $0 - i32.load offset=60 - i32.store - i32.const 6 - local.get $2 - call $fimport$8 - call $10 - local.set $0 - local.get $1 - global.set $global$1 - local.get $0 - end - ) - (func $9 (;22;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 32 - i32.add - global.set $global$1 - local.get $4 - local.tee $3 - local.get $0 - i32.load offset=60 - i32.store - local.get $3 - i32.const 0 - i32.store offset=4 - local.get $3 - local.get $1 - i32.store offset=8 - local.get $3 - local.get $4 - i32.const 20 - i32.add - local.tee $0 - i32.store offset=12 - local.get $3 - local.get $2 - i32.store offset=16 - i32.const 140 - local.get $3 - call $fimport$14 - call $10 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $0 - i32.const -1 - i32.store - i32.const -1 - end - else - local.get $0 - i32.load - end - local.set $0 - local.get $4 - global.set $global$1 - local.get $0 - end - ) - (func $10 (;23;) (type $1) (param $0 i32) (result i32) - local.get $0 - i32.const -4096 - i32.gt_u - if (result i32) ;; label = @1 - block (result i32) ;; label = @2 - call $11 - i32.const 0 - local.get $0 - i32.sub - i32.store - i32.const -1 - end - else - local.get $0 - end - ) - (func $11 (;24;) (type $3) (result i32) - i32.const 3640 - ) - (func $12 (;25;) (type $2) (param $0 i32) - nop - ) - (func $13 (;26;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 80 - i32.add - global.set $global$1 - local.get $4 - local.set $3 - local.get $4 - i32.const 12 - i32.add - local.set $5 - local.get $0 - i32.const 3 - i32.store offset=36 - local.get $0 - i32.load - i32.const 64 - i32.and - i32.eqz - if ;; label = @2 - block ;; label = @3 - local.get $3 - local.get $0 - i32.load offset=60 - i32.store - local.get $3 - i32.const 21505 - i32.store offset=4 - local.get $3 - local.get $5 - i32.store offset=8 - i32.const 54 - local.get $3 - call $fimport$13 - if ;; label = @4 - local.get $0 - i32.const -1 - i32.store8 offset=75 - end - end - end - local.get $0 - local.get $1 - local.get $2 - call $14 - local.set $0 - local.get $4 - global.set $global$1 - local.get $0 - end - ) - (func $14 (;27;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $8 - global.get $global$1 - i32.const 48 - i32.add - global.set $global$1 - local.get $8 - i32.const 16 - i32.add - local.set $9 - local.get $8 - local.set $10 - local.get $8 - i32.const 32 - i32.add - local.tee $3 - local.get $0 - i32.const 28 - i32.add - local.tee $6 - i32.load - local.tee $4 - i32.store - local.get $3 - local.get $0 - i32.const 20 - i32.add - local.tee $11 - i32.load - local.get $4 - i32.sub - local.tee $5 - i32.store offset=4 - local.get $3 - local.get $1 - i32.store offset=8 - local.get $3 - local.get $2 - i32.store offset=12 - local.get $0 - i32.const 60 - i32.add - local.set $13 - local.get $0 - i32.const 44 - i32.add - local.set $14 - local.get $3 - local.set $1 - i32.const 2 - local.set $4 - local.get $5 - local.get $2 - i32.add - local.set $12 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - loop $label$5 ;; label = @5 - i32.const 3596 - i32.load - if ;; label = @6 - block ;; label = @7 - i32.const 1 - local.get $0 - call $fimport$9 - local.get $10 - local.get $13 - i32.load - i32.store - local.get $10 - local.get $1 - i32.store offset=4 - local.get $10 - local.get $4 - i32.store offset=8 - i32.const 146 - local.get $10 - call $fimport$15 - call $10 - local.set $3 - i32.const 0 - call $fimport$7 - end - else - block ;; label = @7 - local.get $9 - local.get $13 - i32.load - i32.store - local.get $9 - local.get $1 - i32.store offset=4 - local.get $9 - local.get $4 - i32.store offset=8 - i32.const 146 - local.get $9 - call $fimport$15 - call $10 - local.set $3 - end - end - local.get $12 - local.get $3 - i32.eq - br_if 1 (;@4;) - local.get $3 - i32.const 0 - i32.lt_s - br_if 2 (;@3;) - local.get $3 - local.get $1 - i32.load offset=4 - local.tee $5 - i32.gt_u - if (result i32) ;; label = @6 - block (result i32) ;; label = @7 - local.get $6 - local.get $14 - i32.load - local.tee $7 - i32.store - local.get $11 - local.get $7 - i32.store - local.get $1 - i32.load offset=12 - local.set $7 - local.get $1 - i32.const 8 - i32.add - local.set $1 - local.get $4 - i32.const -1 - i32.add - local.set $4 - local.get $3 - local.get $5 - i32.sub - end - else - local.get $4 - i32.const 2 - i32.eq - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - local.get $6 - local.get $6 - i32.load - local.get $3 - i32.add - i32.store - local.get $5 - local.set $7 - i32.const 2 - local.set $4 - local.get $3 - end - else - block (result i32) ;; label = @8 - local.get $5 - local.set $7 - local.get $3 - end - end - end - local.set $5 - local.get $1 - local.get $1 - i32.load - local.get $5 - i32.add - i32.store - local.get $1 - local.get $7 - local.get $5 - i32.sub - i32.store offset=4 - local.get $12 - local.get $3 - i32.sub - local.set $12 - br 0 (;@5;) - end - end - local.get $0 - local.get $14 - i32.load - local.tee $1 - local.get $0 - i32.load offset=48 - i32.add - i32.store offset=16 - local.get $6 - local.get $1 - i32.store - local.get $11 - local.get $1 - i32.store - br 1 (;@2;) - end - local.get $0 - i32.const 0 - i32.store offset=16 - local.get $6 - i32.const 0 - i32.store - local.get $11 - i32.const 0 - i32.store - local.get $0 - local.get $0 - i32.load - i32.const 32 - i32.or - i32.store - local.get $4 - i32.const 2 - i32.eq - if (result i32) ;; label = @3 - i32.const 0 - else - local.get $2 - local.get $1 - i32.load offset=4 - i32.sub - end - local.set $2 - end - local.get $8 - global.set $global$1 - local.get $2 - end - ) - (func $15 (;28;) (type $2) (param $0 i32) - local.get $0 - i32.load offset=68 - i32.eqz - if ;; label = @1 - local.get $0 - call $12 - end - ) - (func $16 (;29;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $1 - i32.const 255 - i32.and - local.set $5 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - local.get $2 - i32.const 0 - i32.ne - local.tee $4 - local.get $0 - i32.const 3 - i32.and - i32.const 0 - i32.ne - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $1 - i32.const 255 - i32.and - local.set $4 - local.get $2 - local.set $3 - local.get $0 - local.set $2 - loop $label$6 ;; label = @7 - local.get $2 - i32.load8_s - local.get $4 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $3 - local.set $0 - br 6 (;@3;) - end - end - local.get $3 - i32.const -1 - i32.add - local.tee $3 - i32.const 0 - i32.ne - local.tee $0 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 3 - i32.and - i32.const 0 - i32.ne - i32.and - br_if 0 (;@7;) - br 3 (;@4;) - end - end - else - block ;; label = @6 - local.get $2 - local.set $3 - local.get $0 - local.set $2 - local.get $4 - local.set $0 - end - end - end - local.get $0 - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $0 - br 2 (;@3;) - end - else - i32.const 0 - local.set $0 - end - br 1 (;@2;) - end - local.get $2 - i32.load8_s - local.get $1 - i32.const 255 - i32.and - local.tee $1 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.ne - if ;; label = @3 - block ;; label = @4 - local.get $5 - i32.const 16843009 - i32.mul - local.set $3 - block $label$12 ;; label = @5 - block $label$13 ;; label = @6 - local.get $0 - i32.const 3 - i32.le_u - br_if 0 (;@6;) - loop $label$14 ;; label = @7 - local.get $2 - i32.load - local.get $3 - i32.xor - local.tee $4 - i32.const -2139062144 - i32.and - i32.const -2139062144 - i32.xor - local.get $4 - i32.const -16843009 - i32.add - i32.and - i32.eqz - if ;; label = @8 - block ;; label = @9 - local.get $2 - i32.const 4 - i32.add - local.set $2 - local.get $0 - i32.const -4 - i32.add - local.tee $0 - i32.const 3 - i32.gt_u - br_if 2 (;@7;) - br 3 (;@6;) - end - end - end - br 1 (;@5;) - end - local.get $0 - i32.eqz - if ;; label = @6 - block ;; label = @7 - i32.const 0 - local.set $0 - br 5 (;@2;) - end - end - end - loop $label$17 ;; label = @5 - local.get $2 - i32.load8_s - local.get $1 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eq - br_if 3 (;@2;) - local.get $2 - i32.const 1 - i32.add - local.set $2 - local.get $0 - i32.const -1 - i32.add - local.tee $0 - br_if 0 (;@5;) - i32.const 0 - local.set $0 - end - end - end - end - local.get $0 - if (result i32) ;; label = @2 - local.get $2 - else - i32.const 0 - end - end - ) - (func $17 (;30;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $4 - global.get $global$1 - i32.const 224 - i32.add - global.set $global$1 - local.get $4 - i32.const 136 - i32.add - local.set $5 - local.get $4 - i32.const 80 - i32.add - local.tee $3 - i64.const 0 - i64.store align=4 - local.get $3 - i64.const 0 - i64.store offset=8 align=4 - local.get $3 - i64.const 0 - i64.store offset=16 align=4 - local.get $3 - i64.const 0 - i64.store offset=24 align=4 - local.get $3 - i64.const 0 - i64.store offset=32 align=4 - local.get $4 - i32.const 120 - i32.add - local.tee $6 - local.get $2 - i32.load - i32.store - i32.const 0 - local.get $1 - local.get $6 - local.get $4 - local.tee $2 - local.get $3 - call $18 - i32.const 0 - i32.lt_s - if ;; label = @2 - i32.const -1 - local.set $1 - else - block ;; label = @3 - local.get $0 - i32.load offset=76 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @4 - local.get $0 - call $19 - else - i32.const 0 - end - local.set $12 - local.get $0 - i32.load - local.set $7 - local.get $0 - i32.load8_s offset=74 - i32.const 1 - i32.lt_s - if ;; label = @4 - local.get $0 - local.get $7 - i32.const -33 - i32.and - i32.store - end - local.get $0 - i32.const 48 - i32.add - local.tee $8 - i32.load - if ;; label = @4 - local.get $0 - local.get $1 - local.get $6 - local.get $2 - local.get $3 - call $18 - local.set $1 - else - block ;; label = @5 - local.get $0 - i32.const 44 - i32.add - local.tee $9 - i32.load - local.set $10 - local.get $9 - local.get $5 - i32.store - local.get $0 - i32.const 28 - i32.add - local.tee $13 - local.get $5 - i32.store - local.get $0 - i32.const 20 - i32.add - local.tee $11 - local.get $5 - i32.store - local.get $8 - i32.const 80 - i32.store - local.get $0 - i32.const 16 - i32.add - local.tee $14 - local.get $5 - i32.const 80 - i32.add - i32.store - local.get $0 - local.get $1 - local.get $6 - local.get $2 - local.get $3 - call $18 - local.set $1 - local.get $10 - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 0 - i32.const 0 - local.get $0 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - drop - local.get $11 - i32.load - i32.eqz - if ;; label = @8 - i32.const -1 - local.set $1 - end - local.get $9 - local.get $10 - i32.store - local.get $8 - i32.const 0 - i32.store - local.get $14 - i32.const 0 - i32.store - local.get $13 - i32.const 0 - i32.store - local.get $11 - i32.const 0 - i32.store - end - end - end - end - local.get $0 - local.get $0 - i32.load - local.tee $2 - local.get $7 - i32.const 32 - i32.and - i32.or - i32.store - local.get $12 - if ;; label = @4 - local.get $0 - call $12 - end - local.get $2 - i32.const 32 - i32.and - if ;; label = @4 - i32.const -1 - local.set $1 - end - end - end - local.get $4 - global.set $global$1 - local.get $1 - end - ) - (func $18 (;31;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) - (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) (local $47 i32) (local $48 i32) (local $49 i32) (local $50 i64) (local $51 i64) (local $52 f64) (local $53 f64) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $23 - global.get $global$1 - i32.const 624 - i32.add - global.set $global$1 - local.get $23 - i32.const 16 - i32.add - local.set $20 - local.get $23 - local.set $16 - local.get $23 - i32.const 528 - i32.add - local.set $36 - local.get $0 - i32.const 0 - i32.ne - local.set $30 - local.get $23 - i32.const 536 - i32.add - local.tee $17 - i32.const 40 - i32.add - local.tee $21 - local.set $38 - local.get $17 - i32.const 39 - i32.add - local.set $39 - local.get $23 - i32.const 8 - i32.add - local.tee $37 - i32.const 4 - i32.add - local.set $42 - i32.const 0 - local.get $23 - i32.const 588 - i32.add - local.tee $19 - local.tee $27 - i32.sub - local.set $43 - local.get $23 - i32.const 576 - i32.add - local.tee $17 - i32.const 12 - i32.add - local.set $33 - local.get $17 - i32.const 11 - i32.add - local.set $40 - local.get $33 - local.tee $28 - local.get $27 - i32.sub - local.set $44 - i32.const -2 - local.get $27 - i32.sub - local.set $45 - local.get $28 - i32.const 2 - i32.add - local.set $46 - local.get $23 - i32.const 24 - i32.add - local.tee $47 - i32.const 288 - i32.add - local.set $48 - local.get $19 - i32.const 9 - i32.add - local.tee $31 - local.set $41 - local.get $19 - i32.const 8 - i32.add - local.set $34 - i32.const 0 - local.set $15 - i32.const 0 - local.set $10 - i32.const 0 - local.set $17 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - loop $label$4 ;; label = @4 - block $label$5 ;; label = @5 - local.get $15 - i32.const -1 - i32.gt_s - if ;; label = @6 - local.get $10 - i32.const 2147483647 - local.get $15 - i32.sub - i32.gt_s - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - call $11 - i32.const 75 - i32.store - i32.const -1 - end - else - local.get $10 - local.get $15 - i32.add - end - local.set $15 - end - local.get $1 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eqz - br_if 2 (;@3;) - local.get $1 - local.set $11 - block $label$9 ;; label = @6 - block $label$10 ;; label = @7 - loop $label$11 ;; label = @8 - block $label$12 ;; label = @9 - block $label$13 ;; label = @10 - block $label$14 ;; label = @11 - block $label$15 ;; label = @12 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 0 - i32.sub - br_table 1 (;@11;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 0 (;@12;) 2 (;@10;) - end - local.get $11 - local.set $5 - br 4 (;@7;) - end - local.get $11 - local.set $5 - br 1 (;@9;) - end - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.set $5 - br 1 (;@8;) - end - end - br 1 (;@6;) - end - loop $label$16 ;; label = @7 - local.get $5 - i32.load8_s offset=1 - i32.const 37 - i32.ne - br_if 1 (;@6;) - local.get $11 - i32.const 1 - i32.add - local.set $11 - local.get $5 - i32.const 2 - i32.add - local.tee $5 - i32.load8_s - i32.const 37 - i32.eq - br_if 0 (;@7;) - end - end - local.get $11 - local.get $1 - i32.sub - local.set $10 - local.get $30 - if ;; label = @6 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @7 - local.get $1 - local.get $10 - local.get $0 - call $20 - drop - end - end - local.get $10 - if ;; label = @6 - block ;; label = @7 - local.get $5 - local.set $1 - br 3 (;@4;) - end - end - local.get $5 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $10 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $9 - i32.const 10 - i32.lt_u - if (result i32) ;; label = @6 - block (result i32) ;; label = @7 - local.get $5 - i32.const 3 - i32.add - local.set $10 - local.get $5 - i32.load8_s offset=2 - i32.const 36 - i32.eq - local.tee $12 - if ;; label = @8 - local.get $10 - local.set $11 - end - local.get $12 - if ;; label = @8 - i32.const 1 - local.set $17 - end - local.get $11 - i32.load8_s - local.set $5 - local.get $12 - i32.eqz - if ;; label = @8 - i32.const -1 - local.set $9 - end - local.get $17 - end - else - block (result i32) ;; label = @7 - local.get $10 - local.set $5 - i32.const -1 - local.set $9 - local.get $17 - end - end - local.set $10 - block $label$25 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - local.tee $12 - i32.const 32 - i32.lt_u - if ;; label = @7 - block ;; label = @8 - i32.const 0 - local.set $17 - loop $label$27 ;; label = @9 - i32.const 1 - local.get $12 - i32.shl - i32.const 75913 - i32.and - i32.eqz - br_if 3 (;@6;) - i32.const 1 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - i32.shl - local.get $17 - i32.or - local.set $17 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -32 - i32.add - local.tee $12 - i32.const 32 - i32.lt_u - br_if 0 (;@9;) - end - end - else - i32.const 0 - local.set $17 - end - end - block $label$29 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 42 - i32.eq - if ;; label = @7 - block ;; label = @8 - block $label$31 (result i32) ;; label = @9 - block $label$32 ;; label = @10 - local.get $11 - i32.const 1 - i32.add - local.tee $7 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $12 - i32.const 10 - i32.ge_u - br_if 0 (;@10;) - local.get $11 - i32.load8_s offset=2 - i32.const 36 - i32.ne - br_if 0 (;@10;) - local.get $4 - local.get $12 - i32.const 2 - i32.shl - i32.add - i32.const 10 - i32.store - i32.const 1 - local.set $8 - local.get $3 - local.get $7 - i32.load8_s - i32.const -48 - i32.add - i32.const 3 - i32.shl - i32.add - i64.load - i32.wrap_i64 - local.set $10 - local.get $11 - i32.const 3 - i32.add - br 1 (;@9;) - end - local.get $10 - if ;; label = @10 - block ;; label = @11 - i32.const -1 - local.set $15 - br 6 (;@5;) - end - end - local.get $30 - i32.eqz - if ;; label = @10 - block ;; label = @11 - local.get $17 - local.set $12 - i32.const 0 - local.set $17 - local.get $7 - local.set $11 - i32.const 0 - local.set $10 - br 5 (;@6;) - end - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $11 - i32.load - local.set $10 - local.get $2 - local.get $11 - i32.const 4 - i32.add - i32.store - i32.const 0 - local.set $8 - local.get $7 - end - local.set $11 - local.get $17 - i32.const 8192 - i32.or - local.set $12 - i32.const 0 - local.get $10 - i32.sub - local.set $7 - local.get $11 - i32.load8_s - local.set $5 - local.get $10 - i32.const 0 - i32.lt_s - local.tee $6 - i32.eqz - if ;; label = @9 - local.get $17 - local.set $12 - end - local.get $8 - local.set $17 - local.get $6 - if ;; label = @9 - local.get $7 - local.set $10 - end - end - else - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $12 - i32.const 10 - i32.lt_u - if ;; label = @8 - block ;; label = @9 - i32.const 0 - local.set $7 - local.get $12 - local.set $5 - loop $label$39 ;; label = @10 - local.get $7 - i32.const 10 - i32.mul - local.get $5 - i32.add - local.set $7 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - local.tee $12 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - br_if 0 (;@10;) - end - local.get $7 - i32.const 0 - i32.lt_s - if ;; label = @10 - block ;; label = @11 - i32.const -1 - local.set $15 - br 6 (;@5;) - end - else - block ;; label = @11 - local.get $12 - local.set $5 - local.get $17 - local.set $12 - local.get $10 - local.set $17 - local.get $7 - local.set $10 - end - end - end - else - block ;; label = @9 - local.get $17 - local.set $12 - local.get $10 - local.set $17 - i32.const 0 - local.set $10 - end - end - end - end - block $label$43 ;; label = @6 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 46 - i32.eq - if ;; label = @7 - block ;; label = @8 - local.get $11 - i32.const 1 - i32.add - local.tee $7 - i32.load8_s - local.tee $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 42 - i32.ne - if ;; label = @9 - block ;; label = @10 - local.get $5 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - if ;; label = @11 - block ;; label = @12 - local.get $7 - local.set $11 - i32.const 0 - local.set $7 - end - else - block ;; label = @12 - i32.const 0 - local.set $5 - local.get $7 - local.set $11 - br 6 (;@6;) - end - end - loop $label$48 ;; label = @11 - local.get $7 - i32.const 10 - i32.mul - local.get $5 - i32.add - local.set $5 - local.get $11 - i32.const 1 - i32.add - local.tee $11 - i32.load8_s - i32.const -48 - i32.add - local.tee $8 - i32.const 10 - i32.ge_u - br_if 5 (;@6;) - local.get $5 - local.set $7 - local.get $8 - local.set $5 - br 0 (;@11;) - end - end - end - local.get $11 - i32.const 2 - i32.add - local.tee $7 - i32.load8_s - i32.const -48 - i32.add - local.tee $5 - i32.const 10 - i32.lt_u - if ;; label = @9 - local.get $11 - i32.load8_s offset=3 - i32.const 36 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.get $5 - i32.const 2 - i32.shl - i32.add - i32.const 10 - i32.store - local.get $3 - local.get $7 - i32.load8_s - i32.const -48 - i32.add - i32.const 3 - i32.shl - i32.add - i64.load - i32.wrap_i64 - local.set $5 - local.get $11 - i32.const 4 - i32.add - local.set $11 - br 5 (;@6;) - end - end - end - local.get $17 - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - end - local.get $30 - if (result i32) ;; label = @9 - block (result i32) ;; label = @10 - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $11 - i32.load - local.set $5 - local.get $2 - local.get $11 - i32.const 4 - i32.add - i32.store - local.get $7 - end - else - block (result i32) ;; label = @10 - i32.const 0 - local.set $5 - local.get $7 - end - end - local.set $11 - end - else - i32.const -1 - local.set $5 - end - end - local.get $11 - local.set $7 - i32.const 0 - local.set $8 - loop $label$55 ;; label = @6 - local.get $7 - i32.load8_s - i32.const -65 - i32.add - local.tee $6 - i32.const 57 - i32.gt_u - if ;; label = @7 - block ;; label = @8 - i32.const -1 - local.set $15 - br 3 (;@5;) - end - end - local.get $7 - i32.const 1 - i32.add - local.set $11 - local.get $8 - i32.const 58 - i32.mul - i32.const 1168 - i32.add - local.get $6 - i32.add - i32.load8_s - local.tee $13 - i32.const 255 - i32.and - local.tee $6 - i32.const -1 - i32.add - i32.const 8 - i32.lt_u - if ;; label = @7 - block ;; label = @8 - local.get $11 - local.set $7 - local.get $6 - local.set $8 - br 2 (;@6;) - end - end - end - local.get $13 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.eqz - if ;; label = @6 - block ;; label = @7 - i32.const -1 - local.set $15 - br 2 (;@5;) - end - end - local.get $9 - i32.const -1 - i32.gt_s - local.set $14 - block $label$59 ;; label = @6 - block $label$60 ;; label = @7 - local.get $13 - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 19 - i32.eq - if ;; label = @8 - local.get $14 - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - else - br 2 (;@7;) - end - else - block ;; label = @9 - local.get $14 - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.get $9 - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store - local.get $16 - local.get $3 - local.get $9 - i32.const 3 - i32.shl - i32.add - i64.load - i64.store - br 4 (;@7;) - end - end - local.get $30 - i32.eqz - if ;; label = @10 - block ;; label = @11 - i32.const 0 - local.set $15 - br 6 (;@5;) - end - end - local.get $16 - local.get $6 - local.get $2 - call $21 - end - end - br 1 (;@6;) - end - local.get $30 - i32.eqz - if ;; label = @7 - block ;; label = @8 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 4 (;@4;) - end - end - end - local.get $7 - i32.load8_s - local.tee $7 - i32.const -33 - i32.and - local.set $9 - local.get $8 - i32.const 0 - i32.ne - local.get $7 - i32.const 15 - i32.and - i32.const 3 - i32.eq - i32.and - i32.eqz - if ;; label = @6 - local.get $7 - local.set $9 - end - local.get $12 - i32.const -65537 - i32.and - local.set $7 - local.get $12 - i32.const 8192 - i32.and - if ;; label = @6 - local.get $7 - local.set $12 - end - block $label$70 ;; label = @6 - block $label$71 ;; label = @7 - block $label$72 ;; label = @8 - block $label$73 ;; label = @9 - block $label$74 ;; label = @10 - block $label$75 ;; label = @11 - block $label$76 ;; label = @12 - block $label$77 ;; label = @13 - block $label$78 ;; label = @14 - block $label$79 ;; label = @15 - block $label$80 ;; label = @16 - block $label$81 ;; label = @17 - block $label$82 ;; label = @18 - block $label$83 ;; label = @19 - block $label$84 ;; label = @20 - block $label$85 ;; label = @21 - block $label$86 ;; label = @22 - block $label$87 ;; label = @23 - block $label$88 ;; label = @24 - block $label$89 ;; label = @25 - local.get $9 - i32.const 65 - i32.sub - br_table 11 (;@14;) 12 (;@13;) 9 (;@16;) 12 (;@13;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 10 (;@15;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 11 (;@14;) 12 (;@13;) 6 (;@19;) 4 (;@21;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 4 (;@21;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 7 (;@18;) 0 (;@25;) 3 (;@22;) 1 (;@24;) 12 (;@13;) 12 (;@13;) 8 (;@17;) 12 (;@13;) 5 (;@20;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) - end - block $label$90 ;; label = @25 - block $label$91 ;; label = @26 - block $label$92 ;; label = @27 - block $label$93 ;; label = @28 - block $label$94 ;; label = @29 - block $label$95 ;; label = @30 - block $label$96 ;; label = @31 - block $label$97 ;; label = @32 - local.get $8 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i32.const 0 - i32.sub - br_table 0 (;@32;) 1 (;@31;) 2 (;@30;) 3 (;@29;) 4 (;@28;) 7 (;@25;) 5 (;@27;) 6 (;@26;) 7 (;@25;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 27 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 26 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i64.extend_i32_s - i64.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 25 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store16 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 24 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store8 - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 23 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i32.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 22 (;@4;) - end - local.get $16 - i32.load - local.get $15 - i64.extend_i32_s - i64.store - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 21 (;@4;) - end - i32.const 0 - local.set $10 - local.get $11 - local.set $1 - br 20 (;@4;) - end - local.get $12 - i32.const 8 - i32.or - local.set $12 - local.get $5 - i32.const 8 - i32.le_u - if ;; label = @24 - i32.const 8 - local.set $5 - end - i32.const 120 - local.set $9 - br 11 (;@12;) - end - br 10 (;@12;) - end - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.eq - if ;; label = @22 - local.get $21 - local.set $7 - else - block ;; label = @23 - local.get $21 - local.set $1 - loop $label$101 ;; label = @24 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $50 - i64.const 7 - i64.and - i64.const 48 - i64.or - i64.store8 - local.get $50 - i64.const 3 - i64.shr_u - local.tee $50 - i64.const 0 - i64.ne - br_if 0 (;@24;) - local.get $1 - local.set $7 - end - end - end - local.get $12 - i32.const 8 - i32.and - if ;; label = @22 - block ;; label = @23 - local.get $38 - local.get $7 - i32.sub - local.tee $1 - i32.const 1 - i32.add - local.set $8 - local.get $5 - local.get $1 - i32.le_s - if ;; label = @24 - local.get $8 - local.set $5 - end - i32.const 0 - local.set $6 - i32.const 1648 - local.set $8 - br 16 (;@7;) - end - else - block ;; label = @23 - i32.const 0 - local.set $6 - i32.const 1648 - local.set $8 - br 16 (;@7;) - end - end - end - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.lt_s - if ;; label = @21 - block ;; label = @22 - local.get $16 - i64.const 0 - local.get $50 - i64.sub - local.tee $50 - i64.store - i32.const 1 - local.set $6 - i32.const 1648 - local.set $8 - br 11 (;@11;) - end - end - local.get $12 - i32.const 2048 - i32.and - if ;; label = @21 - block ;; label = @22 - i32.const 1 - local.set $6 - i32.const 1649 - local.set $8 - br 11 (;@11;) - end - else - block ;; label = @22 - local.get $12 - i32.const 1 - i32.and - local.tee $1 - local.set $6 - local.get $1 - if (result i32) ;; label = @23 - i32.const 1650 - else - i32.const 1648 - end - local.set $8 - br 11 (;@11;) - end - end - end - local.get $16 - i64.load - local.set $50 - i32.const 0 - local.set $6 - i32.const 1648 - local.set $8 - br 8 (;@11;) - end - local.get $39 - local.get $16 - i64.load - i64.store8 - local.get $39 - local.set $1 - local.get $7 - local.set $12 - i32.const 1 - local.set $7 - i32.const 0 - local.set $6 - i32.const 1648 - local.set $8 - local.get $21 - local.set $5 - br 12 (;@6;) - end - call $11 - i32.load - call $23 - local.set $1 - br 7 (;@10;) - end - local.get $16 - i32.load - local.tee $1 - i32.eqz - if ;; label = @17 - i32.const 1658 - local.set $1 - end - br 6 (;@10;) - end - local.get $37 - local.get $16 - i64.load - i64.store32 - local.get $42 - i32.const 0 - i32.store - local.get $16 - local.get $37 - i32.store - local.get $37 - local.set $7 - i32.const -1 - local.set $6 - br 6 (;@9;) - end - local.get $16 - i32.load - local.set $7 - local.get $5 - if ;; label = @15 - block ;; label = @16 - local.get $5 - local.set $6 - br 7 (;@9;) - end - else - block ;; label = @16 - local.get $0 - i32.const 32 - local.get $10 - i32.const 0 - local.get $12 - call $24 - i32.const 0 - local.set $1 - br 8 (;@8;) - end - end - end - local.get $16 - f64.load - local.set $52 - local.get $20 - i32.const 0 - i32.store - local.get $52 - i64.reinterpret_f64 - i64.const 0 - i64.lt_s - if (result i32) ;; label = @14 - block (result i32) ;; label = @15 - i32.const 1 - local.set $24 - local.get $52 - f64.neg - local.set $52 - i32.const 1665 - end - else - block (result i32) ;; label = @15 - local.get $12 - i32.const 1 - i32.and - local.set $1 - local.get $12 - i32.const 2048 - i32.and - if (result i32) ;; label = @16 - block (result i32) ;; label = @17 - i32.const 1 - local.set $24 - i32.const 1668 - end - else - block (result i32) ;; label = @17 - local.get $1 - local.set $24 - local.get $1 - if (result i32) ;; label = @18 - i32.const 1671 - else - i32.const 1666 - end - end - end - end - end - local.set $26 - block $label$119 ;; label = @14 - local.get $52 - i64.reinterpret_f64 - i64.const 9218868437227405312 - i64.and - i64.const 9218868437227405312 - i64.lt_u - if ;; label = @15 - block ;; label = @16 - local.get $52 - local.get $20 - call $26 - f64.const 0x1p+1 (;=2;) - f64.mul - local.tee $52 - f64.const 0x0p+0 (;=0;) - f64.ne - local.tee $1 - if ;; label = @17 - local.get $20 - local.get $20 - i32.load - i32.const -1 - i32.add - i32.store - end - local.get $9 - i32.const 32 - i32.or - local.tee $22 - i32.const 97 - i32.eq - if ;; label = @17 - block ;; label = @18 - local.get $26 - i32.const 9 - i32.add - local.set $1 - local.get $9 - i32.const 32 - i32.and - local.tee $6 - if ;; label = @19 - local.get $1 - local.set $26 - end - local.get $5 - i32.const 11 - i32.gt_u - i32.const 12 - local.get $5 - i32.sub - local.tee $1 - i32.eqz - i32.or - i32.eqz - if ;; label = @19 - block ;; label = @20 - f64.const 0x1p+3 (;=8;) - local.set $53 - loop $label$125 ;; label = @21 - local.get $53 - f64.const 0x1p+4 (;=16;) - f64.mul - local.set $53 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - br_if 0 (;@21;) - end - local.get $26 - i32.load8_s - i32.const 45 - i32.eq - if (result f64) ;; label = @21 - local.get $53 - local.get $52 - f64.neg - local.get $53 - f64.sub - f64.add - f64.neg - else - local.get $52 - local.get $53 - f64.add - local.get $53 - f64.sub - end - local.set $52 - end - end - i32.const 0 - local.get $20 - i32.load - local.tee $7 - i32.sub - local.set $1 - local.get $7 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @19 - local.get $1 - else - local.get $7 - end - i64.extend_i32_s - local.get $33 - call $22 - local.tee $1 - local.get $33 - i32.eq - if ;; label = @19 - block ;; label = @20 - local.get $40 - i32.const 48 - i32.store8 - local.get $40 - local.set $1 - end - end - local.get $24 - i32.const 2 - i32.or - local.set $13 - local.get $1 - i32.const -1 - i32.add - local.get $7 - i32.const 31 - i32.shr_s - i32.const 2 - i32.and - i32.const 43 - i32.add - i32.store8 - local.get $1 - i32.const -2 - i32.add - local.tee $8 - local.get $9 - i32.const 15 - i32.add - i32.store8 - local.get $5 - i32.const 1 - i32.lt_s - local.set $9 - local.get $12 - i32.const 8 - i32.and - i32.eqz - local.set $14 - local.get $19 - local.set $1 - loop $label$131 ;; label = @19 - local.get $1 - local.get $52 - i32.trunc_f64_s - local.tee $7 - i32.const 1632 - i32.add - i32.load8_u - local.get $6 - i32.or - i32.store8 - local.get $52 - local.get $7 - f64.convert_i32_s - f64.sub - f64.const 0x1p+4 (;=16;) - f64.mul - local.set $52 - block $label$132 (result i32) ;; label = @20 - local.get $1 - i32.const 1 - i32.add - local.tee $7 - local.get $27 - i32.sub - i32.const 1 - i32.eq - if (result i32) ;; label = @21 - block (result i32) ;; label = @22 - local.get $7 - local.get $14 - local.get $9 - local.get $52 - f64.const 0x0p+0 (;=0;) - f64.eq - i32.and - i32.and - br_if 2 (;@20;) - drop - local.get $7 - i32.const 46 - i32.store8 - local.get $1 - i32.const 2 - i32.add - end - else - local.get $7 - end - end - local.set $1 - local.get $52 - f64.const 0x0p+0 (;=0;) - f64.ne - br_if 0 (;@19;) - end - local.get $46 - local.get $5 - i32.add - local.get $8 - local.tee $7 - i32.sub - local.set $6 - local.get $44 - local.get $7 - i32.sub - local.get $1 - i32.add - local.set $9 - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - i32.const 0 - i32.ne - local.get $45 - local.get $1 - i32.add - local.get $5 - i32.lt_s - i32.and - if (result i32) ;; label = @19 - local.get $6 - else - local.get $9 - local.tee $6 - end - local.get $13 - i32.add - local.tee $5 - local.get $12 - call $24 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $26 - local.get $13 - local.get $0 - call $20 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $5 - local.get $12 - i32.const 65536 - i32.xor - call $24 - local.get $1 - local.get $27 - i32.sub - local.set $1 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $19 - local.get $1 - local.get $0 - call $20 - drop - end - local.get $0 - i32.const 48 - local.get $6 - local.get $1 - local.get $28 - local.get $7 - i32.sub - local.tee $1 - i32.add - i32.sub - i32.const 0 - i32.const 0 - call $24 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @19 - local.get $8 - local.get $1 - local.get $0 - call $20 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - local.get $12 - i32.const 8192 - i32.xor - call $24 - local.get $5 - local.get $10 - i32.ge_s - if ;; label = @19 - local.get $5 - local.set $10 - end - br 4 (;@14;) - end - end - local.get $1 - if ;; label = @17 - block ;; label = @18 - local.get $20 - local.get $20 - i32.load - i32.const -28 - i32.add - local.tee $6 - i32.store - local.get $52 - f64.const 0x1p+28 (;=268435456;) - f64.mul - local.set $52 - end - else - local.get $20 - i32.load - local.set $6 - end - local.get $6 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @17 - local.get $47 - else - local.get $48 - end - local.tee $7 - local.set $8 - loop $label$145 ;; label = @17 - local.get $8 - local.get $52 - i32.trunc_f64_s - local.tee $1 - i32.store - local.get $8 - i32.const 4 - i32.add - local.set $8 - local.get $52 - local.get $1 - f64.convert_i32_u - f64.sub - f64.const 0x1.dcd65p+29 (;=1000000000;) - f64.mul - local.tee $52 - f64.const 0x0p+0 (;=0;) - f64.ne - br_if 0 (;@17;) - end - local.get $6 - i32.const 0 - i32.gt_s - if ;; label = @17 - block ;; label = @18 - local.get $7 - local.set $1 - loop $label$147 ;; label = @19 - local.get $6 - i32.const 29 - i32.gt_s - if (result i32) ;; label = @20 - i32.const 29 - else - local.get $6 - end - local.set $14 - block $label$150 ;; label = @20 - local.get $8 - i32.const -4 - i32.add - local.tee $6 - local.get $1 - i32.ge_u - if ;; label = @21 - block ;; label = @22 - local.get $14 - i64.extend_i32_u - local.set $50 - i32.const 0 - local.set $13 - loop $label$152 ;; label = @23 - local.get $6 - local.get $6 - i32.load - i64.extend_i32_u - local.get $50 - i64.shl - local.get $13 - i64.extend_i32_u - i64.add - local.tee $51 - i64.const 1000000000 - i64.rem_u - i64.store32 - local.get $51 - i64.const 1000000000 - i64.div_u - i32.wrap_i64 - local.set $13 - local.get $6 - i32.const -4 - i32.add - local.tee $6 - local.get $1 - i32.ge_u - br_if 0 (;@23;) - end - local.get $13 - i32.eqz - br_if 2 (;@20;) - local.get $1 - i32.const -4 - i32.add - local.tee $1 - local.get $13 - i32.store - end - end - end - loop $label$153 ;; label = @20 - local.get $8 - local.get $1 - i32.gt_u - if ;; label = @21 - local.get $8 - i32.const -4 - i32.add - local.tee $6 - i32.load - i32.eqz - if ;; label = @22 - block ;; label = @23 - local.get $6 - local.set $8 - br 3 (;@20;) - end - end - end - end - local.get $20 - local.get $20 - i32.load - local.get $14 - i32.sub - local.tee $6 - i32.store - local.get $6 - i32.const 0 - i32.gt_s - br_if 0 (;@19;) - end - end - else - local.get $7 - local.set $1 - end - local.get $5 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @17 - i32.const 6 - else - local.get $5 - end - local.set $18 - local.get $6 - i32.const 0 - i32.lt_s - if ;; label = @17 - block ;; label = @18 - local.get $18 - i32.const 25 - i32.add - i32.const 9 - i32.div_s - i32.const 1 - i32.add - local.set $14 - local.get $22 - i32.const 102 - i32.eq - local.set $25 - local.get $8 - local.set $5 - loop $label$160 ;; label = @19 - i32.const 0 - local.get $6 - i32.sub - local.tee $13 - i32.const 9 - i32.gt_s - if ;; label = @20 - i32.const 9 - local.set $13 - end - block $label$162 ;; label = @20 - local.get $1 - local.get $5 - i32.lt_u - if ;; label = @21 - block ;; label = @22 - i32.const 1 - local.get $13 - i32.shl - i32.const -1 - i32.add - local.set $29 - i32.const 1000000000 - local.get $13 - i32.shr_u - local.set $35 - i32.const 0 - local.set $6 - local.get $1 - local.set $8 - loop $label$164 ;; label = @23 - local.get $8 - local.get $8 - i32.load - local.tee $32 - local.get $13 - i32.shr_u - local.get $6 - i32.add - i32.store - local.get $32 - local.get $29 - i32.and - local.get $35 - i32.mul - local.set $6 - local.get $8 - i32.const 4 - i32.add - local.tee $8 - local.get $5 - i32.lt_u - br_if 0 (;@23;) - end - local.get $1 - i32.const 4 - i32.add - local.set $8 - local.get $1 - i32.load - i32.eqz - if ;; label = @23 - local.get $8 - local.set $1 - end - local.get $6 - i32.eqz - br_if 2 (;@20;) - local.get $5 - local.get $6 - i32.store - local.get $5 - i32.const 4 - i32.add - local.set $5 - end - else - block ;; label = @22 - local.get $1 - i32.const 4 - i32.add - local.set $8 - local.get $1 - i32.load - i32.eqz - if ;; label = @23 - local.get $8 - local.set $1 - end - end - end - end - local.get $25 - if (result i32) ;; label = @20 - local.get $7 - else - local.get $1 - end - local.tee $8 - local.get $14 - i32.const 2 - i32.shl - i32.add - local.set $6 - local.get $5 - local.get $8 - i32.sub - i32.const 2 - i32.shr_s - local.get $14 - i32.gt_s - if ;; label = @20 - local.get $6 - local.set $5 - end - local.get $20 - local.get $20 - i32.load - local.get $13 - i32.add - local.tee $6 - i32.store - local.get $6 - i32.const 0 - i32.lt_s - br_if 0 (;@19;) - local.get $5 - local.set $13 - end - end - else - local.get $8 - local.set $13 - end - local.get $7 - local.set $25 - block $label$172 ;; label = @17 - local.get $1 - local.get $13 - i32.lt_u - if ;; label = @18 - block ;; label = @19 - local.get $25 - local.get $1 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - local.set $5 - local.get $1 - i32.load - local.tee $6 - i32.const 10 - i32.lt_u - br_if 2 (;@17;) - i32.const 10 - local.set $8 - loop $label$174 ;; label = @20 - local.get $5 - i32.const 1 - i32.add - local.set $5 - local.get $6 - local.get $8 - i32.const 10 - i32.mul - local.tee $8 - i32.ge_u - br_if 0 (;@20;) - end - end - else - i32.const 0 - local.set $5 - end - end - local.get $22 - i32.const 103 - i32.eq - local.set $29 - local.get $18 - i32.const 0 - i32.ne - local.set $35 - local.get $18 - local.get $22 - i32.const 102 - i32.ne - if (result i32) ;; label = @17 - local.get $5 - else - i32.const 0 - end - i32.sub - local.get $35 - local.get $29 - i32.and - i32.const 31 - i32.shl - i32.const 31 - i32.shr_s - i32.add - local.tee $8 - local.get $13 - local.get $25 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - i32.const -9 - i32.add - i32.lt_s - if ;; label = @17 - block ;; label = @18 - local.get $8 - i32.const 9216 - i32.add - local.tee $14 - i32.const 9 - i32.rem_s - i32.const 1 - i32.add - local.tee $8 - i32.const 9 - i32.lt_s - if ;; label = @19 - block ;; label = @20 - i32.const 10 - local.set $6 - loop $label$180 ;; label = @21 - local.get $6 - i32.const 10 - i32.mul - local.set $6 - local.get $8 - i32.const 1 - i32.add - local.tee $8 - i32.const 9 - i32.ne - br_if 0 (;@21;) - end - end - else - i32.const 10 - local.set $6 - end - local.get $7 - i32.const 4 - i32.add - local.get $14 - i32.const 9 - i32.div_s - i32.const -1024 - i32.add - i32.const 2 - i32.shl - i32.add - local.tee $8 - i32.load - local.tee $22 - local.get $6 - i32.rem_u - local.set $14 - block $label$182 ;; label = @19 - local.get $8 - i32.const 4 - i32.add - local.get $13 - i32.eq - local.tee $32 - local.get $14 - i32.eqz - i32.and - i32.eqz - if ;; label = @20 - block ;; label = @21 - local.get $14 - local.get $6 - i32.const 2 - i32.div_s - local.tee $49 - i32.lt_u - if (result f64) ;; label = @22 - f64.const 0x1p-1 (;=0.5;) - else - local.get $32 - local.get $14 - local.get $49 - i32.eq - i32.and - if (result f64) ;; label = @23 - f64.const 0x1p+0 (;=1;) - else - f64.const 0x1.8p+0 (;=1.5;) - end - end - local.set $52 - local.get $22 - local.get $6 - i32.div_u - i32.const 1 - i32.and - if (result f64) ;; label = @22 - f64.const 0x1.0000000000001p+53 (;=9007199254740994;) - else - f64.const 0x1p+53 (;=9007199254740992;) - end - local.set $53 - block $label$190 ;; label = @22 - local.get $24 - if ;; label = @23 - block ;; label = @24 - local.get $26 - i32.load8_s - i32.const 45 - i32.ne - br_if 2 (;@22;) - local.get $53 - f64.neg - local.set $53 - local.get $52 - f64.neg - local.set $52 - end - end - end - local.get $8 - local.get $22 - local.get $14 - i32.sub - local.tee $14 - i32.store - local.get $53 - local.get $52 - f64.add - local.get $53 - f64.eq - br_if 2 (;@19;) - local.get $8 - local.get $14 - local.get $6 - i32.add - local.tee $5 - i32.store - local.get $5 - i32.const 999999999 - i32.gt_u - if ;; label = @22 - loop $label$193 ;; label = @23 - local.get $8 - i32.const 0 - i32.store - local.get $8 - i32.const -4 - i32.add - local.tee $8 - local.get $1 - i32.lt_u - if ;; label = @24 - local.get $1 - i32.const -4 - i32.add - local.tee $1 - i32.const 0 - i32.store - end - local.get $8 - local.get $8 - i32.load - i32.const 1 - i32.add - local.tee $5 - i32.store - local.get $5 - i32.const 999999999 - i32.gt_u - br_if 0 (;@23;) - end - end - local.get $25 - local.get $1 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - local.set $5 - local.get $1 - i32.load - local.tee $14 - i32.const 10 - i32.lt_u - br_if 2 (;@19;) - i32.const 10 - local.set $6 - loop $label$195 ;; label = @22 - local.get $5 - i32.const 1 - i32.add - local.set $5 - local.get $14 - local.get $6 - i32.const 10 - i32.mul - local.tee $6 - i32.ge_u - br_if 0 (;@22;) - end - end - end - end - local.get $1 - local.set $14 - local.get $5 - local.set $6 - local.get $13 - local.get $8 - i32.const 4 - i32.add - local.tee $8 - i32.le_u - if ;; label = @19 - local.get $13 - local.set $8 - end - end - else - block ;; label = @18 - local.get $1 - local.set $14 - local.get $5 - local.set $6 - local.get $13 - local.set $8 - end - end - i32.const 0 - local.get $6 - i32.sub - local.set $32 - loop $label$198 ;; label = @17 - block $label$199 ;; label = @18 - local.get $8 - local.get $14 - i32.le_u - if ;; label = @19 - block ;; label = @20 - i32.const 0 - local.set $22 - br 2 (;@18;) - end - end - local.get $8 - i32.const -4 - i32.add - local.tee $1 - i32.load - if ;; label = @19 - i32.const 1 - local.set $22 - else - block ;; label = @20 - local.get $1 - local.set $8 - br 3 (;@17;) - end - end - end - end - block $label$203 ;; label = @17 - local.get $29 - if ;; label = @18 - block ;; label = @19 - local.get $35 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $18 - i32.add - local.tee $1 - local.get $6 - i32.gt_s - local.get $6 - i32.const -5 - i32.gt_s - i32.and - if (result i32) ;; label = @20 - block (result i32) ;; label = @21 - local.get $9 - i32.const -1 - i32.add - local.set $5 - local.get $1 - i32.const -1 - i32.add - local.get $6 - i32.sub - end - else - block (result i32) ;; label = @21 - local.get $9 - i32.const -2 - i32.add - local.set $5 - local.get $1 - i32.const -1 - i32.add - end - end - local.set $1 - local.get $12 - i32.const 8 - i32.and - local.tee $13 - br_if 2 (;@17;) - block $label$207 ;; label = @20 - local.get $22 - if ;; label = @21 - block ;; label = @22 - local.get $8 - i32.const -4 - i32.add - i32.load - local.tee $18 - i32.eqz - if ;; label = @23 - block ;; label = @24 - i32.const 9 - local.set $9 - br 4 (;@20;) - end - end - local.get $18 - i32.const 10 - i32.rem_u - if ;; label = @23 - block ;; label = @24 - i32.const 0 - local.set $9 - br 4 (;@20;) - end - else - block ;; label = @24 - i32.const 10 - local.set $13 - i32.const 0 - local.set $9 - end - end - loop $label$212 ;; label = @23 - local.get $9 - i32.const 1 - i32.add - local.set $9 - local.get $18 - local.get $13 - i32.const 10 - i32.mul - local.tee $13 - i32.rem_u - i32.eqz - br_if 0 (;@23;) - end - end - else - i32.const 9 - local.set $9 - end - end - local.get $8 - local.get $25 - i32.sub - i32.const 2 - i32.shr_s - i32.const 9 - i32.mul - i32.const -9 - i32.add - local.set $18 - local.get $5 - i32.const 32 - i32.or - i32.const 102 - i32.eq - if ;; label = @20 - block ;; label = @21 - i32.const 0 - local.set $13 - local.get $1 - local.get $18 - local.get $9 - i32.sub - local.tee $9 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @22 - i32.const 0 - local.tee $9 - else - local.get $9 - end - i32.ge_s - if ;; label = @22 - local.get $9 - local.set $1 - end - end - else - block ;; label = @21 - i32.const 0 - local.set $13 - local.get $1 - local.get $18 - local.get $6 - i32.add - local.get $9 - i32.sub - local.tee $9 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @22 - i32.const 0 - local.tee $9 - else - local.get $9 - end - i32.ge_s - if ;; label = @22 - local.get $9 - local.set $1 - end - end - end - end - else - block ;; label = @19 - local.get $12 - i32.const 8 - i32.and - local.set $13 - local.get $18 - local.set $1 - local.get $9 - local.set $5 - end - end - end - local.get $5 - i32.const 32 - i32.or - i32.const 102 - i32.eq - local.tee $25 - if ;; label = @17 - block ;; label = @18 - i32.const 0 - local.set $9 - local.get $6 - i32.const 0 - i32.le_s - if ;; label = @19 - i32.const 0 - local.set $6 - end - end - else - block ;; label = @18 - local.get $28 - local.get $6 - i32.const 0 - i32.lt_s - if (result i32) ;; label = @19 - local.get $32 - else - local.get $6 - end - i64.extend_i32_s - local.get $33 - call $22 - local.tee $9 - i32.sub - i32.const 2 - i32.lt_s - if ;; label = @19 - loop $label$229 ;; label = @20 - local.get $9 - i32.const -1 - i32.add - local.tee $9 - i32.const 48 - i32.store8 - local.get $28 - local.get $9 - i32.sub - i32.const 2 - i32.lt_s - br_if 0 (;@20;) - end - end - local.get $9 - i32.const -1 - i32.add - local.get $6 - i32.const 31 - i32.shr_s - i32.const 2 - i32.and - i32.const 43 - i32.add - i32.store8 - local.get $9 - i32.const -2 - i32.add - local.tee $6 - local.get $5 - i32.store8 - local.get $6 - local.set $9 - local.get $28 - local.get $6 - i32.sub - local.set $6 - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $24 - i32.const 1 - i32.add - local.get $1 - i32.add - local.get $1 - local.get $13 - i32.or - local.tee $29 - i32.const 0 - i32.ne - i32.add - local.get $6 - i32.add - local.tee $18 - local.get $12 - call $24 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - local.get $26 - local.get $24 - local.get $0 - call $20 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $18 - local.get $12 - i32.const 65536 - i32.xor - call $24 - block $label$231 ;; label = @17 - local.get $25 - if ;; label = @18 - block ;; label = @19 - local.get $14 - local.get $7 - i32.gt_u - if (result i32) ;; label = @20 - local.get $7 - else - local.get $14 - end - local.tee $9 - local.set $6 - loop $label$235 ;; label = @20 - local.get $6 - i32.load - i64.extend_i32_u - local.get $31 - call $22 - local.set $5 - block $label$236 ;; label = @21 - local.get $6 - local.get $9 - i32.eq - if ;; label = @22 - block ;; label = @23 - local.get $5 - local.get $31 - i32.ne - br_if 2 (;@21;) - local.get $34 - i32.const 48 - i32.store8 - local.get $34 - local.set $5 - end - else - block ;; label = @23 - local.get $5 - local.get $19 - i32.le_u - br_if 2 (;@21;) - local.get $19 - i32.const 48 - local.get $5 - local.get $27 - i32.sub - call $35 - drop - loop $label$239 ;; label = @24 - local.get $5 - i32.const -1 - i32.add - local.tee $5 - local.get $19 - i32.gt_u - br_if 0 (;@24;) - end - end - end - end - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @21 - local.get $5 - local.get $41 - local.get $5 - i32.sub - local.get $0 - call $20 - drop - end - local.get $6 - i32.const 4 - i32.add - local.tee $5 - local.get $7 - i32.le_u - if ;; label = @21 - block ;; label = @22 - local.get $5 - local.set $6 - br 2 (;@20;) - end - end - end - block $label$242 ;; label = @20 - local.get $29 - if ;; label = @21 - block ;; label = @22 - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@20;) - i32.const 1700 - i32.const 1 - local.get $0 - call $20 - drop - end - end - end - local.get $1 - i32.const 0 - i32.gt_s - local.get $5 - local.get $8 - i32.lt_u - i32.and - if ;; label = @20 - loop $label$245 ;; label = @21 - local.get $5 - i32.load - i64.extend_i32_u - local.get $31 - call $22 - local.tee $7 - local.get $19 - i32.gt_u - if ;; label = @22 - block ;; label = @23 - local.get $19 - i32.const 48 - local.get $7 - local.get $27 - i32.sub - call $35 - drop - loop $label$247 ;; label = @24 - local.get $7 - i32.const -1 - i32.add - local.tee $7 - local.get $19 - i32.gt_u - br_if 0 (;@24;) - end - end - end - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @22 - local.get $7 - local.get $1 - i32.const 9 - i32.gt_s - if (result i32) ;; label = @23 - i32.const 9 - else - local.get $1 - end - local.get $0 - call $20 - drop - end - local.get $1 - i32.const -9 - i32.add - local.set $7 - local.get $1 - i32.const 9 - i32.gt_s - local.get $5 - i32.const 4 - i32.add - local.tee $5 - local.get $8 - i32.lt_u - i32.and - if ;; label = @22 - block ;; label = @23 - local.get $7 - local.set $1 - br 2 (;@21;) - end - else - local.get $7 - local.set $1 - end - end - end - local.get $0 - i32.const 48 - local.get $1 - i32.const 9 - i32.add - i32.const 9 - i32.const 0 - call $24 - end - else - block ;; label = @19 - local.get $14 - i32.const 4 - i32.add - local.set $5 - local.get $22 - i32.eqz - if ;; label = @20 - local.get $5 - local.set $8 - end - local.get $1 - i32.const -1 - i32.gt_s - if ;; label = @20 - block ;; label = @21 - local.get $13 - i32.eqz - local.set $13 - local.get $14 - local.set $7 - local.get $1 - local.set $5 - loop $label$256 ;; label = @22 - local.get $7 - i32.load - i64.extend_i32_u - local.get $31 - call $22 - local.tee $1 - local.get $31 - i32.eq - if ;; label = @23 - block ;; label = @24 - local.get $34 - i32.const 48 - i32.store8 - local.get $34 - local.set $1 - end - end - block $label$258 ;; label = @23 - local.get $7 - local.get $14 - i32.eq - if ;; label = @24 - block ;; label = @25 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @26 - local.get $1 - i32.const 1 - local.get $0 - call $20 - drop - end - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $13 - local.get $5 - i32.const 1 - i32.lt_s - i32.and - br_if 2 (;@23;) - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@23;) - i32.const 1700 - i32.const 1 - local.get $0 - call $20 - drop - end - else - block ;; label = @25 - local.get $1 - local.get $19 - i32.le_u - br_if 2 (;@23;) - local.get $19 - i32.const 48 - local.get $1 - local.get $43 - i32.add - call $35 - drop - loop $label$262 ;; label = @26 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $19 - i32.gt_u - br_if 0 (;@26;) - end - end - end - end - local.get $41 - local.get $1 - i32.sub - local.set $6 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @23 - local.get $1 - local.get $5 - local.get $6 - i32.gt_s - if (result i32) ;; label = @24 - local.get $6 - else - local.get $5 - end - local.get $0 - call $20 - drop - end - local.get $7 - i32.const 4 - i32.add - local.tee $7 - local.get $8 - i32.lt_u - local.get $5 - local.get $6 - i32.sub - local.tee $5 - i32.const -1 - i32.gt_s - i32.and - br_if 0 (;@22;) - local.get $5 - local.set $1 - end - end - end - local.get $0 - i32.const 48 - local.get $1 - i32.const 18 - i32.add - i32.const 18 - i32.const 0 - call $24 - local.get $0 - i32.load - i32.const 32 - i32.and - br_if 2 (;@17;) - local.get $9 - local.get $28 - local.get $9 - i32.sub - local.get $0 - call $20 - drop - end - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $18 - local.get $12 - i32.const 8192 - i32.xor - call $24 - local.get $18 - local.get $10 - i32.ge_s - if ;; label = @17 - local.get $18 - local.set $10 - end - end - else - block ;; label = @16 - local.get $0 - i32.const 32 - local.get $10 - local.get $52 - local.get $52 - f64.ne - i32.const 0 - i32.or - local.tee $6 - if (result i32) ;; label = @17 - i32.const 0 - local.tee $24 - else - local.get $24 - end - i32.const 3 - i32.add - local.tee $8 - local.get $7 - call $24 - local.get $0 - i32.load - local.tee $1 - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - block ;; label = @18 - local.get $26 - local.get $24 - local.get $0 - call $20 - drop - local.get $0 - i32.load - local.set $1 - end - end - local.get $9 - i32.const 32 - i32.and - i32.const 0 - i32.ne - local.tee $5 - if (result i32) ;; label = @17 - i32.const 1684 - else - i32.const 1688 - end - local.set $7 - local.get $5 - if (result i32) ;; label = @17 - i32.const 1692 - else - i32.const 1696 - end - local.set $5 - local.get $6 - i32.eqz - if ;; label = @17 - local.get $7 - local.set $5 - end - local.get $1 - i32.const 32 - i32.and - i32.eqz - if ;; label = @17 - local.get $5 - i32.const 3 - local.get $0 - call $20 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $8 - local.get $12 - i32.const 8192 - i32.xor - call $24 - local.get $8 - local.get $10 - i32.ge_s - if ;; label = @17 - local.get $8 - local.set $10 - end - end - end - end - local.get $11 - local.set $1 - br 9 (;@4;) - end - local.get $5 - local.set $7 - i32.const 0 - local.set $6 - i32.const 1648 - local.set $8 - local.get $21 - local.set $5 - br 6 (;@6;) - end - local.get $9 - i32.const 32 - i32.and - local.set $7 - local.get $16 - i64.load - local.tee $50 - i64.const 0 - i64.eq - if (result i32) ;; label = @12 - block (result i32) ;; label = @13 - i64.const 0 - local.set $50 - local.get $21 - end - else - block (result i32) ;; label = @13 - local.get $21 - local.set $1 - loop $label$280 ;; label = @14 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $50 - i32.wrap_i64 - i32.const 15 - i32.and - i32.const 1632 - i32.add - i32.load8_u - local.get $7 - i32.or - i32.store8 - local.get $50 - i64.const 4 - i64.shr_u - local.tee $50 - i64.const 0 - i64.ne - br_if 0 (;@14;) - end - local.get $16 - i64.load - local.set $50 - local.get $1 - end - end - local.set $7 - local.get $9 - i32.const 4 - i32.shr_s - i32.const 1648 - i32.add - local.set $8 - local.get $12 - i32.const 8 - i32.and - i32.eqz - local.get $50 - i64.const 0 - i64.eq - i32.or - local.tee $1 - if ;; label = @12 - i32.const 1648 - local.set $8 - end - local.get $1 - if (result i32) ;; label = @12 - i32.const 0 - else - i32.const 2 - end - local.set $6 - br 4 (;@7;) - end - local.get $50 - local.get $21 - call $22 - local.set $7 - br 3 (;@7;) - end - local.get $1 - i32.const 0 - local.get $5 - call $16 - local.tee $13 - i32.eqz - local.set $14 - local.get $13 - local.get $1 - i32.sub - local.set $8 - local.get $1 - local.get $5 - i32.add - local.set $9 - local.get $7 - local.set $12 - local.get $14 - if (result i32) ;; label = @10 - local.get $5 - else - local.get $8 - end - local.set $7 - i32.const 0 - local.set $6 - i32.const 1648 - local.set $8 - local.get $14 - if (result i32) ;; label = @10 - local.get $9 - else - local.get $13 - end - local.set $5 - br 3 (;@6;) - end - i32.const 0 - local.set $1 - i32.const 0 - local.set $5 - local.get $7 - local.set $8 - loop $label$288 ;; label = @9 - block $label$289 ;; label = @10 - local.get $8 - i32.load - local.tee $9 - i32.eqz - br_if 0 (;@10;) - local.get $36 - local.get $9 - call $25 - local.tee $5 - i32.const 0 - i32.lt_s - local.get $5 - local.get $6 - local.get $1 - i32.sub - i32.gt_u - i32.or - br_if 0 (;@10;) - local.get $8 - i32.const 4 - i32.add - local.set $8 - local.get $6 - local.get $5 - local.get $1 - i32.add - local.tee $1 - i32.gt_u - br_if 1 (;@9;) - end - end - local.get $5 - i32.const 0 - i32.lt_s - if ;; label = @9 - block ;; label = @10 - i32.const -1 - local.set $15 - br 5 (;@5;) - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $1 - local.get $12 - call $24 - local.get $1 - if ;; label = @9 - block ;; label = @10 - i32.const 0 - local.set $5 - loop $label$292 ;; label = @11 - local.get $7 - i32.load - local.tee $8 - i32.eqz - br_if 3 (;@8;) - local.get $36 - local.get $8 - call $25 - local.tee $8 - local.get $5 - i32.add - local.tee $5 - local.get $1 - i32.gt_s - br_if 3 (;@8;) - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @12 - local.get $36 - local.get $8 - local.get $0 - call $20 - drop - end - local.get $7 - i32.const 4 - i32.add - local.set $7 - local.get $5 - local.get $1 - i32.lt_u - br_if 0 (;@11;) - br 3 (;@8;) - end - end - else - block ;; label = @10 - i32.const 0 - local.set $1 - br 2 (;@8;) - end - end - end - local.get $0 - i32.const 32 - local.get $10 - local.get $1 - local.get $12 - i32.const 8192 - i32.xor - call $24 - local.get $10 - local.get $1 - i32.le_s - if ;; label = @8 - local.get $1 - local.set $10 - end - local.get $11 - local.set $1 - br 3 (;@4;) - end - local.get $12 - i32.const -65537 - i32.and - local.set $1 - local.get $5 - i32.const -1 - i32.gt_s - if ;; label = @7 - local.get $1 - local.set $12 - end - local.get $5 - local.get $16 - i64.load - i64.const 0 - i64.ne - local.tee $9 - i32.or - if (result i32) ;; label = @7 - block (result i32) ;; label = @8 - local.get $7 - local.set $1 - local.get $5 - local.get $9 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $38 - local.get $7 - i32.sub - i32.add - local.tee $7 - i32.gt_s - if ;; label = @9 - local.get $5 - local.set $7 - end - local.get $21 - end - else - block (result i32) ;; label = @8 - local.get $21 - local.set $1 - i32.const 0 - local.set $7 - local.get $21 - end - end - local.set $5 - end - local.get $0 - i32.const 32 - local.get $10 - local.get $7 - local.get $5 - local.get $1 - i32.sub - local.tee $9 - i32.lt_s - if (result i32) ;; label = @6 - local.get $9 - local.tee $7 - else - local.get $7 - end - local.get $6 - i32.add - local.tee $5 - i32.lt_s - if (result i32) ;; label = @6 - local.get $5 - local.tee $10 - else - local.get $10 - end - local.get $5 - local.get $12 - call $24 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @6 - local.get $8 - local.get $6 - local.get $0 - call $20 - drop - end - local.get $0 - i32.const 48 - local.get $10 - local.get $5 - local.get $12 - i32.const 65536 - i32.xor - call $24 - local.get $0 - i32.const 48 - local.get $7 - local.get $9 - i32.const 0 - call $24 - local.get $0 - i32.load - i32.const 32 - i32.and - i32.eqz - if ;; label = @6 - local.get $1 - local.get $9 - local.get $0 - call $20 - drop - end - local.get $0 - i32.const 32 - local.get $10 - local.get $5 - local.get $12 - i32.const 8192 - i32.xor - call $24 - local.get $11 - local.set $1 - br 1 (;@4;) - end - end - br 1 (;@2;) - end - local.get $0 - i32.eqz - if ;; label = @3 - local.get $17 - if ;; label = @4 - block ;; label = @5 - i32.const 1 - local.set $0 - loop $label$308 ;; label = @6 - local.get $4 - local.get $0 - i32.const 2 - i32.shl - i32.add - i32.load - local.tee $1 - if ;; label = @7 - block ;; label = @8 - local.get $3 - local.get $0 - i32.const 3 - i32.shl - i32.add - local.get $1 - local.get $2 - call $21 - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.const 10 - i32.lt_s - br_if 2 (;@6;) - i32.const 1 - local.set $15 - br 6 (;@2;) - end - end - end - loop $label$310 ;; label = @6 - local.get $4 - local.get $0 - i32.const 2 - i32.shl - i32.add - i32.load - if ;; label = @7 - block ;; label = @8 - i32.const -1 - local.set $15 - br 6 (;@2;) - end - end - local.get $0 - i32.const 1 - i32.add - local.tee $0 - i32.const 10 - i32.lt_s - br_if 0 (;@6;) - i32.const 1 - local.set $15 - end - end - else - i32.const 0 - local.set $15 - end - end - end - local.get $23 - global.set $global$1 - local.get $15 - end - ) - (func $19 (;32;) (type $1) (param $0 i32) (result i32) - i32.const 0 - ) - (func $20 (;33;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) - block $label$1 (result i32) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - local.get $2 - i32.const 16 - i32.add - local.tee $4 - i32.load - local.tee $3 - br_if 0 (;@3;) - local.get $2 - call $29 - if ;; label = @4 - i32.const 0 - local.set $3 - else - block ;; label = @5 - local.get $4 - i32.load - local.set $3 - br 2 (;@3;) - end - end - br 1 (;@2;) - end - local.get $3 - local.get $2 - i32.const 20 - i32.add - local.tee $5 - i32.load - local.tee $4 - i32.sub - local.get $1 - i32.lt_u - if ;; label = @3 - block ;; label = @4 - local.get $2 - local.get $0 - local.get $1 - local.get $2 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - local.set $3 - br 2 (;@2;) - end - end - block $label$7 (result i32) ;; label = @3 - local.get $2 - i32.load8_s offset=75 - i32.const -1 - i32.gt_s - if (result i32) ;; label = @4 - block (result i32) ;; label = @5 - local.get $1 - local.set $3 - loop $label$9 ;; label = @6 - i32.const 0 - local.get $3 - i32.eqz - br_if 3 (;@3;) - drop - local.get $0 - local.get $3 - i32.const -1 - i32.add - local.tee $6 - i32.add - i32.load8_s - i32.const 10 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $6 - local.set $3 - br 2 (;@6;) - end - end - end - local.get $2 - local.get $0 - local.get $3 - local.get $2 - i32.load offset=36 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - local.get $3 - i32.lt_u - br_if 3 (;@2;) - local.get $5 - i32.load - local.set $4 - local.get $1 - local.get $3 - i32.sub - local.set $1 - local.get $0 - local.get $3 - i32.add - local.set $0 - local.get $3 - end - else - i32.const 0 - end - end - local.set $2 - local.get $4 - local.get $0 - local.get $1 - call $36 - drop - local.get $5 - local.get $5 - i32.load - local.get $1 - i32.add - i32.store - local.get $2 - local.get $1 - i32.add - local.set $3 - end - local.get $3 - end - ) - (func $21 (;34;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) (local $4 i64) (local $5 f64) - block $label$1 ;; label = @1 - local.get $1 - i32.const 20 - i32.le_u - if ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - block $label$6 ;; label = @6 - block $label$7 ;; label = @7 - block $label$8 ;; label = @8 - block $label$9 ;; label = @9 - block $label$10 ;; label = @10 - block $label$11 ;; label = @11 - block $label$12 ;; label = @12 - block $label$13 ;; label = @13 - local.get $1 - i32.const 9 - i32.sub - br_table 0 (;@13;) 1 (;@12;) 2 (;@11;) 3 (;@10;) 4 (;@9;) 5 (;@8;) 6 (;@7;) 7 (;@6;) 8 (;@5;) 9 (;@4;) 10 (;@3;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.store - br 11 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i64.extend_i32_s - i64.store - br 10 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i64.extend_i32_u - i64.store - br 9 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - i64.load - local.set $4 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $4 - i64.store - br 8 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 65535 - i32.and - i32.const 16 - i32.shl - i32.const 16 - i32.shr_s - i64.extend_i32_s - i64.store - br 7 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 65535 - i32.and - i64.extend_i32_u - i64.store - br 6 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 255 - i32.and - i32.const 24 - i32.shl - i32.const 24 - i32.shr_s - i64.extend_i32_s - i64.store - br 5 (;@1;) - end - local.get $2 - i32.load - i32.const 3 - i32.add - i32.const -4 - i32.and - local.tee $1 - i32.load - local.set $3 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.store - local.get $0 - local.get $3 - i32.const 255 - i32.and - i64.extend_i32_u - i64.store - br 4 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - f64.load - local.set $5 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $5 - f64.store - br 3 (;@1;) - end - local.get $2 - i32.load - i32.const 7 - i32.add - i32.const -8 - i32.and - local.tee $1 - f64.load - local.set $5 - local.get $2 - local.get $1 - i32.const 8 - i32.add - i32.store - local.get $0 - local.get $5 - f64.store - end - end - end - ) - (func $22 (;35;) (type $9) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i64) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.wrap_i64 - local.set $2 - local.get $0 - i64.const 4294967295 - i64.gt_u - if ;; label = @2 - block ;; label = @3 - loop $label$3 ;; label = @4 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $0 - i64.const 10 - i64.rem_u - i64.const 48 - i64.or - i64.store8 - local.get $0 - i64.const 10 - i64.div_u - local.set $4 - local.get $0 - i64.const 42949672959 - i64.gt_u - if ;; label = @5 - block ;; label = @6 - local.get $4 - local.set $0 - br 2 (;@4;) - end - end - end - local.get $4 - i32.wrap_i64 - local.set $2 - end - end - local.get $2 - if ;; label = @2 - loop $label$6 ;; label = @3 - local.get $1 - i32.const -1 - i32.add - local.tee $1 - local.get $2 - i32.const 10 - i32.rem_u - i32.const 48 - i32.or - i32.store8 - local.get $2 - i32.const 10 - i32.div_u - local.set $3 - local.get $2 - i32.const 10 - i32.ge_u - if ;; label = @4 - block ;; label = @5 - local.get $3 - local.set $2 - br 2 (;@3;) - end - end - end - end - local.get $1 - end - ) - (func $23 (;36;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - i32.const 0 - local.set $1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - loop $label$5 ;; label = @5 - local.get $1 - i32.const 1702 - i32.add - i32.load8_u - local.get $0 - i32.eq - br_if 1 (;@4;) - local.get $1 - i32.const 1 - i32.add - local.tee $1 - i32.const 87 - i32.ne - br_if 0 (;@5;) - i32.const 87 - local.set $1 - i32.const 1790 - local.set $0 - br 2 (;@3;) - end - end - local.get $1 - if ;; label = @4 - block ;; label = @5 - i32.const 1790 - local.set $0 - br 2 (;@3;) - end - else - i32.const 1790 - local.set $0 - end - br 1 (;@2;) - end - loop $label$8 ;; label = @3 - local.get $0 - local.set $2 - loop $label$9 ;; label = @4 - local.get $2 - i32.const 1 - i32.add - local.set $0 - local.get $2 - i32.load8_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.set $2 - br 2 (;@4;) - end - end - end - local.get $1 - i32.const -1 - i32.add - local.tee $1 - br_if 0 (;@3;) - end - end - local.get $0 - end - ) - (func $24 (;37;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) - (local $5 i32) (local $6 i32) (local $7 i32) - block $label$1 ;; label = @1 - global.get $global$1 - local.set $7 - global.get $global$1 - i32.const 256 - i32.add - global.set $global$1 - local.get $7 - local.set $6 - block $label$2 ;; label = @2 - local.get $2 - local.get $3 - i32.gt_s - local.get $4 - i32.const 73728 - i32.and - i32.eqz - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $6 - local.get $1 - local.get $2 - local.get $3 - i32.sub - local.tee $5 - i32.const 256 - i32.gt_u - if (result i32) ;; label = @5 - i32.const 256 - else - local.get $5 - end - call $35 - drop - local.get $0 - i32.load - local.tee $1 - i32.const 32 - i32.and - i32.eqz - local.set $4 - local.get $5 - i32.const 255 - i32.gt_u - if ;; label = @5 - block ;; label = @6 - loop $label$7 ;; label = @7 - local.get $4 - if ;; label = @8 - block ;; label = @9 - local.get $6 - i32.const 256 - local.get $0 - call $20 - drop - local.get $0 - i32.load - local.set $1 - end - end - local.get $1 - i32.const 32 - i32.and - i32.eqz - local.set $4 - local.get $5 - i32.const -256 - i32.add - local.tee $5 - i32.const 255 - i32.gt_u - br_if 0 (;@7;) - end - local.get $4 - i32.eqz - br_if 4 (;@2;) - local.get $2 - local.get $3 - i32.sub - i32.const 255 - i32.and - local.set $5 - end - else - local.get $4 - i32.eqz - br_if 3 (;@2;) - end - local.get $6 - local.get $5 - local.get $0 - call $20 - drop - end - end - end - local.get $7 - global.set $global$1 - end - ) - (func $25 (;38;) (type $4) (param $0 i32) (param $1 i32) (result i32) - local.get $0 - if (result i32) ;; label = @1 - local.get $0 - local.get $1 - i32.const 0 - call $28 - else - i32.const 0 - end - ) - (func $26 (;39;) (type $11) (param $0 f64) (param $1 i32) (result f64) - local.get $0 - local.get $1 - call $27 - ) - (func $27 (;40;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (local $2 i64) (local $3 i64) - block $label$1 (result f64) ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - block $label$4 ;; label = @4 - block $label$5 ;; label = @5 - local.get $0 - i64.reinterpret_f64 - local.tee $2 - i64.const 52 - i64.shr_u - local.tee $3 - i32.wrap_i64 - i32.const 65535 - i32.and - i32.const 2047 - i32.and - i32.const 16 - i32.shl - i32.const 16 - i32.shr_s - i32.const 0 - i32.sub - br_table 0 (;@5;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 1 (;@4;) 2 (;@3;) - end - local.get $1 - local.get $0 - f64.const 0x0p+0 (;=0;) - f64.ne - if (result i32) ;; label = @5 - block (result i32) ;; label = @6 - local.get $0 - f64.const 0x1p+64 (;=18446744073709552000;) - f64.mul - local.get $1 - call $27 - local.set $0 - local.get $1 - i32.load - i32.const -64 - i32.add - end - else - i32.const 0 - end - i32.store - br 2 (;@2;) - end - br 1 (;@2;) - end - local.get $1 - local.get $3 - i32.wrap_i64 - i32.const 2047 - i32.and - i32.const -1022 - i32.add - i32.store - local.get $2 - i64.const -9218868437227405313 - i64.and - i64.const 4602678819172646912 - i64.or - f64.reinterpret_i64 - local.set $0 - end - local.get $0 - end - ) - (func $28 (;41;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $1 - i32.const 128 - i32.lt_u - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.store8 - i32.const 1 - br 4 (;@1;) - end - end - local.get $1 - i32.const 2048 - i32.lt_u - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 192 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - i32.const 2 - br 4 (;@1;) - end - end - local.get $1 - i32.const 55296 - i32.lt_u - local.get $1 - i32.const -8192 - i32.and - i32.const 57344 - i32.eq - i32.or - if ;; label = @4 - block ;; label = @5 - local.get $0 - local.get $1 - i32.const 12 - i32.shr_u - i32.const 224 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=2 - i32.const 3 - br 4 (;@1;) - end - end - local.get $1 - i32.const -65536 - i32.add - i32.const 1048576 - i32.lt_u - if (result i32) ;; label = @4 - block (result i32) ;; label = @5 - local.get $0 - local.get $1 - i32.const 18 - i32.shr_u - i32.const 240 - i32.or - i32.store8 - local.get $0 - local.get $1 - i32.const 12 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=1 - local.get $0 - local.get $1 - i32.const 6 - i32.shr_u - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=2 - local.get $0 - local.get $1 - i32.const 63 - i32.and - i32.const 128 - i32.or - i32.store8 offset=3 - i32.const 4 - end - else - block (result i32) ;; label = @5 - call $11 - i32.const 84 - i32.store - i32.const -1 - end - end - end - else - i32.const 1 - end - end - ) - (func $29 (;42;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - i32.const 74 - i32.add - local.tee $2 - i32.load8_s - local.set $1 - local.get $2 - local.get $1 - i32.const 255 - i32.add - local.get $1 - i32.or - i32.store8 - local.get $0 - i32.load - local.tee $1 - i32.const 8 - i32.and - if (result i32) ;; label = @2 - block (result i32) ;; label = @3 - local.get $0 - local.get $1 - i32.const 32 - i32.or - i32.store - i32.const -1 - end - else - block (result i32) ;; label = @3 - local.get $0 - i32.const 0 - i32.store offset=8 - local.get $0 - i32.const 0 - i32.store offset=4 - local.get $0 - local.get $0 - i32.load offset=44 - local.tee $1 - i32.store offset=28 - local.get $0 - local.get $1 - i32.store offset=20 - local.get $0 - local.get $1 - local.get $0 - i32.load offset=48 - i32.add - i32.store offset=16 - i32.const 0 - end - end - local.tee $0 - end - ) - (func $30 (;43;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $2 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $2 - local.tee $3 - local.get $1 - i32.store - i32.const 1024 - i32.load - local.get $0 - local.get $3 - call $17 - local.set $0 - local.get $2 - global.set $global$1 - local.get $0 - end - ) - (func $31 (;44;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$1 - local.set $14 - global.get $global$1 - i32.const 16 - i32.add - global.set $global$1 - local.get $14 - local.set $18 - block $label$2 ;; label = @2 - local.get $0 - i32.const 245 - i32.lt_u - if ;; label = @3 - block ;; label = @4 - local.get $0 - i32.const 11 - i32.add - i32.const -8 - i32.and - local.set $3 - i32.const 3644 - i32.load - local.tee $8 - local.get $0 - i32.const 11 - i32.lt_u - if (result i32) ;; label = @5 - i32.const 16 - local.tee $3 - else - local.get $3 - end - i32.const 3 - i32.shr_u - local.tee $2 - i32.shr_u - local.tee $0 - i32.const 3 - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $0 - i32.const 1 - i32.and - i32.const 1 - i32.xor - local.get $2 - i32.add - local.tee $5 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.tee $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $7 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.set $4 - local.get $2 - local.get $4 - i32.eq - if ;; label = @7 - i32.const 3644 - local.get $8 - i32.const 1 - local.get $5 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - else - block ;; label = @8 - local.get $4 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $4 - i32.const 12 - i32.add - local.tee $0 - i32.load - local.get $7 - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $0 - local.get $2 - i32.store - local.get $3 - local.get $4 - i32.store - end - else - call $fimport$10 - end - end - end - local.get $7 - local.get $5 - i32.const 3 - i32.shl - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $7 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - local.get $14 - global.set $global$1 - local.get $1 - return - end - end - local.get $3 - i32.const 3652 - i32.load - local.tee $16 - i32.gt_u - if ;; label = @5 - block ;; label = @6 - local.get $0 - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.get $2 - i32.shl - i32.const 2 - local.get $2 - i32.shl - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.or - i32.and - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $5 - local.get $0 - local.get $5 - i32.shr_u - local.tee $2 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $5 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - local.tee $2 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $2 - local.get $0 - i32.shr_u - i32.add - local.tee $11 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.tee $4 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $9 - i32.const 8 - i32.add - local.tee $5 - i32.load - local.set $12 - local.get $4 - local.get $12 - i32.eq - if ;; label = @9 - i32.const 3644 - local.get $8 - i32.const 1 - local.get $11 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $7 - i32.store - else - block ;; label = @10 - local.get $12 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$10 - end - local.get $12 - i32.const 12 - i32.add - local.tee $0 - i32.load - local.get $9 - i32.eq - if ;; label = @11 - block ;; label = @12 - local.get $0 - local.get $4 - i32.store - local.get $2 - local.get $12 - i32.store - local.get $8 - local.set $7 - end - else - call $fimport$10 - end - end - end - local.get $9 - local.get $3 - i32.const 3 - i32.or - i32.store offset=4 - local.get $9 - local.get $3 - i32.add - local.tee $4 - local.get $11 - i32.const 3 - i32.shl - local.get $3 - i32.sub - local.tee $11 - i32.const 1 - i32.or - i32.store offset=4 - local.get $4 - local.get $11 - i32.add - local.get $11 - i32.store - local.get $16 - if ;; label = @9 - block ;; label = @10 - i32.const 3664 - i32.load - local.set $9 - local.get $16 - i32.const 3 - i32.shr_u - local.tee $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.set $2 - local.get $7 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @11 - local.get $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $0 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @12 - call $fimport$10 - else - block ;; label = @13 - local.get $3 - local.set $6 - local.get $0 - local.set $1 - end - end - else - block ;; label = @12 - i32.const 3644 - local.get $7 - local.get $0 - i32.or - i32.store - local.get $2 - i32.const 8 - i32.add - local.set $6 - local.get $2 - local.set $1 - end - end - local.get $6 - local.get $9 - i32.store - local.get $1 - local.get $9 - i32.store offset=12 - local.get $9 - local.get $1 - i32.store offset=8 - local.get $9 - local.get $2 - i32.store offset=12 - end - end - i32.const 3652 - local.get $11 - i32.store - i32.const 3664 - local.get $4 - i32.store - local.get $14 - global.set $global$1 - local.get $5 - return - end - end - i32.const 3648 - i32.load - local.tee $6 - if ;; label = @7 - block ;; label = @8 - local.get $6 - i32.const 0 - local.get $6 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $2 - local.get $0 - local.get $2 - i32.shr_u - local.tee $1 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $2 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - local.tee $1 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $1 - local.get $0 - i32.shr_u - i32.add - i32.const 2 - i32.shl - i32.const 3948 - i32.add - i32.load - local.tee $2 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.sub - local.set $9 - local.get $2 - local.set $1 - loop $label$25 ;; label = @9 - block $label$26 ;; label = @10 - local.get $1 - i32.load offset=16 - local.tee $0 - i32.eqz - if ;; label = @11 - local.get $1 - i32.load offset=20 - local.tee $0 - i32.eqz - br_if 1 (;@10;) - end - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.sub - local.tee $1 - local.get $9 - i32.lt_u - local.tee $7 - if ;; label = @11 - local.get $1 - local.set $9 - end - local.get $0 - local.set $1 - local.get $7 - if ;; label = @11 - local.get $0 - local.set $2 - end - br 1 (;@9;) - end - end - local.get $2 - i32.const 3660 - i32.load - local.tee $12 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $2 - local.get $2 - local.get $3 - i32.add - local.tee $13 - i32.ge_u - if ;; label = @9 - call $fimport$10 - end - local.get $2 - i32.load offset=24 - local.set $15 - block $label$32 ;; label = @9 - local.get $2 - i32.load offset=12 - local.tee $0 - local.get $2 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $2 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @12 - local.get $2 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @13 - block ;; label = @14 - i32.const 0 - local.set $4 - br 5 (;@9;) - end - end - end - loop $label$36 ;; label = @12 - local.get $0 - i32.const 20 - i32.add - local.tee $11 - i32.load - local.tee $7 - if ;; label = @13 - block ;; label = @14 - local.get $7 - local.set $0 - local.get $11 - local.set $1 - br 2 (;@12;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $11 - i32.load - local.tee $7 - if ;; label = @13 - block ;; label = @14 - local.get $7 - local.set $0 - local.get $11 - local.set $1 - br 2 (;@12;) - end - end - end - local.get $1 - local.get $12 - i32.lt_u - if ;; label = @12 - call $fimport$10 - else - block ;; label = @13 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $4 - end - end - end - else - block ;; label = @11 - local.get $2 - i32.load offset=8 - local.tee $11 - local.get $12 - i32.lt_u - if ;; label = @12 - call $fimport$10 - end - local.get $11 - i32.const 12 - i32.add - local.tee $7 - i32.load - local.get $2 - i32.ne - if ;; label = @12 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $2 - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $7 - local.get $0 - i32.store - local.get $1 - local.get $11 - i32.store - local.get $0 - local.set $4 - end - else - call $fimport$10 - end - end - end - end - block $label$46 ;; label = @9 - local.get $15 - if ;; label = @10 - block ;; label = @11 - local.get $2 - local.get $2 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $0 - local.get $4 - i32.store - local.get $4 - i32.eqz - if ;; label = @14 - block ;; label = @15 - i32.const 3648 - local.get $6 - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 6 (;@9;) - end - end - end - else - block ;; label = @13 - local.get $15 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$10 - end - local.get $15 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $2 - i32.eq - if ;; label = @14 - local.get $0 - local.get $4 - i32.store - else - local.get $15 - local.get $4 - i32.store offset=20 - end - local.get $4 - i32.eqz - br_if 4 (;@9;) - end - end - local.get $4 - i32.const 3660 - i32.load - local.tee $0 - i32.lt_u - if ;; label = @12 - call $fimport$10 - end - local.get $4 - local.get $15 - i32.store offset=24 - local.get $2 - i32.load offset=16 - local.tee $1 - if ;; label = @12 - local.get $1 - local.get $0 - i32.lt_u - if ;; label = @13 - call $fimport$10 - else - block ;; label = @14 - local.get $4 - local.get $1 - i32.store offset=16 - local.get $1 - local.get $4 - i32.store offset=24 - end - end - end - local.get $2 - i32.load offset=20 - local.tee $0 - if ;; label = @12 - local.get $0 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @13 - call $fimport$10 - else - block ;; label = @14 - local.get $4 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $4 - i32.store offset=24 - end - end - end - end - end - end - local.get $9 - i32.const 16 - i32.lt_u - if ;; label = @9 - block ;; label = @10 - local.get $2 - local.get $9 - local.get $3 - i32.add - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - else - block ;; label = @10 - local.get $2 - local.get $3 - i32.const 3 - i32.or - i32.store offset=4 - local.get $13 - local.get $9 - i32.const 1 - i32.or - i32.store offset=4 - local.get $13 - local.get $9 - i32.add - local.get $9 - i32.store - local.get $16 - if ;; label = @11 - block ;; label = @12 - i32.const 3664 - i32.load - local.set $7 - local.get $16 - i32.const 3 - i32.shr_u - local.tee $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.set $3 - local.get $8 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @13 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$10 - else - block ;; label = @15 - local.get $1 - local.set $10 - local.get $0 - local.set $5 - end - end - else - block ;; label = @14 - i32.const 3644 - local.get $8 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $10 - local.get $3 - local.set $5 - end - end - local.get $10 - local.get $7 - i32.store - local.get $5 - local.get $7 - i32.store offset=12 - local.get $7 - local.get $5 - i32.store offset=8 - local.get $7 - local.get $3 - i32.store offset=12 - end - end - i32.const 3652 - local.get $9 - i32.store - i32.const 3664 - local.get $13 - i32.store - end - end - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - else - local.get $3 - local.set $0 - end - end - else - local.get $3 - local.set $0 - end - end - else - local.get $0 - i32.const -65 - i32.gt_u - if ;; label = @4 - i32.const -1 - local.set $0 - else - block ;; label = @5 - local.get $0 - i32.const 11 - i32.add - local.tee $0 - i32.const -8 - i32.and - local.set $7 - i32.const 3648 - i32.load - local.tee $5 - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @8 - local.get $7 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @9 - i32.const 31 - else - local.get $7 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $3 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $3 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.set $17 - i32.const 0 - local.get $7 - i32.sub - local.set $3 - block $label$78 ;; label = @8 - block $label$79 ;; label = @9 - block $label$80 ;; label = @10 - local.get $17 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - i32.load - local.tee $1 - if ;; label = @11 - block ;; label = @12 - i32.const 25 - local.get $17 - i32.const 1 - i32.shr_u - i32.sub - local.set $0 - i32.const 0 - local.set $4 - local.get $7 - local.get $17 - i32.const 31 - i32.eq - if (result i32) ;; label = @13 - i32.const 0 - else - local.get $0 - end - i32.shl - local.set $10 - i32.const 0 - local.set $0 - loop $label$84 ;; label = @13 - local.get $1 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.sub - local.tee $6 - local.get $3 - i32.lt_u - if ;; label = @14 - local.get $6 - if ;; label = @15 - block ;; label = @16 - local.get $6 - local.set $3 - local.get $1 - local.set $0 - end - else - block ;; label = @16 - i32.const 0 - local.set $3 - local.get $1 - local.set $0 - br 7 (;@9;) - end - end - end - local.get $1 - i32.load offset=20 - local.tee $19 - i32.eqz - local.get $19 - local.get $1 - i32.const 16 - i32.add - local.get $10 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - i32.load - local.tee $6 - i32.eq - i32.or - if (result i32) ;; label = @14 - local.get $4 - else - local.get $19 - end - local.set $1 - local.get $10 - local.get $6 - i32.eqz - local.tee $4 - i32.const 1 - i32.and - i32.const 1 - i32.xor - i32.shl - local.set $10 - local.get $4 - if ;; label = @14 - block ;; label = @15 - local.get $1 - local.set $4 - local.get $0 - local.set $1 - br 5 (;@10;) - end - else - block ;; label = @15 - local.get $1 - local.set $4 - local.get $6 - local.set $1 - br 2 (;@13;) - end - end - end - end - else - block ;; label = @12 - i32.const 0 - local.set $4 - i32.const 0 - local.set $1 - end - end - end - local.get $4 - i32.eqz - local.get $1 - i32.eqz - i32.and - if (result i32) ;; label = @10 - block (result i32) ;; label = @11 - local.get $5 - i32.const 2 - local.get $17 - i32.shl - local.tee $0 - i32.const 0 - local.get $0 - i32.sub - i32.or - i32.and - local.tee $0 - i32.eqz - if ;; label = @12 - block ;; label = @13 - local.get $7 - local.set $0 - br 11 (;@2;) - end - end - local.get $0 - i32.const 0 - local.get $0 - i32.sub - i32.and - i32.const -1 - i32.add - local.tee $0 - i32.const 12 - i32.shr_u - i32.const 16 - i32.and - local.set $10 - local.get $0 - local.get $10 - i32.shr_u - local.tee $4 - i32.const 5 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - local.get $10 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 2 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 1 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - local.tee $4 - i32.const 1 - i32.shr_u - i32.const 1 - i32.and - local.tee $0 - i32.or - local.get $4 - local.get $0 - i32.shr_u - i32.add - i32.const 2 - i32.shl - i32.const 3948 - i32.add - i32.load - end - else - local.get $4 - end - local.tee $0 - br_if 0 (;@9;) - local.get $1 - local.set $4 - br 1 (;@8;) - end - loop $label$96 ;; label = @9 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.sub - local.tee $4 - local.get $3 - i32.lt_u - local.tee $10 - if ;; label = @10 - local.get $4 - local.set $3 - end - local.get $10 - if ;; label = @10 - local.get $0 - local.set $1 - end - local.get $0 - i32.load offset=16 - local.tee $4 - if ;; label = @10 - block ;; label = @11 - local.get $4 - local.set $0 - br 2 (;@9;) - end - end - local.get $0 - i32.load offset=20 - local.tee $0 - br_if 0 (;@9;) - local.get $1 - local.set $4 - end - end - local.get $4 - if ;; label = @8 - local.get $3 - i32.const 3652 - i32.load - local.get $7 - i32.sub - i32.lt_u - if ;; label = @9 - block ;; label = @10 - local.get $4 - i32.const 3660 - i32.load - local.tee $12 - i32.lt_u - if ;; label = @11 - call $fimport$10 - end - local.get $4 - local.get $4 - local.get $7 - i32.add - local.tee $6 - i32.ge_u - if ;; label = @11 - call $fimport$10 - end - local.get $4 - i32.load offset=24 - local.set $10 - block $label$104 ;; label = @11 - local.get $4 - i32.load offset=12 - local.tee $0 - local.get $4 - i32.eq - if ;; label = @12 - block ;; label = @13 - local.get $4 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @14 - local.get $4 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @15 - block ;; label = @16 - i32.const 0 - local.set $13 - br 5 (;@11;) - end - end - end - loop $label$108 ;; label = @14 - local.get $0 - i32.const 20 - i32.add - local.tee $9 - i32.load - local.tee $11 - if ;; label = @15 - block ;; label = @16 - local.get $11 - local.set $0 - local.get $9 - local.set $1 - br 2 (;@14;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $9 - i32.load - local.tee $11 - if ;; label = @15 - block ;; label = @16 - local.get $11 - local.set $0 - local.get $9 - local.set $1 - br 2 (;@14;) - end - end - end - local.get $1 - local.get $12 - i32.lt_u - if ;; label = @14 - call $fimport$10 - else - block ;; label = @15 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $13 - end - end - end - else - block ;; label = @13 - local.get $4 - i32.load offset=8 - local.tee $9 - local.get $12 - i32.lt_u - if ;; label = @14 - call $fimport$10 - end - local.get $9 - i32.const 12 - i32.add - local.tee $11 - i32.load - local.get $4 - i32.ne - if ;; label = @14 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $4 - i32.eq - if ;; label = @14 - block ;; label = @15 - local.get $11 - local.get $0 - i32.store - local.get $1 - local.get $9 - i32.store - local.get $0 - local.set $13 - end - else - call $fimport$10 - end - end - end - end - block $label$118 ;; label = @11 - local.get $10 - if ;; label = @12 - block ;; label = @13 - local.get $4 - local.get $4 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @14 - block ;; label = @15 - local.get $0 - local.get $13 - i32.store - local.get $13 - i32.eqz - if ;; label = @16 - block ;; label = @17 - i32.const 3648 - local.get $5 - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $2 - i32.store - br 6 (;@11;) - end - end - end - else - block ;; label = @15 - local.get $10 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @16 - call $fimport$10 - end - local.get $10 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @16 - local.get $0 - local.get $13 - i32.store - else - local.get $10 - local.get $13 - i32.store offset=20 - end - local.get $13 - i32.eqz - if ;; label = @16 - block ;; label = @17 - local.get $5 - local.set $2 - br 6 (;@11;) - end - end - end - end - local.get $13 - i32.const 3660 - i32.load - local.tee $0 - i32.lt_u - if ;; label = @14 - call $fimport$10 - end - local.get $13 - local.get $10 - i32.store offset=24 - local.get $4 - i32.load offset=16 - local.tee $1 - if ;; label = @14 - local.get $1 - local.get $0 - i32.lt_u - if ;; label = @15 - call $fimport$10 - else - block ;; label = @16 - local.get $13 - local.get $1 - i32.store offset=16 - local.get $1 - local.get $13 - i32.store offset=24 - end - end - end - local.get $4 - i32.load offset=20 - local.tee $0 - if ;; label = @14 - local.get $0 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @15 - call $fimport$10 - else - block ;; label = @16 - local.get $13 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $13 - i32.store offset=24 - local.get $5 - local.set $2 - end - end - else - local.get $5 - local.set $2 - end - end - else - local.get $5 - local.set $2 - end - end - block $label$136 ;; label = @11 - local.get $3 - i32.const 16 - i32.lt_u - if ;; label = @12 - block ;; label = @13 - local.get $4 - local.get $3 - local.get $7 - i32.add - local.tee $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $4 - local.get $0 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - else - block ;; label = @13 - local.get $4 - local.get $7 - i32.const 3 - i32.or - i32.store offset=4 - local.get $6 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $3 - i32.add - local.get $3 - i32.store - local.get $3 - i32.const 3 - i32.shr_u - local.set $0 - local.get $3 - i32.const 256 - i32.lt_u - if ;; label = @14 - block ;; label = @15 - local.get $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.set $3 - i32.const 3644 - i32.load - local.tee $1 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @16 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @17 - call $fimport$10 - else - block ;; label = @18 - local.get $1 - local.set $16 - local.get $0 - local.set $8 - end - end - else - block ;; label = @17 - i32.const 3644 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $16 - local.get $3 - local.set $8 - end - end - local.get $16 - local.get $6 - i32.store - local.get $8 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $8 - i32.store offset=8 - local.get $6 - local.get $3 - i32.store offset=12 - br 4 (;@11;) - end - end - local.get $3 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @14 - local.get $3 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @15 - i32.const 31 - else - local.get $3 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $5 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $5 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $5 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - local.set $1 - local.get $6 - local.get $5 - i32.store offset=28 - local.get $6 - i32.const 16 - i32.add - local.tee $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store - local.get $2 - i32.const 1 - local.get $5 - i32.shl - local.tee $0 - i32.and - i32.eqz - if ;; label = @14 - block ;; label = @15 - i32.const 3648 - local.get $2 - local.get $0 - i32.or - i32.store - local.get $1 - local.get $6 - i32.store - local.get $6 - local.get $1 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 4 (;@11;) - end - end - local.get $1 - i32.load - local.set $0 - i32.const 25 - local.get $5 - i32.const 1 - i32.shr_u - i32.sub - local.set $1 - local.get $3 - local.get $5 - i32.const 31 - i32.eq - if (result i32) ;; label = @14 - i32.const 0 - else - local.get $1 - end - i32.shl - local.set $5 - block $label$151 ;; label = @14 - block $label$152 ;; label = @15 - block $label$153 ;; label = @16 - loop $label$154 ;; label = @17 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $3 - i32.eq - br_if 2 (;@15;) - local.get $5 - i32.const 1 - i32.shl - local.set $2 - local.get $0 - i32.const 16 - i32.add - local.get $5 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $5 - i32.load - local.tee $1 - i32.eqz - br_if 1 (;@16;) - local.get $2 - local.set $5 - local.get $1 - local.set $0 - br 0 (;@17;) - end - end - local.get $5 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @16 - call $fimport$10 - else - block ;; label = @17 - local.get $5 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 6 (;@11;) - end - end - br 1 (;@14;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $2 - i32.const 3660 - i32.load - local.tee $1 - i32.ge_u - local.get $0 - local.get $1 - i32.ge_u - i32.and - if ;; label = @15 - block ;; label = @16 - local.get $2 - local.get $6 - i32.store offset=12 - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $2 - i32.store offset=8 - local.get $6 - local.get $0 - i32.store offset=12 - local.get $6 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - end - end - local.get $14 - global.set $global$1 - local.get $4 - i32.const 8 - i32.add - return - end - else - local.get $7 - local.set $0 - end - else - local.get $7 - local.set $0 - end - end - else - local.get $7 - local.set $0 - end - end - end - end - end - i32.const 3652 - i32.load - local.tee $1 - local.get $0 - i32.ge_u - if ;; label = @2 - block ;; label = @3 - i32.const 3664 - i32.load - local.set $2 - local.get $1 - local.get $0 - i32.sub - local.tee $3 - i32.const 15 - i32.gt_u - if ;; label = @4 - block ;; label = @5 - i32.const 3664 - local.get $2 - local.get $0 - i32.add - local.tee $1 - i32.store - i32.const 3652 - local.get $3 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $1 - local.get $3 - i32.add - local.get $3 - i32.store - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - end - else - block ;; label = @5 - i32.const 3652 - i32.const 0 - i32.store - i32.const 3664 - i32.const 0 - i32.store - local.get $2 - local.get $1 - i32.const 3 - i32.or - i32.store offset=4 - local.get $2 - local.get $1 - i32.add - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const 1 - i32.or - i32.store - end - end - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - i32.const 3656 - i32.load - local.tee $10 - local.get $0 - i32.gt_u - if ;; label = @2 - block ;; label = @3 - i32.const 3656 - local.get $10 - local.get $0 - i32.sub - local.tee $3 - i32.store - i32.const 3668 - i32.const 3668 - i32.load - local.tee $2 - local.get $0 - i32.add - local.tee $1 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - i32.const 4116 - i32.load - if (result i32) ;; label = @2 - i32.const 4124 - i32.load - else - block (result i32) ;; label = @3 - i32.const 4124 - i32.const 4096 - i32.store - i32.const 4120 - i32.const 4096 - i32.store - i32.const 4128 - i32.const -1 - i32.store - i32.const 4132 - i32.const -1 - i32.store - i32.const 4136 - i32.const 0 - i32.store - i32.const 4088 - i32.const 0 - i32.store - local.get $18 - local.get $18 - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - local.tee $1 - i32.store - i32.const 4116 - local.get $1 - i32.store - i32.const 4096 - end - end - local.tee $1 - local.get $0 - i32.const 47 - i32.add - local.tee $13 - i32.add - local.tee $8 - i32.const 0 - local.get $1 - i32.sub - local.tee $4 - i32.and - local.tee $6 - local.get $0 - i32.le_u - if ;; label = @2 - block ;; label = @3 - local.get $14 - global.set $global$1 - i32.const 0 - return - end - end - i32.const 4084 - i32.load - local.tee $2 - if ;; label = @2 - i32.const 4076 - i32.load - local.tee $3 - local.get $6 - i32.add - local.tee $1 - local.get $3 - i32.le_u - local.get $1 - local.get $2 - i32.gt_u - i32.or - if ;; label = @3 - block ;; label = @4 - local.get $14 - global.set $global$1 - i32.const 0 - return - end - end - end - local.get $0 - i32.const 48 - i32.add - local.set $7 - block $label$171 ;; label = @2 - block $label$172 ;; label = @3 - i32.const 4088 - i32.load - i32.const 4 - i32.and - i32.eqz - if ;; label = @4 - block ;; label = @5 - block $label$174 ;; label = @6 - block $label$175 ;; label = @7 - block $label$176 ;; label = @8 - i32.const 3668 - i32.load - local.tee $3 - i32.eqz - br_if 0 (;@8;) - i32.const 4092 - local.set $2 - loop $label$177 ;; label = @9 - block $label$178 ;; label = @10 - local.get $2 - i32.load - local.tee $1 - local.get $3 - i32.le_u - if ;; label = @11 - local.get $1 - local.get $2 - i32.const 4 - i32.add - local.tee $5 - i32.load - i32.add - local.get $3 - i32.gt_u - br_if 1 (;@10;) - end - local.get $2 - i32.load offset=8 - local.tee $1 - i32.eqz - br_if 2 (;@8;) - local.get $1 - local.set $2 - br 1 (;@9;) - end - end - local.get $8 - local.get $10 - i32.sub - local.get $4 - i32.and - local.tee $3 - i32.const 2147483647 - i32.lt_u - if ;; label = @9 - local.get $3 - call $34 - local.tee $1 - local.get $2 - i32.load - local.get $5 - i32.load - i32.add - i32.eq - if ;; label = @10 - local.get $1 - i32.const -1 - i32.ne - br_if 7 (;@3;) - else - block ;; label = @11 - local.get $1 - local.set $2 - local.get $3 - local.set $1 - br 4 (;@7;) - end - end - end - br 2 (;@6;) - end - i32.const 0 - call $34 - local.tee $1 - i32.const -1 - i32.ne - if ;; label = @8 - block ;; label = @9 - i32.const 4120 - i32.load - local.tee $2 - i32.const -1 - i32.add - local.tee $5 - local.get $1 - local.tee $3 - i32.add - i32.const 0 - local.get $2 - i32.sub - i32.and - local.get $3 - i32.sub - local.set $2 - local.get $5 - local.get $3 - i32.and - if (result i32) ;; label = @10 - local.get $2 - else - i32.const 0 - end - local.get $6 - i32.add - local.tee $3 - i32.const 4076 - i32.load - local.tee $5 - i32.add - local.set $4 - local.get $3 - local.get $0 - i32.gt_u - local.get $3 - i32.const 2147483647 - i32.lt_u - i32.and - if ;; label = @10 - block ;; label = @11 - i32.const 4084 - i32.load - local.tee $2 - if ;; label = @12 - local.get $4 - local.get $5 - i32.le_u - local.get $4 - local.get $2 - i32.gt_u - i32.or - br_if 6 (;@6;) - end - local.get $3 - call $34 - local.tee $2 - local.get $1 - i32.eq - br_if 8 (;@3;) - local.get $3 - local.set $1 - br 4 (;@7;) - end - end - end - end - br 1 (;@6;) - end - i32.const 0 - local.get $1 - i32.sub - local.set $5 - local.get $7 - local.get $1 - i32.gt_u - local.get $1 - i32.const 2147483647 - i32.lt_u - local.get $2 - i32.const -1 - i32.ne - i32.and - i32.and - if ;; label = @7 - local.get $13 - local.get $1 - i32.sub - i32.const 4124 - i32.load - local.tee $3 - i32.add - i32.const 0 - local.get $3 - i32.sub - i32.and - local.tee $3 - i32.const 2147483647 - i32.lt_u - if ;; label = @8 - local.get $3 - call $34 - i32.const -1 - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $5 - call $34 - drop - br 4 (;@6;) - end - else - local.get $3 - local.get $1 - i32.add - local.set $3 - end - else - local.get $1 - local.set $3 - end - else - local.get $1 - local.set $3 - end - local.get $2 - i32.const -1 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $2 - local.set $1 - br 5 (;@3;) - end - end - end - i32.const 4088 - i32.const 4088 - i32.load - i32.const 4 - i32.or - i32.store - end - end - local.get $6 - i32.const 2147483647 - i32.lt_u - if ;; label = @4 - local.get $6 - call $34 - local.tee $1 - i32.const 0 - call $34 - local.tee $3 - i32.lt_u - local.get $1 - i32.const -1 - i32.ne - local.get $3 - i32.const -1 - i32.ne - i32.and - i32.and - if ;; label = @5 - local.get $3 - local.get $1 - i32.sub - local.tee $3 - local.get $0 - i32.const 40 - i32.add - i32.gt_u - br_if 2 (;@3;) - end - end - br 1 (;@2;) - end - i32.const 4076 - i32.const 4076 - i32.load - local.get $3 - i32.add - local.tee $2 - i32.store - local.get $2 - i32.const 4080 - i32.load - i32.gt_u - if ;; label = @3 - i32.const 4080 - local.get $2 - i32.store - end - block $label$198 ;; label = @3 - i32.const 3668 - i32.load - local.tee $8 - if ;; label = @4 - block ;; label = @5 - i32.const 4092 - local.set $2 - block $label$200 ;; label = @6 - block $label$201 ;; label = @7 - loop $label$202 ;; label = @8 - local.get $1 - local.get $2 - i32.load - local.tee $4 - local.get $2 - i32.const 4 - i32.add - local.tee $7 - i32.load - local.tee $5 - i32.add - i32.eq - br_if 1 (;@7;) - local.get $2 - i32.load offset=8 - local.tee $2 - br_if 0 (;@8;) - end - br 1 (;@6;) - end - local.get $2 - i32.load offset=12 - i32.const 8 - i32.and - i32.eqz - if ;; label = @7 - local.get $8 - local.get $1 - i32.lt_u - local.get $8 - local.get $4 - i32.ge_u - i32.and - if ;; label = @8 - block ;; label = @9 - local.get $7 - local.get $5 - local.get $3 - i32.add - i32.store - i32.const 3656 - i32.load - local.set $5 - i32.const 0 - local.get $8 - i32.const 8 - i32.add - local.tee $2 - i32.sub - i32.const 7 - i32.and - local.set $1 - i32.const 3668 - local.get $8 - local.get $2 - i32.const 7 - i32.and - if (result i32) ;; label = @10 - local.get $1 - else - i32.const 0 - local.tee $1 - end - i32.add - local.tee $2 - i32.store - i32.const 3656 - local.get $3 - local.get $1 - i32.sub - local.get $5 - i32.add - local.tee $1 - i32.store - local.get $2 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $1 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 3672 - i32.const 4132 - i32.load - i32.store - br 6 (;@3;) - end - end - end - end - local.get $1 - i32.const 3660 - i32.load - local.tee $2 - i32.lt_u - if ;; label = @6 - block ;; label = @7 - i32.const 3660 - local.get $1 - i32.store - local.get $1 - local.set $2 - end - end - local.get $1 - local.get $3 - i32.add - local.set $10 - i32.const 4092 - local.set $5 - block $label$208 ;; label = @6 - block $label$209 ;; label = @7 - loop $label$210 ;; label = @8 - local.get $5 - i32.load - local.get $10 - i32.eq - br_if 1 (;@7;) - local.get $5 - i32.load offset=8 - local.tee $5 - br_if 0 (;@8;) - i32.const 4092 - local.set $5 - end - br 1 (;@6;) - end - local.get $5 - i32.load offset=12 - i32.const 8 - i32.and - if ;; label = @7 - i32.const 4092 - local.set $5 - else - block ;; label = @8 - local.get $5 - local.get $1 - i32.store - local.get $5 - i32.const 4 - i32.add - local.tee $5 - local.get $5 - i32.load - local.get $3 - i32.add - i32.store - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $4 - i32.sub - i32.const 7 - i32.and - local.set $7 - i32.const 0 - local.get $10 - i32.const 8 - i32.add - local.tee $5 - i32.sub - i32.const 7 - i32.and - local.set $3 - local.get $1 - local.get $4 - i32.const 7 - i32.and - if (result i32) ;; label = @9 - local.get $7 - else - i32.const 0 - end - i32.add - local.tee $13 - local.get $0 - i32.add - local.set $6 - local.get $10 - local.get $5 - i32.const 7 - i32.and - if (result i32) ;; label = @9 - local.get $3 - else - i32.const 0 - end - i32.add - local.tee $4 - local.get $13 - i32.sub - local.get $0 - i32.sub - local.set $7 - local.get $13 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - block $label$217 ;; label = @9 - local.get $4 - local.get $8 - i32.eq - if ;; label = @10 - block ;; label = @11 - i32.const 3656 - i32.const 3656 - i32.load - local.get $7 - i32.add - local.tee $0 - i32.store - i32.const 3668 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - end - else - block ;; label = @11 - local.get $4 - i32.const 3664 - i32.load - i32.eq - if ;; label = @12 - block ;; label = @13 - i32.const 3652 - i32.const 3652 - i32.load - local.get $7 - i32.add - local.tee $0 - i32.store - i32.const 3664 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $0 - i32.add - local.get $0 - i32.store - br 4 (;@9;) - end - end - local.get $4 - i32.load offset=4 - local.tee $0 - i32.const 3 - i32.and - i32.const 1 - i32.eq - if (result i32) ;; label = @12 - block (result i32) ;; label = @13 - local.get $0 - i32.const -8 - i32.and - local.set $11 - local.get $0 - i32.const 3 - i32.shr_u - local.set $1 - block $label$222 ;; label = @14 - local.get $0 - i32.const 256 - i32.lt_u - if ;; label = @15 - block ;; label = @16 - local.get $4 - i32.load offset=12 - local.set $5 - block $label$224 ;; label = @17 - local.get $4 - i32.load offset=8 - local.tee $3 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.tee $0 - i32.ne - if ;; label = @18 - block ;; label = @19 - local.get $3 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $3 - i32.load offset=12 - local.get $4 - i32.eq - br_if 2 (;@17;) - call $fimport$10 - end - end - end - local.get $5 - local.get $3 - i32.eq - if ;; label = @17 - block ;; label = @18 - i32.const 3644 - i32.const 3644 - i32.load - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 4 (;@14;) - end - end - block $label$228 ;; label = @17 - local.get $5 - local.get $0 - i32.eq - if ;; label = @18 - local.get $5 - i32.const 8 - i32.add - local.set $20 - else - block ;; label = @19 - local.get $5 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $5 - i32.const 8 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - block ;; label = @21 - local.get $0 - local.set $20 - br 4 (;@17;) - end - end - call $fimport$10 - end - end - end - local.get $3 - local.get $5 - i32.store offset=12 - local.get $20 - local.get $3 - i32.store - end - else - block ;; label = @16 - local.get $4 - i32.load offset=24 - local.set $8 - block $label$234 ;; label = @17 - local.get $4 - i32.load offset=12 - local.tee $0 - local.get $4 - i32.eq - if ;; label = @18 - block ;; label = @19 - local.get $4 - i32.const 16 - i32.add - local.tee $3 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.eqz - if ;; label = @20 - local.get $3 - i32.load - local.tee $0 - if ;; label = @21 - local.get $3 - local.set $1 - else - block ;; label = @22 - i32.const 0 - local.set $12 - br 5 (;@17;) - end - end - end - loop $label$239 ;; label = @20 - local.get $0 - i32.const 20 - i32.add - local.tee $5 - i32.load - local.tee $3 - if ;; label = @21 - block ;; label = @22 - local.get $3 - local.set $0 - local.get $5 - local.set $1 - br 2 (;@20;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $5 - i32.load - local.tee $3 - if ;; label = @21 - block ;; label = @22 - local.get $3 - local.set $0 - local.get $5 - local.set $1 - br 2 (;@20;) - end - end - end - local.get $1 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - else - block ;; label = @21 - local.get $1 - i32.const 0 - i32.store - local.get $0 - local.set $12 - end - end - end - else - block ;; label = @19 - local.get $4 - i32.load offset=8 - local.tee $5 - local.get $2 - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $5 - i32.const 12 - i32.add - local.tee $3 - i32.load - local.get $4 - i32.ne - if ;; label = @20 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - block ;; label = @21 - local.get $3 - local.get $0 - i32.store - local.get $1 - local.get $5 - i32.store - local.get $0 - local.set $12 - end - else - call $fimport$10 - end - end - end - end - local.get $8 - i32.eqz - br_if 2 (;@14;) - block $label$249 ;; label = @17 - local.get $4 - local.get $4 - i32.load offset=28 - local.tee $1 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - local.tee $0 - i32.load - i32.eq - if ;; label = @18 - block ;; label = @19 - local.get $0 - local.get $12 - i32.store - local.get $12 - br_if 2 (;@17;) - i32.const 3648 - i32.const 3648 - i32.load - i32.const 1 - local.get $1 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 5 (;@14;) - end - else - block ;; label = @19 - local.get $8 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @20 - call $fimport$10 - end - local.get $8 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $4 - i32.eq - if ;; label = @20 - local.get $0 - local.get $12 - i32.store - else - local.get $8 - local.get $12 - i32.store offset=20 - end - local.get $12 - i32.eqz - br_if 5 (;@14;) - end - end - end - local.get $12 - i32.const 3660 - i32.load - local.tee $1 - i32.lt_u - if ;; label = @17 - call $fimport$10 - end - local.get $12 - local.get $8 - i32.store offset=24 - local.get $4 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.tee $3 - if ;; label = @17 - local.get $3 - local.get $1 - i32.lt_u - if ;; label = @18 - call $fimport$10 - else - block ;; label = @19 - local.get $12 - local.get $3 - i32.store offset=16 - local.get $3 - local.get $12 - i32.store offset=24 - end - end - end - local.get $0 - i32.load offset=4 - local.tee $0 - i32.eqz - br_if 2 (;@14;) - local.get $0 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @17 - call $fimport$10 - else - block ;; label = @18 - local.get $12 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $12 - i32.store offset=24 - end - end - end - end - end - local.get $11 - local.get $7 - i32.add - local.set $7 - local.get $4 - local.get $11 - i32.add - end - else - local.get $4 - end - local.tee $0 - i32.const 4 - i32.add - local.tee $0 - local.get $0 - i32.load - i32.const -2 - i32.and - i32.store - local.get $6 - local.get $7 - i32.const 1 - i32.or - i32.store offset=4 - local.get $6 - local.get $7 - i32.add - local.get $7 - i32.store - local.get $7 - i32.const 3 - i32.shr_u - local.set $0 - local.get $7 - i32.const 256 - i32.lt_u - if ;; label = @12 - block ;; label = @13 - local.get $0 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.set $3 - block $label$263 ;; label = @14 - i32.const 3644 - i32.load - local.tee $1 - i32.const 1 - local.get $0 - i32.shl - local.tee $0 - i32.and - if ;; label = @15 - block ;; label = @16 - local.get $3 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 3660 - i32.load - i32.ge_u - if ;; label = @17 - block ;; label = @18 - local.get $1 - local.set $21 - local.get $0 - local.set $9 - br 4 (;@14;) - end - end - call $fimport$10 - end - else - block ;; label = @16 - i32.const 3644 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - i32.const 8 - i32.add - local.set $21 - local.get $3 - local.set $9 - end - end - end - local.get $21 - local.get $6 - i32.store - local.get $9 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $9 - i32.store offset=8 - local.get $6 - local.get $3 - i32.store offset=12 - br 4 (;@9;) - end - end - block $label$267 (result i32) ;; label = @12 - local.get $7 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @13 - block (result i32) ;; label = @14 - i32.const 31 - local.get $7 - i32.const 16777215 - i32.gt_u - br_if 2 (;@12;) - drop - local.get $7 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $3 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $0 - local.get $3 - i32.or - local.get $1 - local.get $0 - i32.shl - local.tee $1 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $0 - i32.or - i32.sub - local.get $1 - local.get $0 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - end - local.tee $2 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - local.set $3 - local.get $6 - local.get $2 - i32.store offset=28 - local.get $6 - i32.const 16 - i32.add - local.tee $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store - i32.const 3648 - i32.load - local.tee $1 - i32.const 1 - local.get $2 - i32.shl - local.tee $0 - i32.and - i32.eqz - if ;; label = @12 - block ;; label = @13 - i32.const 3648 - local.get $1 - local.get $0 - i32.or - i32.store - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $3 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 4 (;@9;) - end - end - local.get $3 - i32.load - local.set $0 - i32.const 25 - local.get $2 - i32.const 1 - i32.shr_u - i32.sub - local.set $1 - local.get $7 - local.get $2 - i32.const 31 - i32.eq - if (result i32) ;; label = @12 - i32.const 0 - else - local.get $1 - end - i32.shl - local.set $2 - block $label$273 ;; label = @12 - block $label$274 ;; label = @13 - block $label$275 ;; label = @14 - loop $label$276 ;; label = @15 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $7 - i32.eq - br_if 2 (;@13;) - local.get $2 - i32.const 1 - i32.shl - local.set $3 - local.get $0 - i32.const 16 - i32.add - local.get $2 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $2 - i32.load - local.tee $1 - i32.eqz - br_if 1 (;@14;) - local.get $3 - local.set $2 - local.get $1 - local.set $0 - br 0 (;@15;) - end - end - local.get $2 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @14 - call $fimport$10 - else - block ;; label = @15 - local.get $2 - local.get $6 - i32.store - local.get $6 - local.get $0 - i32.store offset=24 - local.get $6 - local.get $6 - i32.store offset=12 - local.get $6 - local.get $6 - i32.store offset=8 - br 6 (;@9;) - end - end - br 1 (;@12;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $2 - i32.const 3660 - i32.load - local.tee $1 - i32.ge_u - local.get $0 - local.get $1 - i32.ge_u - i32.and - if ;; label = @13 - block ;; label = @14 - local.get $2 - local.get $6 - i32.store offset=12 - local.get $3 - local.get $6 - i32.store - local.get $6 - local.get $2 - i32.store offset=8 - local.get $6 - local.get $0 - i32.store offset=12 - local.get $6 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - end - end - local.get $14 - global.set $global$1 - local.get $13 - i32.const 8 - i32.add - return - end - end - end - loop $label$281 ;; label = @6 - block $label$282 ;; label = @7 - local.get $5 - i32.load - local.tee $2 - local.get $8 - i32.le_u - if ;; label = @8 - local.get $2 - local.get $5 - i32.load offset=4 - i32.add - local.tee $13 - local.get $8 - i32.gt_u - br_if 1 (;@7;) - end - local.get $5 - i32.load offset=8 - local.set $5 - br 1 (;@6;) - end - end - i32.const 0 - local.get $13 - i32.const -47 - i32.add - local.tee $7 - i32.const 8 - i32.add - local.tee $5 - i32.sub - i32.const 7 - i32.and - local.set $2 - local.get $7 - local.get $5 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $2 - else - i32.const 0 - end - i32.add - local.tee $2 - local.get $8 - i32.const 16 - i32.add - local.tee $12 - i32.lt_u - if (result i32) ;; label = @6 - local.get $8 - else - local.get $2 - end - local.tee $7 - i32.const 8 - i32.add - local.set $10 - local.get $7 - i32.const 24 - i32.add - local.set $5 - local.get $3 - i32.const -40 - i32.add - local.set $9 - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $4 - i32.sub - i32.const 7 - i32.and - local.set $2 - i32.const 3668 - local.get $1 - local.get $4 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $2 - else - i32.const 0 - local.tee $2 - end - i32.add - local.tee $4 - i32.store - i32.const 3656 - local.get $9 - local.get $2 - i32.sub - local.tee $2 - i32.store - local.get $4 - local.get $2 - i32.const 1 - i32.or - i32.store offset=4 - local.get $4 - local.get $2 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 3672 - i32.const 4132 - i32.load - i32.store - local.get $7 - i32.const 4 - i32.add - local.tee $2 - i32.const 27 - i32.store - local.get $10 - i32.const 4092 - i64.load align=4 - i64.store align=4 - local.get $10 - i32.const 4100 - i64.load align=4 - i64.store offset=8 align=4 - i32.const 4092 - local.get $1 - i32.store - i32.const 4096 - local.get $3 - i32.store - i32.const 4104 - i32.const 0 - i32.store - i32.const 4100 - local.get $10 - i32.store - local.get $5 - local.set $1 - loop $label$290 ;; label = @6 - local.get $1 - i32.const 4 - i32.add - local.tee $1 - i32.const 7 - i32.store - local.get $1 - i32.const 4 - i32.add - local.get $13 - i32.lt_u - br_if 0 (;@6;) - end - local.get $7 - local.get $8 - i32.ne - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.get $2 - i32.load - i32.const -2 - i32.and - i32.store - local.get $8 - local.get $7 - local.get $8 - i32.sub - local.tee $4 - i32.const 1 - i32.or - i32.store offset=4 - local.get $7 - local.get $4 - i32.store - local.get $4 - i32.const 3 - i32.shr_u - local.set $1 - local.get $4 - i32.const 256 - i32.lt_u - if ;; label = @8 - block ;; label = @9 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.set $2 - i32.const 3644 - i32.load - local.tee $3 - i32.const 1 - local.get $1 - i32.shl - local.tee $1 - i32.and - if ;; label = @10 - local.get $2 - i32.const 8 - i32.add - local.tee $3 - i32.load - local.tee $1 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$10 - else - block ;; label = @12 - local.get $3 - local.set $15 - local.get $1 - local.set $11 - end - end - else - block ;; label = @11 - i32.const 3644 - local.get $3 - local.get $1 - i32.or - i32.store - local.get $2 - i32.const 8 - i32.add - local.set $15 - local.get $2 - local.set $11 - end - end - local.get $15 - local.get $8 - i32.store - local.get $11 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $11 - i32.store offset=8 - local.get $8 - local.get $2 - i32.store offset=12 - br 6 (;@3;) - end - end - local.get $4 - i32.const 8 - i32.shr_u - local.tee $1 - if (result i32) ;; label = @8 - local.get $4 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @9 - i32.const 31 - else - local.get $4 - i32.const 14 - local.get $1 - local.get $1 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $2 - i32.shl - local.tee $3 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $1 - local.get $2 - i32.or - local.get $3 - local.get $1 - i32.shl - local.tee $3 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $1 - i32.or - i32.sub - local.get $3 - local.get $1 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $1 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $1 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $5 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - local.set $2 - local.get $8 - local.get $5 - i32.store offset=28 - local.get $8 - i32.const 0 - i32.store offset=20 - local.get $12 - i32.const 0 - i32.store - i32.const 3648 - i32.load - local.tee $3 - i32.const 1 - local.get $5 - i32.shl - local.tee $1 - i32.and - i32.eqz - if ;; label = @8 - block ;; label = @9 - i32.const 3648 - local.get $3 - local.get $1 - i32.or - i32.store - local.get $2 - local.get $8 - i32.store - local.get $8 - local.get $2 - i32.store offset=24 - local.get $8 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $8 - i32.store offset=8 - br 6 (;@3;) - end - end - local.get $2 - i32.load - local.set $1 - i32.const 25 - local.get $5 - i32.const 1 - i32.shr_u - i32.sub - local.set $3 - local.get $4 - local.get $5 - i32.const 31 - i32.eq - if (result i32) ;; label = @8 - i32.const 0 - else - local.get $3 - end - i32.shl - local.set $5 - block $label$304 ;; label = @8 - block $label$305 ;; label = @9 - block $label$306 ;; label = @10 - loop $label$307 ;; label = @11 - local.get $1 - i32.load offset=4 - i32.const -8 - i32.and - local.get $4 - i32.eq - br_if 2 (;@9;) - local.get $5 - i32.const 1 - i32.shl - local.set $2 - local.get $1 - i32.const 16 - i32.add - local.get $5 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $5 - i32.load - local.tee $3 - i32.eqz - br_if 1 (;@10;) - local.get $2 - local.set $5 - local.get $3 - local.set $1 - br 0 (;@11;) - end - end - local.get $5 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $5 - local.get $8 - i32.store - local.get $8 - local.get $1 - i32.store offset=24 - local.get $8 - local.get $8 - i32.store offset=12 - local.get $8 - local.get $8 - i32.store offset=8 - br 8 (;@3;) - end - end - br 1 (;@8;) - end - local.get $1 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $5 - i32.const 3660 - i32.load - local.tee $3 - i32.ge_u - local.get $1 - local.get $3 - i32.ge_u - i32.and - if ;; label = @9 - block ;; label = @10 - local.get $5 - local.get $8 - i32.store offset=12 - local.get $2 - local.get $8 - i32.store - local.get $8 - local.get $5 - i32.store offset=8 - local.get $8 - local.get $1 - i32.store offset=12 - local.get $8 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - end - end - else - block ;; label = @5 - i32.const 3660 - i32.load - local.tee $2 - i32.eqz - local.get $1 - local.get $2 - i32.lt_u - i32.or - if ;; label = @6 - i32.const 3660 - local.get $1 - i32.store - end - i32.const 4092 - local.get $1 - i32.store - i32.const 4096 - local.get $3 - i32.store - i32.const 4104 - i32.const 0 - i32.store - i32.const 3680 - i32.const 4116 - i32.load - i32.store - i32.const 3676 - i32.const -1 - i32.store - i32.const 0 - local.set $2 - loop $label$314 ;; label = @6 - local.get $2 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.tee $5 - local.get $5 - i32.store offset=12 - local.get $5 - local.get $5 - i32.store offset=8 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - i32.const 32 - i32.ne - br_if 0 (;@6;) - end - local.get $3 - i32.const -40 - i32.add - local.set $5 - i32.const 0 - local.get $1 - i32.const 8 - i32.add - local.tee $2 - i32.sub - i32.const 7 - i32.and - local.set $3 - i32.const 3668 - local.get $1 - local.get $2 - i32.const 7 - i32.and - if (result i32) ;; label = @6 - local.get $3 - else - i32.const 0 - end - local.tee $1 - i32.add - local.tee $3 - i32.store - i32.const 3656 - local.get $5 - local.get $1 - i32.sub - local.tee $1 - i32.store - local.get $3 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $1 - i32.add - i32.const 40 - i32.store offset=4 - i32.const 3672 - i32.const 4132 - i32.load - i32.store - end - end - end - i32.const 3656 - i32.load - local.tee $1 - local.get $0 - i32.gt_u - if ;; label = @3 - block ;; label = @4 - i32.const 3656 - local.get $1 - local.get $0 - i32.sub - local.tee $3 - i32.store - i32.const 3668 - i32.const 3668 - i32.load - local.tee $2 - local.get $0 - i32.add - local.tee $1 - i32.store - local.get $1 - local.get $3 - i32.const 1 - i32.or - i32.store offset=4 - local.get $2 - local.get $0 - i32.const 3 - i32.or - i32.store offset=4 - local.get $14 - global.set $global$1 - local.get $2 - i32.const 8 - i32.add - return - end - end - end - call $11 - i32.const 12 - i32.store - local.get $14 - global.set $global$1 - i32.const 0 - end - ) - (func $32 (;45;) (type $2) (param $0 i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) - block $label$1 ;; label = @1 - local.get $0 - i32.eqz - if ;; label = @2 - return - end - local.get $0 - i32.const -8 - i32.add - local.tee $1 - i32.const 3660 - i32.load - local.tee $11 - i32.lt_u - if ;; label = @2 - call $fimport$10 - end - local.get $0 - i32.const -4 - i32.add - i32.load - local.tee $0 - i32.const 3 - i32.and - local.tee $8 - i32.const 1 - i32.eq - if ;; label = @2 - call $fimport$10 - end - local.get $1 - local.get $0 - i32.const -8 - i32.and - local.tee $4 - i32.add - local.set $6 - block $label$5 ;; label = @2 - local.get $0 - i32.const 1 - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $1 - local.set $3 - local.get $4 - local.set $2 - end - else - block ;; label = @4 - local.get $8 - i32.eqz - if ;; label = @5 - return - end - local.get $1 - i32.const 0 - local.get $1 - i32.load - local.tee $8 - i32.sub - i32.add - local.tee $0 - local.get $11 - i32.lt_u - if ;; label = @5 - call $fimport$10 - end - local.get $8 - local.get $4 - i32.add - local.set $1 - local.get $0 - i32.const 3664 - i32.load - i32.eq - if ;; label = @5 - block ;; label = @6 - local.get $6 - i32.const 4 - i32.add - local.tee $2 - i32.load - local.tee $3 - i32.const 3 - i32.and - i32.const 3 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 6 (;@2;) - end - end - i32.const 3652 - local.get $1 - i32.store - local.get $2 - local.get $3 - i32.const -2 - i32.and - i32.store - local.get $0 - local.get $1 - i32.const 1 - i32.or - i32.store offset=4 - local.get $0 - local.get $1 - i32.add - local.get $1 - i32.store - return - end - end - local.get $8 - i32.const 3 - i32.shr_u - local.set $10 - local.get $8 - i32.const 256 - i32.lt_u - if ;; label = @5 - block ;; label = @6 - local.get $0 - i32.load offset=12 - local.set $3 - local.get $0 - i32.load offset=8 - local.tee $4 - local.get $10 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.tee $2 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $4 - local.get $11 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $4 - i32.load offset=12 - local.get $0 - i32.ne - if ;; label = @9 - call $fimport$10 - end - end - end - local.get $3 - local.get $4 - i32.eq - if ;; label = @7 - block ;; label = @8 - i32.const 3644 - i32.const 3644 - i32.load - i32.const 1 - local.get $10 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 6 (;@2;) - end - end - local.get $3 - local.get $2 - i32.eq - if ;; label = @7 - local.get $3 - i32.const 8 - i32.add - local.set $5 - else - block ;; label = @8 - local.get $3 - local.get $11 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $3 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.get $0 - i32.eq - if ;; label = @9 - local.get $2 - local.set $5 - else - call $fimport$10 - end - end - end - local.get $4 - local.get $3 - i32.store offset=12 - local.get $5 - local.get $4 - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 4 (;@2;) - end - end - local.get $0 - i32.load offset=24 - local.set $12 - block $label$22 ;; label = @5 - local.get $0 - i32.load offset=12 - local.tee $4 - local.get $0 - i32.eq - if ;; label = @6 - block ;; label = @7 - local.get $0 - i32.const 16 - i32.add - local.tee $5 - i32.const 4 - i32.add - local.tee $8 - i32.load - local.tee $4 - if ;; label = @8 - local.get $8 - local.set $5 - else - local.get $5 - i32.load - local.tee $4 - i32.eqz - if ;; label = @9 - block ;; label = @10 - i32.const 0 - local.set $7 - br 5 (;@5;) - end - end - end - loop $label$27 ;; label = @8 - local.get $4 - i32.const 20 - i32.add - local.tee $8 - i32.load - local.tee $10 - if ;; label = @9 - block ;; label = @10 - local.get $10 - local.set $4 - local.get $8 - local.set $5 - br 2 (;@8;) - end - end - local.get $4 - i32.const 16 - i32.add - local.tee $8 - i32.load - local.tee $10 - if ;; label = @9 - block ;; label = @10 - local.get $10 - local.set $4 - local.get $8 - local.set $5 - br 2 (;@8;) - end - end - end - local.get $5 - local.get $11 - i32.lt_u - if ;; label = @8 - call $fimport$10 - else - block ;; label = @9 - local.get $5 - i32.const 0 - i32.store - local.get $4 - local.set $7 - end - end - end - else - block ;; label = @7 - local.get $0 - i32.load offset=8 - local.tee $5 - local.get $11 - i32.lt_u - if ;; label = @8 - call $fimport$10 - end - local.get $5 - i32.const 12 - i32.add - local.tee $8 - i32.load - local.get $0 - i32.ne - if ;; label = @8 - call $fimport$10 - end - local.get $4 - i32.const 8 - i32.add - local.tee $10 - i32.load - local.get $0 - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $8 - local.get $4 - i32.store - local.get $10 - local.get $5 - i32.store - local.get $4 - local.set $7 - end - else - call $fimport$10 - end - end - end - end - local.get $12 - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $0 - i32.load offset=28 - local.tee $4 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - local.tee $5 - i32.load - i32.eq - if ;; label = @7 - block ;; label = @8 - local.get $5 - local.get $7 - i32.store - local.get $7 - i32.eqz - if ;; label = @9 - block ;; label = @10 - i32.const 3648 - i32.const 3648 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 8 (;@2;) - end - end - end - else - block ;; label = @8 - local.get $12 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $12 - i32.const 16 - i32.add - local.tee $4 - i32.load - local.get $0 - i32.eq - if ;; label = @9 - local.get $4 - local.get $7 - i32.store - else - local.get $12 - local.get $7 - i32.store offset=20 - end - local.get $7 - i32.eqz - if ;; label = @9 - block ;; label = @10 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - br 8 (;@2;) - end - end - end - end - local.get $7 - i32.const 3660 - i32.load - local.tee $5 - i32.lt_u - if ;; label = @7 - call $fimport$10 - end - local.get $7 - local.get $12 - i32.store offset=24 - local.get $0 - i32.const 16 - i32.add - local.tee $8 - i32.load - local.tee $4 - if ;; label = @7 - local.get $4 - local.get $5 - i32.lt_u - if ;; label = @8 - call $fimport$10 - else - block ;; label = @9 - local.get $7 - local.get $4 - i32.store offset=16 - local.get $4 - local.get $7 - i32.store offset=24 - end - end - end - local.get $8 - i32.load offset=4 - local.tee $4 - if ;; label = @7 - local.get $4 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @8 - call $fimport$10 - else - block ;; label = @9 - local.get $7 - local.get $4 - i32.store offset=20 - local.get $4 - local.get $7 - i32.store offset=24 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - else - block ;; label = @8 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - end - else - block ;; label = @6 - local.get $0 - local.set $3 - local.get $1 - local.set $2 - end - end - end - end - end - local.get $3 - local.get $6 - i32.ge_u - if ;; label = @2 - call $fimport$10 - end - local.get $6 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - i32.const 1 - i32.and - i32.eqz - if ;; label = @2 - call $fimport$10 - end - local.get $0 - i32.const 2 - i32.and - if ;; label = @2 - block ;; label = @3 - local.get $1 - local.get $0 - i32.const -2 - i32.and - i32.store - local.get $3 - local.get $2 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $2 - i32.add - local.get $2 - i32.store - end - else - block ;; label = @3 - local.get $6 - i32.const 3668 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 3656 - i32.const 3656 - i32.load - local.get $2 - i32.add - local.tee $0 - i32.store - i32.const 3668 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - i32.const 3664 - i32.load - i32.ne - if ;; label = @6 - return - end - i32.const 3664 - i32.const 0 - i32.store - i32.const 3652 - i32.const 0 - i32.store - return - end - end - local.get $6 - i32.const 3664 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 3652 - i32.const 3652 - i32.load - local.get $2 - i32.add - local.tee $0 - i32.store - i32.const 3664 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $0 - i32.add - local.get $0 - i32.store - return - end - end - local.get $0 - i32.const -8 - i32.and - local.get $2 - i32.add - local.set $5 - local.get $0 - i32.const 3 - i32.shr_u - local.set $4 - block $label$61 ;; label = @4 - local.get $0 - i32.const 256 - i32.lt_u - if ;; label = @5 - block ;; label = @6 - local.get $6 - i32.load offset=12 - local.set $2 - local.get $6 - i32.load offset=8 - local.tee $1 - local.get $4 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.tee $0 - i32.ne - if ;; label = @7 - block ;; label = @8 - local.get $1 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $1 - i32.load offset=12 - local.get $6 - i32.ne - if ;; label = @9 - call $fimport$10 - end - end - end - local.get $2 - local.get $1 - i32.eq - if ;; label = @7 - block ;; label = @8 - i32.const 3644 - i32.const 3644 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 4 (;@4;) - end - end - local.get $2 - local.get $0 - i32.eq - if ;; label = @7 - local.get $2 - i32.const 8 - i32.add - local.set $14 - else - block ;; label = @8 - local.get $2 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $2 - i32.const 8 - i32.add - local.tee $0 - i32.load - local.get $6 - i32.eq - if ;; label = @9 - local.get $0 - local.set $14 - else - call $fimport$10 - end - end - end - local.get $1 - local.get $2 - i32.store offset=12 - local.get $14 - local.get $1 - i32.store - end - else - block ;; label = @6 - local.get $6 - i32.load offset=24 - local.set $7 - block $label$73 ;; label = @7 - local.get $6 - i32.load offset=12 - local.tee $0 - local.get $6 - i32.eq - if ;; label = @8 - block ;; label = @9 - local.get $6 - i32.const 16 - i32.add - local.tee $2 - i32.const 4 - i32.add - local.tee $1 - i32.load - local.tee $0 - if ;; label = @10 - local.get $1 - local.set $2 - else - local.get $2 - i32.load - local.tee $0 - i32.eqz - if ;; label = @11 - block ;; label = @12 - i32.const 0 - local.set $9 - br 5 (;@7;) - end - end - end - loop $label$78 ;; label = @10 - local.get $0 - i32.const 20 - i32.add - local.tee $1 - i32.load - local.tee $4 - if ;; label = @11 - block ;; label = @12 - local.get $4 - local.set $0 - local.get $1 - local.set $2 - br 2 (;@10;) - end - end - local.get $0 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $4 - if ;; label = @11 - block ;; label = @12 - local.get $4 - local.set $0 - local.get $1 - local.set $2 - br 2 (;@10;) - end - end - end - local.get $2 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $2 - i32.const 0 - i32.store - local.get $0 - local.set $9 - end - end - end - else - block ;; label = @9 - local.get $6 - i32.load offset=8 - local.tee $2 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - end - local.get $2 - i32.const 12 - i32.add - local.tee $1 - i32.load - local.get $6 - i32.ne - if ;; label = @10 - call $fimport$10 - end - local.get $0 - i32.const 8 - i32.add - local.tee $4 - i32.load - local.get $6 - i32.eq - if ;; label = @10 - block ;; label = @11 - local.get $1 - local.get $0 - i32.store - local.get $4 - local.get $2 - i32.store - local.get $0 - local.set $9 - end - else - call $fimport$10 - end - end - end - end - local.get $7 - if ;; label = @7 - block ;; label = @8 - local.get $6 - local.get $6 - i32.load offset=28 - local.tee $0 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - local.tee $2 - i32.load - i32.eq - if ;; label = @9 - block ;; label = @10 - local.get $2 - local.get $9 - i32.store - local.get $9 - i32.eqz - if ;; label = @11 - block ;; label = @12 - i32.const 3648 - i32.const 3648 - i32.load - i32.const 1 - local.get $0 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - br 8 (;@4;) - end - end - end - else - block ;; label = @10 - local.get $7 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @11 - call $fimport$10 - end - local.get $7 - i32.const 16 - i32.add - local.tee $0 - i32.load - local.get $6 - i32.eq - if ;; label = @11 - local.get $0 - local.get $9 - i32.store - else - local.get $7 - local.get $9 - i32.store offset=20 - end - local.get $9 - i32.eqz - br_if 6 (;@4;) - end - end - local.get $9 - i32.const 3660 - i32.load - local.tee $2 - i32.lt_u - if ;; label = @9 - call $fimport$10 - end - local.get $9 - local.get $7 - i32.store offset=24 - local.get $6 - i32.const 16 - i32.add - local.tee $1 - i32.load - local.tee $0 - if ;; label = @9 - local.get $0 - local.get $2 - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $9 - local.get $0 - i32.store offset=16 - local.get $0 - local.get $9 - i32.store offset=24 - end - end - end - local.get $1 - i32.load offset=4 - local.tee $0 - if ;; label = @9 - local.get $0 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @10 - call $fimport$10 - else - block ;; label = @11 - local.get $9 - local.get $0 - i32.store offset=20 - local.get $0 - local.get $9 - i32.store offset=24 - end - end - end - end - end - end - end - end - local.get $3 - local.get $5 - i32.const 1 - i32.or - i32.store offset=4 - local.get $3 - local.get $5 - i32.add - local.get $5 - i32.store - local.get $3 - i32.const 3664 - i32.load - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 3652 - local.get $5 - i32.store - return - end - else - local.get $5 - local.set $2 - end - end - end - local.get $2 - i32.const 3 - i32.shr_u - local.set $1 - local.get $2 - i32.const 256 - i32.lt_u - if ;; label = @2 - block ;; label = @3 - local.get $1 - i32.const 1 - i32.shl - i32.const 2 - i32.shl - i32.const 3684 - i32.add - local.set $0 - i32.const 3644 - i32.load - local.tee $2 - i32.const 1 - local.get $1 - i32.shl - local.tee $1 - i32.and - if ;; label = @4 - local.get $0 - i32.const 8 - i32.add - local.tee $2 - i32.load - local.tee $1 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @5 - call $fimport$10 - else - block ;; label = @6 - local.get $2 - local.set $15 - local.get $1 - local.set $13 - end - end - else - block ;; label = @5 - i32.const 3644 - local.get $2 - local.get $1 - i32.or - i32.store - local.get $0 - i32.const 8 - i32.add - local.set $15 - local.get $0 - local.set $13 - end - end - local.get $15 - local.get $3 - i32.store - local.get $13 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $13 - i32.store offset=8 - local.get $3 - local.get $0 - i32.store offset=12 - return - end - end - local.get $2 - i32.const 8 - i32.shr_u - local.tee $0 - if (result i32) ;; label = @2 - local.get $2 - i32.const 16777215 - i32.gt_u - if (result i32) ;; label = @3 - i32.const 31 - else - local.get $2 - i32.const 14 - local.get $0 - local.get $0 - i32.const 1048320 - i32.add - i32.const 16 - i32.shr_u - i32.const 8 - i32.and - local.tee $0 - i32.shl - local.tee $1 - i32.const 520192 - i32.add - i32.const 16 - i32.shr_u - i32.const 4 - i32.and - local.tee $4 - local.get $0 - i32.or - local.get $1 - local.get $4 - i32.shl - local.tee $0 - i32.const 245760 - i32.add - i32.const 16 - i32.shr_u - i32.const 2 - i32.and - local.tee $1 - i32.or - i32.sub - local.get $0 - local.get $1 - i32.shl - i32.const 15 - i32.shr_u - i32.add - local.tee $0 - i32.const 7 - i32.add - i32.shr_u - i32.const 1 - i32.and - local.get $0 - i32.const 1 - i32.shl - i32.or - end - else - i32.const 0 - end - local.tee $1 - i32.const 2 - i32.shl - i32.const 3948 - i32.add - local.set $0 - local.get $3 - local.get $1 - i32.store offset=28 - local.get $3 - i32.const 0 - i32.store offset=20 - local.get $3 - i32.const 0 - i32.store offset=16 - block $label$113 ;; label = @2 - i32.const 3648 - i32.load - local.tee $4 - i32.const 1 - local.get $1 - i32.shl - local.tee $5 - i32.and - if ;; label = @3 - block ;; label = @4 - local.get $0 - i32.load - local.set $0 - i32.const 25 - local.get $1 - i32.const 1 - i32.shr_u - i32.sub - local.set $4 - local.get $2 - local.get $1 - i32.const 31 - i32.eq - if (result i32) ;; label = @5 - i32.const 0 - else - local.get $4 - end - i32.shl - local.set $1 - block $label$117 ;; label = @5 - block $label$118 ;; label = @6 - block $label$119 ;; label = @7 - loop $label$120 ;; label = @8 - local.get $0 - i32.load offset=4 - i32.const -8 - i32.and - local.get $2 - i32.eq - br_if 2 (;@6;) - local.get $1 - i32.const 1 - i32.shl - local.set $4 - local.get $0 - i32.const 16 - i32.add - local.get $1 - i32.const 31 - i32.shr_u - i32.const 2 - i32.shl - i32.add - local.tee $1 - i32.load - local.tee $5 - i32.eqz - br_if 1 (;@7;) - local.get $4 - local.set $1 - local.get $5 - local.set $0 - br 0 (;@8;) - end - end - local.get $1 - i32.const 3660 - i32.load - i32.lt_u - if ;; label = @7 - call $fimport$10 - else - block ;; label = @8 - local.get $1 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.store offset=24 - local.get $3 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $3 - i32.store offset=8 - br 6 (;@2;) - end - end - br 1 (;@5;) - end - local.get $0 - i32.const 8 - i32.add - local.tee $1 - i32.load - local.tee $2 - i32.const 3660 - i32.load - local.tee $4 - i32.ge_u - local.get $0 - local.get $4 - i32.ge_u - i32.and - if ;; label = @6 - block ;; label = @7 - local.get $2 - local.get $3 - i32.store offset=12 - local.get $1 - local.get $3 - i32.store - local.get $3 - local.get $2 - i32.store offset=8 - local.get $3 - local.get $0 - i32.store offset=12 - local.get $3 - i32.const 0 - i32.store offset=24 - end - else - call $fimport$10 - end - end - end - else - block ;; label = @4 - i32.const 3648 - local.get $4 - local.get $5 - i32.or - i32.store - local.get $0 - local.get $3 - i32.store - local.get $3 - local.get $0 - i32.store offset=24 - local.get $3 - local.get $3 - i32.store offset=12 - local.get $3 - local.get $3 - i32.store offset=8 - end - end - end - i32.const 3676 - i32.const 3676 - i32.load - i32.const -1 - i32.add - local.tee $0 - i32.store - local.get $0 - if ;; label = @2 - return - else - i32.const 4100 - local.set $0 - end - loop $label$128 ;; label = @2 - local.get $0 - i32.load - local.tee $2 - i32.const 8 - i32.add - local.set $0 - local.get $2 - br_if 0 (;@2;) - end - i32.const 3676 - i32.const -1 - i32.store - end - ) - (func $33 (;46;) (type $6) - nop - ) - (func $34 (;47;) (type $1) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) - block $label$1 (result i32) ;; label = @1 - global.get $global$0 - i32.load - local.tee $2 - local.get $0 - i32.const 15 - i32.add - i32.const -16 - i32.and - local.tee $0 - i32.add - local.set $1 - local.get $0 - i32.const 0 - i32.gt_s - local.get $1 - local.get $2 - i32.lt_s - i32.and - local.get $1 - i32.const 0 - i32.lt_s - i32.or - if ;; label = @2 - block ;; label = @3 - call $fimport$6 - drop - i32.const 12 - call $fimport$11 - i32.const -1 - return - end - end - global.get $global$0 - local.get $1 - i32.store - local.get $1 - call $fimport$5 - i32.gt_s - if ;; label = @2 - call $fimport$4 - i32.eqz - if ;; label = @3 - block ;; label = @4 - i32.const 12 - call $fimport$11 - global.get $global$0 - local.get $2 - i32.store - i32.const -1 - return - end - end - end - local.get $2 - end - ) - (func $35 (;48;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (local $4 i32) (local $5 i32) - block $label$1 (result i32) ;; label = @1 - local.get $0 - local.get $2 - i32.add - local.set $4 - local.get $2 - i32.const 20 - i32.ge_s - if ;; label = @2 - block ;; label = @3 - local.get $1 - i32.const 255 - i32.and - local.set $1 - local.get $0 - i32.const 3 - i32.and - local.tee $3 - if ;; label = @4 - block ;; label = @5 - local.get $0 - i32.const 4 - i32.add - local.get $3 - i32.sub - local.set $3 - loop $label$4 ;; label = @6 - local.get $0 - local.get $3 - i32.lt_s - if ;; label = @7 - block ;; label = @8 - local.get $0 - local.get $1 - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - br 2 (;@6;) - end - end - end - end - end - local.get $1 - local.get $1 - i32.const 8 - i32.shl - i32.or - local.get $1 - i32.const 16 - i32.shl - i32.or - local.get $1 - i32.const 24 - i32.shl - i32.or - local.set $3 - local.get $4 - i32.const -4 - i32.and - local.set $5 - loop $label$6 ;; label = @4 - local.get $0 - local.get $5 - i32.lt_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $3 - i32.store - local.get $0 - i32.const 4 - i32.add - local.set $0 - br 2 (;@4;) - end - end - end - end - end - loop $label$8 ;; label = @2 - local.get $0 - local.get $4 - i32.lt_s - if ;; label = @3 - block ;; label = @4 - local.get $0 - local.get $1 - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - br 2 (;@2;) - end - end - end - local.get $0 - local.get $2 - i32.sub - end - ) - (func $36 (;49;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - block $label$1 (result i32) ;; label = @1 - local.get $2 - i32.const 4096 - i32.ge_s - if ;; label = @2 - local.get $0 - local.get $1 - local.get $2 - call $fimport$12 - return - end - local.get $0 - local.set $3 - local.get $0 - i32.const 3 - i32.and - local.get $1 - i32.const 3 - i32.and - i32.eq - if ;; label = @2 - block ;; label = @3 - loop $label$4 ;; label = @4 - local.get $0 - i32.const 3 - i32.and - if ;; label = @5 - block ;; label = @6 - local.get $2 - i32.eqz - if ;; label = @7 - local.get $3 - return - end - local.get $0 - local.get $1 - i32.load8_s - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - br 2 (;@4;) - end - end - end - loop $label$7 ;; label = @4 - local.get $2 - i32.const 4 - i32.ge_s - if ;; label = @5 - block ;; label = @6 - local.get $0 - local.get $1 - i32.load - i32.store - local.get $0 - i32.const 4 - i32.add - local.set $0 - local.get $1 - i32.const 4 - i32.add - local.set $1 - local.get $2 - i32.const 4 - i32.sub - local.set $2 - br 2 (;@4;) - end - end - end - end - end - loop $label$9 ;; label = @2 - local.get $2 - i32.const 0 - i32.gt_s - if ;; label = @3 - block ;; label = @4 - local.get $0 - local.get $1 - i32.load8_s - i32.store8 - local.get $0 - i32.const 1 - i32.add - local.set $0 - local.get $1 - i32.const 1 - i32.add - local.set $1 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - br 2 (;@2;) - end - end - end - local.get $3 - end - ) - (func $37 (;50;) (type $3) (result i32) - i32.const 0 - ) - (func $38 (;51;) (type $4) (param $0 i32) (param $1 i32) (result i32) - local.get $1 - local.get $0 - i32.const 1 - i32.and - i32.const 0 - i32.add - call_indirect (type $1) - ) - (func $39 (;52;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - local.get $1 - local.get $2 - local.get $3 - local.get $0 - i32.const 3 - i32.and - i32.const 2 - i32.add - call_indirect (type $0) - ) - (func $40 (;53;) (type $5) (param $0 i32) (param $1 i32) - local.get $1 - local.get $0 - i32.const 1 - i32.and - i32.const 6 - i32.add - call_indirect (type $2) - ) - (func $41 (;54;) (type $1) (param $0 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - i32.const 0 - call $fimport$3 - i32.const 0 - end - ) - (func $42 (;55;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - block $label$1 (result i32) ;; label = @1 - i32.const 1 - call $fimport$3 - i32.const 0 - end - ) - (func $43 (;56;) (type $2) (param $0 i32) - i32.const 2 - call $fimport$3 - ) - (global $global$0 (;5;) (mut i32) global.get $gimport$0) - (global $global$1 (;6;) (mut i32) global.get $gimport$1) - (global $global$2 (;7;) (mut i32) global.get $gimport$2) - (global $global$3 (;8;) (mut i32) i32.const 0) - (global $global$4 (;9;) (mut i32) i32.const 0) - (global $global$5 (;10;) (mut i32) i32.const 0) - (export "_sbrk" (func $34)) - (export "_free" (func $32)) - (export "_main" (func $7)) - (export "_pthread_self" (func $37)) - (export "_memset" (func $35)) - (export "_malloc" (func $31)) - (export "_memcpy" (func $36)) - (export "___errno_location" (func $11)) - (export "runPostSets" (func $33)) - (export "stackAlloc" (func $0)) - (export "stackSave" (func $1)) - (export "stackRestore" (func $2)) - (export "establishStackSpace" (func $3)) - (export "setThrew" (func $4)) - (export "setTempRet0" (func $5)) - (export "getTempRet0" (func $6)) - (export "dynCall_ii" (func $38)) - (export "dynCall_iiii" (func $39)) - (export "dynCall_vi" (func $40)) - (elem (;0;) (global.get $gimport$19) func $41 $8 $42 $13 $9 $14 $43 $15) - (data (;0;) (i32.const 1024) "\04\04\00\00\05") - (data (;1;) (i32.const 1040) "\01") - (data (;2;) (i32.const 1064) "\01\00\00\00\02\00\00\004\10\00\00\00\04") - (data (;3;) (i32.const 1088) "\01") - (data (;4;) (i32.const 1103) "\0a\ff\ff\ff\ff") - (data (;5;) (i32.const 1140) "error: %d\5cn\00lastprime: %d.\0a\00\11\00\0a\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\09\00\00\00\00\0b") - (data (;6;) (i32.const 1200) "\11\00\0f\0a\11\11\11\03\0a\07\00\01\13\09\0b\0b\00\00\09\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") - (data (;7;) (i32.const 1249) "\0b") - (data (;8;) (i32.const 1258) "\11\00\0a\0a\11\11\11\00\0a\00\00\02\00\09\0b\00\00\00\09\00\0b\00\00\0b") - (data (;9;) (i32.const 1307) "\0c") - (data (;10;) (i32.const 1319) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c") - (data (;11;) (i32.const 1365) "\0e") - (data (;12;) (i32.const 1377) "\0d\00\00\00\04\0d\00\00\00\00\09\0e\00\00\00\00\00\0e\00\00\0e") - (data (;13;) (i32.const 1423) "\10") - (data (;14;) (i32.const 1435) "\0f\00\00\00\00\0f\00\00\00\00\09\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") - (data (;15;) (i32.const 1490) "\12\00\00\00\12\12\12\00\00\00\00\00\00\09") - (data (;16;) (i32.const 1539) "\0b") - (data (;17;) (i32.const 1551) "\0a\00\00\00\00\0a\00\00\00\00\09\0b\00\00\00\00\00\0b\00\00\0b") - (data (;18;) (i32.const 1597) "\0c") - (data (;19;) (i32.const 1609) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\22\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\09\0a\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\5c]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") -) \ No newline at end of file diff --git a/cranelift/wasm/wasmtests/fac-multi-value.wat b/cranelift/wasm/wasmtests/fac-multi-value.wat deleted file mode 100644 index ad5e3d9a04c7..000000000000 --- a/cranelift/wasm/wasmtests/fac-multi-value.wat +++ /dev/null @@ -1,19 +0,0 @@ -(module - ;; Iterative factorial without locals. - (func $pick0 (param i64) (result i64 i64) - (local.get 0) (local.get 0) - ) - (func $pick1 (param i64 i64) (result i64 i64 i64) - (local.get 0) (local.get 1) (local.get 0) - ) - (func (export "fac-ssa") (param i64) (result i64) - (i64.const 1) (local.get 0) - (loop $l (param i64 i64) (result i64) - (call $pick1) (call $pick1) (i64.mul) - (call $pick1) (i64.const 1) (i64.sub) - (call $pick0) (i64.const 0) (i64.gt_u) - (br_if $l) - (drop) (return) - ) - ) -) diff --git a/cranelift/wasm/wasmtests/fibonacci.wat b/cranelift/wasm/wasmtests/fibonacci.wat deleted file mode 100644 index a40b2c91e442..000000000000 --- a/cranelift/wasm/wasmtests/fibonacci.wat +++ /dev/null @@ -1,22 +0,0 @@ -(module - (memory 1) - (func $main (local i32 i32 i32 i32) - (local.set 0 (i32.const 0)) - (local.set 1 (i32.const 1)) - (local.set 2 (i32.const 1)) - (local.set 3 (i32.const 0)) - (block - (loop - (br_if 1 (i32.gt_s (local.get 0) (i32.const 5))) - (local.set 3 (local.get 2)) - (local.set 2 (i32.add (local.get 2) (local.get 1))) - (local.set 1 (local.get 3)) - (local.set 0 (i32.add (local.get 0) (i32.const 1))) - (br 0) - ) - ) - (i32.store (i32.const 0) (local.get 2)) - ) - (start $main) - (data (i32.const 0) "0000") -) diff --git a/cranelift/wasm/wasmtests/globals.wat b/cranelift/wasm/wasmtests/globals.wat deleted file mode 100644 index fb9a591172bc..000000000000 --- a/cranelift/wasm/wasmtests/globals.wat +++ /dev/null @@ -1,8 +0,0 @@ -(module - (global $x (mut i32) (i32.const 4)) - (memory 1) - (func $main (local i32) - (i32.store (i32.const 0) (global.get $x)) - ) - (start $main) -) diff --git a/cranelift/wasm/wasmtests/icall-simd.wat b/cranelift/wasm/wasmtests/icall-simd.wat deleted file mode 100644 index d656b265b96a..000000000000 --- a/cranelift/wasm/wasmtests/icall-simd.wat +++ /dev/null @@ -1,7 +0,0 @@ -(module - (type $ft (func (param v128) (result v128))) - (func $foo (export "foo") (param i32) (param v128) (result v128) - (call_indirect (type $ft) (local.get 1) (local.get 0)) - ) - (table (;0;) 23 23 anyfunc) -) diff --git a/cranelift/wasm/wasmtests/icall.wat b/cranelift/wasm/wasmtests/icall.wat deleted file mode 100644 index f1dde0eafea5..000000000000 --- a/cranelift/wasm/wasmtests/icall.wat +++ /dev/null @@ -1,7 +0,0 @@ -(module - (type $ft (func (param f32) (result i32))) - (func $foo (export "foo") (param i32 f32) (result i32) - (call_indirect (type $ft) (local.get 1) (local.get 0)) - ) - (table (;0;) 23 23 anyfunc) -) diff --git a/cranelift/wasm/wasmtests/if-reachability-translation-0.wat b/cranelift/wasm/wasmtests/if-reachability-translation-0.wat deleted file mode 100644 index 54145a9d4eb7..000000000000 --- a/cranelift/wasm/wasmtests/if-reachability-translation-0.wat +++ /dev/null @@ -1,12 +0,0 @@ -;; An unreachable `if` means that the consequent, alternative, and following -;; block are also unreachable. - -(module - (func (param i32) (result i32) - unreachable - if ;; label = @2 - nop - else - nop - end - i32.const 0)) diff --git a/cranelift/wasm/wasmtests/if-reachability-translation-1.wat b/cranelift/wasm/wasmtests/if-reachability-translation-1.wat deleted file mode 100644 index 6e1e6121f45e..000000000000 --- a/cranelift/wasm/wasmtests/if-reachability-translation-1.wat +++ /dev/null @@ -1,12 +0,0 @@ -;; Reachable `if` head and reachable consequent and alternative means that the -;; following block is also reachable. - -(module - (func (param i32) (result i32) - local.get 0 - if ;; label = @2 - nop - else - nop - end - i32.const 0)) diff --git a/cranelift/wasm/wasmtests/if-reachability-translation-2.wat b/cranelift/wasm/wasmtests/if-reachability-translation-2.wat deleted file mode 100644 index 4bbaf99820b0..000000000000 --- a/cranelift/wasm/wasmtests/if-reachability-translation-2.wat +++ /dev/null @@ -1,12 +0,0 @@ -;; Reachable `if` head and unreachable consequent and reachable alternative -;; means that the following block is also reachable. - -(module - (func (param i32) (result i32) - local.get 0 - if - unreachable - else - nop - end - i32.const 0)) diff --git a/cranelift/wasm/wasmtests/if-reachability-translation-3.wat b/cranelift/wasm/wasmtests/if-reachability-translation-3.wat deleted file mode 100644 index 72251cba1674..000000000000 --- a/cranelift/wasm/wasmtests/if-reachability-translation-3.wat +++ /dev/null @@ -1,12 +0,0 @@ -;; Reachable `if` head and consequent and unreachable alternative means that the -;; following block is also reachable. - -(module - (func (param i32) (result i32) - local.get 0 - if - nop - else - unreachable - end - i32.const 0)) diff --git a/cranelift/wasm/wasmtests/if-reachability-translation-4.wat b/cranelift/wasm/wasmtests/if-reachability-translation-4.wat deleted file mode 100644 index b8a406943077..000000000000 --- a/cranelift/wasm/wasmtests/if-reachability-translation-4.wat +++ /dev/null @@ -1,12 +0,0 @@ -;; Reachable `if` head and unreachable consequent and alternative means that the -;; following block is unreachable. - -(module - (func (param i32) (result i32) - local.get 0 - if - unreachable - else - unreachable - end - i32.const 0)) diff --git a/cranelift/wasm/wasmtests/if-reachability-translation-5.wat b/cranelift/wasm/wasmtests/if-reachability-translation-5.wat deleted file mode 100644 index 7b1f665e056d..000000000000 --- a/cranelift/wasm/wasmtests/if-reachability-translation-5.wat +++ /dev/null @@ -1,14 +0,0 @@ -;; Reachable `if` head and unreachable consequent and alternative, but with a -;; branch out of the consequent, means that the following block is reachable. - -(module - (func (param i32 i32) (result i32) - local.get 0 - if - local.get 1 - br_if 0 - unreachable - else - unreachable - end - i32.const 0)) diff --git a/cranelift/wasm/wasmtests/if-reachability-translation-6.wat b/cranelift/wasm/wasmtests/if-reachability-translation-6.wat deleted file mode 100644 index d9da824f14c4..000000000000 --- a/cranelift/wasm/wasmtests/if-reachability-translation-6.wat +++ /dev/null @@ -1,14 +0,0 @@ -;; Reachable `if` head and unreachable consequent and alternative, but with a -;; branch out of the alternative, means that the following block is reachable. - -(module - (func (param i32 i32) (result i32) - local.get 0 - if - unreachable - else - local.get 1 - br_if 0 - unreachable - end - i32.const 0)) diff --git a/cranelift/wasm/wasmtests/if-unreachable-else-params-2.wat b/cranelift/wasm/wasmtests/if-unreachable-else-params-2.wat deleted file mode 100644 index 369ea4db6524..000000000000 --- a/cranelift/wasm/wasmtests/if-unreachable-else-params-2.wat +++ /dev/null @@ -1,18 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result f64))) - (func $main (type 0) (param i32 i32) (result f64) - f64.const 1.0 - local.get 0 - local.get 1 - if (param i32) ;; label = @2 - i64.load16_s align=1 - drop - else - unreachable - end) - (table (;0;) 63 255 funcref) - (memory (;0;) 13 16) - (export "t1" (table 0)) - (export "m1" (memory 0)) - (export "main" (func $main)) - (export "memory" (memory 0))) diff --git a/cranelift/wasm/wasmtests/if-unreachable-else-params.wat b/cranelift/wasm/wasmtests/if-unreachable-else-params.wat deleted file mode 100644 index 02e2d50f9510..000000000000 --- a/cranelift/wasm/wasmtests/if-unreachable-else-params.wat +++ /dev/null @@ -1,41 +0,0 @@ -(module - (type (;0;) (func (param i32))) - (func $main (type 0) (param i32) - i32.const 35 - loop (param i32) ;; label = @1 - local.get 0 - if (param i32) ;; label = @2 - i64.load16_s align=1 - unreachable - unreachable - unreachable - unreachable - unreachable - local.get 0 - unreachable - unreachable - i64.load8_u offset=11789 - unreachable - else - i32.popcnt - local.set 0 - return - unreachable - end - unreachable - unreachable - nop - f32.lt - i32.store8 offset=82 - unreachable - end - unreachable - unreachable - unreachable - unreachable) - (table (;0;) 63 255 funcref) - (memory (;0;) 13 16) - (export "t1" (table 0)) - (export "m1" (memory 0)) - (export "main" (func $main)) - (export "memory" (memory 0))) diff --git a/cranelift/wasm/wasmtests/issue-1306-name-section-with-u32-max-function-index.wasm b/cranelift/wasm/wasmtests/issue-1306-name-section-with-u32-max-function-index.wasm deleted file mode 100644 index 79d8dc33881d..000000000000 Binary files a/cranelift/wasm/wasmtests/issue-1306-name-section-with-u32-max-function-index.wasm and /dev/null differ diff --git a/cranelift/wasm/wasmtests/memory.wat b/cranelift/wasm/wasmtests/memory.wat deleted file mode 100644 index 0c81bad174de..000000000000 --- a/cranelift/wasm/wasmtests/memory.wat +++ /dev/null @@ -1,11 +0,0 @@ -(module - (memory 1) - (func $main (local i32) - (i32.store (i32.const 0) (i32.const 0x0)) - (if (i32.load (i32.const 0)) - (then (i32.store (i32.const 0) (i32.const 0xa))) - (else (i32.store (i32.const 0) (i32.const 0xb)))) - ) - (start $main) - (data (i32.const 0) "0000") -) diff --git a/cranelift/wasm/wasmtests/multi-0.wat b/cranelift/wasm/wasmtests/multi-0.wat deleted file mode 100644 index 806493918c30..000000000000 --- a/cranelift/wasm/wasmtests/multi-0.wat +++ /dev/null @@ -1,3 +0,0 @@ -(module - (func (export "i64.dup") (param i64) (result i64 i64) - (local.get 0) (local.get 0))) diff --git a/cranelift/wasm/wasmtests/multi-1.wat b/cranelift/wasm/wasmtests/multi-1.wat deleted file mode 100644 index a81464741938..000000000000 --- a/cranelift/wasm/wasmtests/multi-1.wat +++ /dev/null @@ -1,6 +0,0 @@ -(module - (func (export "multiBlock") (param i64 i32) (result i32 i64 f64) - (local.get 1) - (local.get 0) - (block (param i32 i64) (result i32 i64 f64) - (f64.const 1234.5)))) diff --git a/cranelift/wasm/wasmtests/multi-10.wat b/cranelift/wasm/wasmtests/multi-10.wat deleted file mode 100644 index 01fbf4294152..000000000000 --- a/cranelift/wasm/wasmtests/multi-10.wat +++ /dev/null @@ -1,10 +0,0 @@ -(module - (func (export "f") (param i64 i32) (result i64 i64) - (local.get 0) - (local.get 1) - ;; If with else. Fewer params than results. - (if (param i64) (result i64 i64) - (then - (i64.const -1)) - (else - (i64.const -2))))) diff --git a/cranelift/wasm/wasmtests/multi-11.wat b/cranelift/wasm/wasmtests/multi-11.wat deleted file mode 100644 index 1ae75889bc46..000000000000 --- a/cranelift/wasm/wasmtests/multi-11.wat +++ /dev/null @@ -1,7 +0,0 @@ -(module - (func (export "multiLoop") (param i64) (result i64 i64) - (local.get 0) - ;; Fewer params than results. - (loop (param i64) (result i64 i64) - i64.const 42 - return))) diff --git a/cranelift/wasm/wasmtests/multi-12.wat b/cranelift/wasm/wasmtests/multi-12.wat deleted file mode 100644 index 9a3e4f7fb54c..000000000000 --- a/cranelift/wasm/wasmtests/multi-12.wat +++ /dev/null @@ -1,9 +0,0 @@ -(module - (func (export "multiLoop") (param i64 i64 i64) (result i64 i64) - (local.get 2) - (local.get 1) - (local.get 0) - ;; More params than results. - (loop (param i64 i64 i64) (result i64 i64) - drop - return))) diff --git a/cranelift/wasm/wasmtests/multi-13.wat b/cranelift/wasm/wasmtests/multi-13.wat deleted file mode 100644 index 4f4846300eca..000000000000 --- a/cranelift/wasm/wasmtests/multi-13.wat +++ /dev/null @@ -1,10 +0,0 @@ -(module - (func (export "as-if-then") (param i32 i32) (result i32) - (block (result i32) - (if (result i32) (local.get 0) - (then (br 1 (i32.const 3))) - (else (local.get 1)) - ) - ) - ) -) diff --git a/cranelift/wasm/wasmtests/multi-14.wat b/cranelift/wasm/wasmtests/multi-14.wat deleted file mode 100644 index 26d0cb596a5b..000000000000 --- a/cranelift/wasm/wasmtests/multi-14.wat +++ /dev/null @@ -1,10 +0,0 @@ -(module - (func (export "as-if-else") (param i32 i32) (result i32) - (block (result i32) - (if (result i32) (local.get 0) - (then (local.get 1)) - (else (br 1 (i32.const 4))) - ) - ) - ) -) diff --git a/cranelift/wasm/wasmtests/multi-15.wat b/cranelift/wasm/wasmtests/multi-15.wat deleted file mode 100644 index 2f017bd6aab2..000000000000 --- a/cranelift/wasm/wasmtests/multi-15.wat +++ /dev/null @@ -1,22 +0,0 @@ -(module - (func (export "large-sig") - (param i32 i64 f32 f32 i32 f64 f32 i32 i32 i32 f32 f64 f64 f64 i32 i32 f32) - (result f64 f32 i32 i32 i32 i64 f32 i32 i32 f32 f64 f64 i32 f32 i32 f64) - (local.get 5) - (local.get 2) - (local.get 0) - (local.get 8) - (local.get 7) - (local.get 1) - (local.get 3) - (local.get 9) - (local.get 4) - (local.get 6) - (local.get 13) - (local.get 11) - (local.get 15) - (local.get 16) - (local.get 14) - (local.get 12) - ) -) diff --git a/cranelift/wasm/wasmtests/multi-16.wat b/cranelift/wasm/wasmtests/multi-16.wat deleted file mode 100644 index 1e60aa8cc81a..000000000000 --- a/cranelift/wasm/wasmtests/multi-16.wat +++ /dev/null @@ -1,9 +0,0 @@ -(module - (func (export "param") (param i32) (result i32) - (i32.const 1) - (if (param i32) (result i32) (local.get 0) - (then (i32.const 2) (i32.add)) - (else (i32.const -2) (i32.add)) - ) - ) -) diff --git a/cranelift/wasm/wasmtests/multi-17.wat b/cranelift/wasm/wasmtests/multi-17.wat deleted file mode 100644 index e190851e85f3..000000000000 --- a/cranelift/wasm/wasmtests/multi-17.wat +++ /dev/null @@ -1,26 +0,0 @@ -(module - (func $main (type 0) (param i32 i32 i32) (result i32) - i32.const 0 - i32.const 0 - i32.const 0 - i32.const 0 - - i32.const 0 - if (param i32 i32 i32) (result i32) ;; label = @1 - br 0 (;@1;) - else - call $main - end - - i32.const 0 - - i32.const 0 - if (param i32 i32 i32) (result i32) ;; label = @1 - drop - drop - else - drop - drop - end - ) - (export "main" (func $main))) diff --git a/cranelift/wasm/wasmtests/multi-2.wat b/cranelift/wasm/wasmtests/multi-2.wat deleted file mode 100644 index 6f2a7378b101..000000000000 --- a/cranelift/wasm/wasmtests/multi-2.wat +++ /dev/null @@ -1,6 +0,0 @@ -(module - (func (export "multiLoop") (param i64 i64) (result i64 i64) - (local.get 1) - (local.get 0) - (loop (param i64 i64) (result i64 i64) - return))) diff --git a/cranelift/wasm/wasmtests/multi-3.wat b/cranelift/wasm/wasmtests/multi-3.wat deleted file mode 100644 index d58071f9c742..000000000000 --- a/cranelift/wasm/wasmtests/multi-3.wat +++ /dev/null @@ -1,13 +0,0 @@ -(module - (func (export "multiIf") (param i32 i64 i64) (result i64 i64) - (local.get 2) - (local.get 1) - (local.get 0) - (if (param i64 i64) (result i64 i64) - (then return) - ;; Hits the code path for an `else` after a block that ends unreachable. - (else - (drop) - (drop) - (i64.const 0) - (i64.const 0))))) diff --git a/cranelift/wasm/wasmtests/multi-4.wat b/cranelift/wasm/wasmtests/multi-4.wat deleted file mode 100644 index 9c028531d31b..000000000000 --- a/cranelift/wasm/wasmtests/multi-4.wat +++ /dev/null @@ -1,13 +0,0 @@ -(module - (func (export "multiIf2") (param i32 i64 i64) (result i64 i64) - (local.get 2) - (local.get 1) - (local.get 0) - (if (param i64 i64) (result i64 i64) - (then - i64.add - i64.const 1) - ;; Hits the code path for an `else` after a block that does not end unreachable. - (else - i64.sub - i64.const 2)))) diff --git a/cranelift/wasm/wasmtests/multi-5.wat b/cranelift/wasm/wasmtests/multi-5.wat deleted file mode 100644 index 92944770f9e8..000000000000 --- a/cranelift/wasm/wasmtests/multi-5.wat +++ /dev/null @@ -1,11 +0,0 @@ -(module - (func (export "foo") - i32.const 1 - i64.const 2 - ;; More params than results. - (block (param i32 i64) (result i32) - drop - ) - drop - ) -) diff --git a/cranelift/wasm/wasmtests/multi-6.wat b/cranelift/wasm/wasmtests/multi-6.wat deleted file mode 100644 index c1135a1187ec..000000000000 --- a/cranelift/wasm/wasmtests/multi-6.wat +++ /dev/null @@ -1,11 +0,0 @@ -(module - (func (export "foo") - i32.const 1 - ;; Fewer params than results. - (block (param i32) (result i32 i64) - i64.const 2 - ) - drop - drop - ) -) diff --git a/cranelift/wasm/wasmtests/multi-7.wat b/cranelift/wasm/wasmtests/multi-7.wat deleted file mode 100644 index c4545ba26cb5..000000000000 --- a/cranelift/wasm/wasmtests/multi-7.wat +++ /dev/null @@ -1,9 +0,0 @@ -(module - (func (export "f") (param i64 i32) (result i64) - (local.get 0) - (local.get 1) - ;; If with no else. Same number of params and results. - (if (param i64) (result i64) - (then - (drop) - (i64.const -1))))) diff --git a/cranelift/wasm/wasmtests/multi-8.wat b/cranelift/wasm/wasmtests/multi-8.wat deleted file mode 100644 index 1bc23f9f5d07..000000000000 --- a/cranelift/wasm/wasmtests/multi-8.wat +++ /dev/null @@ -1,12 +0,0 @@ -(module - (func (export "f") (param i64 i32) (result i64) - (local.get 0) - (local.get 1) - ;; If with else. Same number of params and results. - (if (param i64) (result i64) - (then - (drop) - (i64.const -1)) - (else - (drop) - (i64.const -2))))) diff --git a/cranelift/wasm/wasmtests/multi-9.wat b/cranelift/wasm/wasmtests/multi-9.wat deleted file mode 100644 index d0cecf71b2b4..000000000000 --- a/cranelift/wasm/wasmtests/multi-9.wat +++ /dev/null @@ -1,15 +0,0 @@ -(module - (func (export "f") (param i64 i32) (result i64) - (local.get 0) - (local.get 1) - (local.get 1) - ;; If with else. More params than results. - (if (param i64 i32) (result i64) - (then - (drop) - (drop) - (i64.const -1)) - (else - (drop) - (drop) - (i64.const -2))))) diff --git a/cranelift/wasm/wasmtests/nullref.wat b/cranelift/wasm/wasmtests/nullref.wat deleted file mode 100644 index 5dbdcc29a281..000000000000 --- a/cranelift/wasm/wasmtests/nullref.wat +++ /dev/null @@ -1,11 +0,0 @@ -(module - (func (result externref) - (ref.null extern) - ) - - (func (result externref) - (block (result externref) - (ref.null extern) - ) - ) -) diff --git a/cranelift/wasm/wasmtests/passive-data.wat b/cranelift/wasm/wasmtests/passive-data.wat deleted file mode 100644 index 00d3b2c849ae..000000000000 --- a/cranelift/wasm/wasmtests/passive-data.wat +++ /dev/null @@ -1,12 +0,0 @@ -(module - (data $passive "this is a passive data segment") - (memory 0) - - (func (export "init") (param i32 i32 i32) - local.get 0 ;; dst - local.get 1 ;; src - local.get 2 ;; cnt - memory.init $passive) - - (func (export "drop") - data.drop $passive)) diff --git a/cranelift/wasm/wasmtests/pr2303.wat b/cranelift/wasm/wasmtests/pr2303.wat deleted file mode 100644 index f86831e3eeb2..000000000000 --- a/cranelift/wasm/wasmtests/pr2303.wat +++ /dev/null @@ -1,15 +0,0 @@ -(module - (memory (export "mem") 1 1) - (func (export "runif") (param $cond i32) - i32.const 48 - (v128.load (i32.const 0)) - (v128.load (i32.const 16)) - (if (param v128) (param v128) (result v128 v128) - (local.get $cond) - (then i64x2.add - (v128.load (i32.const 32))) - (else i32x4.sub - (v128.load (i32.const 0)))) - i16x8.mul - v128.store) -) diff --git a/cranelift/wasm/wasmtests/pr2559.wat b/cranelift/wasm/wasmtests/pr2559.wat deleted file mode 100644 index 8cf50677c891..000000000000 --- a/cranelift/wasm/wasmtests/pr2559.wat +++ /dev/null @@ -1,51 +0,0 @@ -(module - (type (;0;) (func (result v128 v128 v128))) - - (func $main1 (type 0) (result v128 v128 v128) - call $main1 - i8x16.add - call $main1 - i8x16.ge_u - i16x8.ne - i32.const 13 - br_if 0 (;@0;) - i32.const 43 - br_if 0 (;@0;) - i32.const 13 - br_if 0 (;@0;) - i32.const 87 - select - unreachable - i32.const 0 - br_if 0 (;@0;) - i32.const 13 - br_if 0 (;@0;) - i32.const 43 - br_if 0 (;@0;) - ) - (export "main1" (func $main1)) - - (func $main2 (type 0) (result v128 v128 v128) - call $main2 - i8x16.add - call $main2 - i8x16.ge_u - i16x8.ne - i32.const 13 - br_if 0 (;@0;) - i32.const 43 - br_if 0 (;@0;) - i32.const 13 - br_if 0 (;@0;) - i32.const 87 - select (result v128) - unreachable - i32.const 0 - br_if 0 (;@0;) - i32.const 13 - br_if 0 (;@0;) - i32.const 43 - br_if 0 (;@0;) - ) - (export "main2" (func $main2)) -) diff --git a/cranelift/wasm/wasmtests/ref-func-0.wat b/cranelift/wasm/wasmtests/ref-func-0.wat deleted file mode 100644 index 4cdee3f567da..000000000000 --- a/cranelift/wasm/wasmtests/ref-func-0.wat +++ /dev/null @@ -1,12 +0,0 @@ -(module - (func $imported (import "env" "f") (param i32) (result i32)) - (func $local (result externref externref funcref funcref) - global.get 0 - global.get 1 - global.get 2 - global.get 3) - - (global (export "externref-imported") externref (ref.null extern)) - (global (export "externref-local") externref (ref.null extern)) - (global (export "funcref-imported") funcref (ref.func $imported)) - (global (export "funcref-local") funcref (ref.func $local))) diff --git a/cranelift/wasm/wasmtests/rust_fannkuch.wat b/cranelift/wasm/wasmtests/rust_fannkuch.wat deleted file mode 100644 index c61f08c67a98..000000000000 --- a/cranelift/wasm/wasmtests/rust_fannkuch.wat +++ /dev/null @@ -1,1723 +0,0 @@ -(module - (type $0 (;0;) (func (param i32 i32 i32) (result i32))) - (type $1 (;1;) (func (param i32 i32) (result i32))) - (type $2 (;2;) (func (param i32))) - (type $3 (;3;) (func (param i32) (result i32))) - (type $4 (;4;) (func (param i32 i32))) - (type $5 (;5;) (func (param i64 i32) (result i32))) - (type $6 (;6;) (func (param i32) (result i64))) - (type $7 (;7;) (func)) - (type $8 (;8;) (func (param i32 i32))) - (type $9 (;9;) (func (param i32 i32 i32) (result i32))) - (func $0 (;0;) (type $7) - (local $0 i32) (local $1 i32) - i32.const 1 - local.set $0 - block $label$1 ;; label = @1 - block $label$2 ;; label = @2 - block $label$3 ;; label = @3 - i32.const 1049232 - i32.load - i32.const 1 - i32.eq - if ;; label = @4 - block ;; label = @5 - i32.const 1049236 - i32.const 1049236 - i32.load - i32.const 1 - i32.add - local.tee $0 - i32.store - local.get $0 - i32.const 3 - i32.lt_u - br_if 2 (;@3;) - br 3 (;@2;) - end - end - i32.const 1049232 - i64.const 4294967297 - i64.store - end - i32.const 1049240 - i32.load - local.tee $1 - i32.const -1 - i32.le_s - br_if 0 (;@2;) - i32.const 1049240 - local.get $1 - i32.store - local.get $0 - i32.const 2 - i32.lt_u - br_if 1 (;@1;) - end - unreachable - end - unreachable - ) - (func $1 (;1;) (type $2) (param $0 i32) - (local $1 i32) - global.get $global$0 - i32.const 16 - i32.sub - local.tee $1 - global.set $global$0 - local.get $0 - i32.load offset=8 - i32.eqz - if ;; label = @1 - block ;; label = @2 - i32.const 1049172 - call $2 - unreachable - end - end - local.get $1 - local.get $0 - i32.const 20 - i32.add - i64.load align=4 - i64.store offset=8 - local.get $1 - local.get $0 - i64.load offset=12 align=4 - i64.store - call $0 - unreachable - ) - (func $2 (;2;) (type $2) (param $0 i32) - (local $1 i32) (local $2 i64) (local $3 i64) (local $4 i64) - global.get $global$0 - i32.const 48 - i32.sub - local.tee $1 - global.set $global$0 - local.get $0 - i64.load offset=8 align=4 - local.set $2 - local.get $0 - i64.load offset=16 align=4 - local.set $3 - local.get $0 - i64.load align=4 - local.set $4 - local.get $1 - i32.const 20 - i32.add - i32.const 0 - i32.store - local.get $1 - local.get $4 - i64.store offset=24 - local.get $1 - i32.const 1048656 - i32.store offset=16 - local.get $1 - i64.const 1 - i64.store offset=4 align=4 - local.get $1 - local.get $1 - i32.const 24 - i32.add - i32.store - local.get $1 - local.get $3 - i64.store offset=40 - local.get $1 - local.get $2 - i64.store offset=32 - local.get $1 - local.get $1 - i32.const 32 - i32.add - call $5 - unreachable - ) - (func $3 (;3;) (type $8) (param $0 i32) (param $1 i32) - (local $2 i32) - global.get $global$0 - i32.const 48 - i32.sub - local.tee $2 - global.set $global$0 - local.get $2 - i32.const 16 - i32.store offset=4 - local.get $2 - local.get $1 - i32.store - local.get $2 - i32.const 44 - i32.add - i32.const 1 - i32.store - local.get $2 - i32.const 28 - i32.add - i32.const 2 - i32.store - local.get $2 - i32.const 1 - i32.store offset=36 - local.get $2 - i64.const 2 - i64.store offset=12 align=4 - local.get $2 - i32.const 1049140 - i32.store offset=8 - local.get $2 - local.get $2 - i32.store offset=40 - local.get $2 - local.get $2 - i32.const 4 - i32.add - i32.store offset=32 - local.get $2 - local.get $2 - i32.const 32 - i32.add - i32.store offset=24 - local.get $2 - i32.const 8 - i32.add - local.get $0 - call $5 - unreachable - ) - (func $4 (;4;) (type $1) (param $0 i32) (param $1 i32) (result i32) - local.get $0 - i64.load32_u - local.get $1 - call $6 - ) - (func $5 (;5;) (type $4) (param $0 i32) (param $1 i32) - (local $2 i32) (local $3 i64) - global.get $global$0 - i32.const 32 - i32.sub - local.tee $2 - global.set $global$0 - local.get $1 - i64.load align=4 - local.set $3 - local.get $2 - i32.const 20 - i32.add - local.get $1 - i64.load offset=8 align=4 - i64.store align=4 - local.get $2 - local.get $3 - i64.store offset=12 align=4 - local.get $2 - local.get $0 - i32.store offset=8 - local.get $2 - i32.const 1049156 - i32.store offset=4 - local.get $2 - i32.const 1048656 - i32.store - local.get $2 - call $1 - unreachable - ) - (func $6 (;6;) (type $5) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i64) (local $14 i32) (local $15 i32) - global.get $global$0 - i32.const 48 - i32.sub - local.tee $6 - global.set $global$0 - i32.const 39 - local.set $2 - block $label$1 ;; label = @1 - block $label$2 ;; label = @2 - local.get $0 - i64.const 10000 - i64.ge_u - if ;; label = @3 - block ;; label = @4 - loop $label$4 ;; label = @5 - local.get $6 - i32.const 9 - i32.add - local.get $2 - i32.add - local.tee $3 - i32.const -4 - i32.add - local.get $0 - local.get $0 - i64.const 10000 - i64.div_u - local.tee $13 - i64.const -10000 - i64.mul - i64.add - i32.wrap_i64 - local.tee $4 - i32.const 100 - i32.div_u - local.tee $5 - i32.const 1 - i32.shl - i32.const 1048706 - i32.add - i32.load16_u align=1 - i32.store16 align=1 - local.get $3 - i32.const -2 - i32.add - local.get $5 - i32.const -100 - i32.mul - local.get $4 - i32.add - i32.const 1 - i32.shl - i32.const 1048706 - i32.add - i32.load16_u align=1 - i32.store16 align=1 - local.get $2 - i32.const -4 - i32.add - local.set $2 - block (result i32) ;; label = @6 - local.get $0 - i64.const 99999999 - i64.gt_u - local.set $14 - local.get $13 - local.set $0 - local.get $14 - end - br_if 0 (;@5;) - end - local.get $13 - i32.wrap_i64 - local.tee $3 - i32.const 99 - i32.le_s - br_if 3 (;@1;) - br 2 (;@2;) - end - end - local.get $0 - local.tee $13 - i32.wrap_i64 - local.tee $3 - i32.const 99 - i32.le_s - br_if 1 (;@1;) - end - local.get $2 - i32.const -2 - i32.add - local.tee $2 - local.get $6 - i32.const 9 - i32.add - i32.add - local.get $13 - i32.wrap_i64 - local.tee $4 - i32.const 65535 - i32.and - i32.const 100 - i32.div_u - local.tee $3 - i32.const -100 - i32.mul - local.get $4 - i32.add - i32.const 65535 - i32.and - i32.const 1 - i32.shl - i32.const 1048706 - i32.add - i32.load16_u align=1 - i32.store16 align=1 - end - block $label$5 ;; label = @1 - local.get $3 - i32.const 9 - i32.le_s - if ;; label = @2 - block ;; label = @3 - local.get $2 - i32.const -1 - i32.add - local.tee $2 - local.get $6 - i32.const 9 - i32.add - i32.add - local.get $3 - i32.const 48 - i32.add - i32.store8 - br 2 (;@1;) - end - end - local.get $2 - i32.const -2 - i32.add - local.tee $2 - local.get $6 - i32.const 9 - i32.add - i32.add - local.get $3 - i32.const 1 - i32.shl - i32.const 1048706 - i32.add - i32.load16_u align=1 - i32.store16 align=1 - end - i32.const 39 - local.get $2 - i32.sub - local.set $7 - i32.const 1 - local.set $3 - i32.const 43 - i32.const 1114112 - local.get $1 - i32.load - local.tee $4 - i32.const 1 - i32.and - local.tee $11 - select - local.set $8 - local.get $4 - i32.const 29 - i32.shl - i32.const 31 - i32.shr_s - i32.const 1048656 - i32.and - local.set $9 - local.get $6 - i32.const 9 - i32.add - local.get $2 - i32.add - local.set $10 - block $label$7 ;; label = @1 - block $label$8 ;; label = @2 - block $label$9 ;; label = @3 - block $label$10 ;; label = @4 - block $label$11 ;; label = @5 - block $label$12 ;; label = @6 - block $label$13 ;; label = @7 - block $label$14 ;; label = @8 - block $label$15 (result i32) ;; label = @9 - block $label$16 ;; label = @10 - block $label$17 ;; label = @11 - block $label$18 ;; label = @12 - block $label$19 ;; label = @13 - local.get $1 - i32.load offset=8 - i32.const 1 - i32.eq - if ;; label = @14 - block ;; label = @15 - local.get $1 - i32.const 12 - i32.add - i32.load - local.tee $5 - local.get $7 - local.get $11 - i32.add - local.tee $2 - i32.le_u - br_if 2 (;@13;) - local.get $4 - i32.const 8 - i32.and - br_if 3 (;@12;) - local.get $5 - local.get $2 - i32.sub - local.set $4 - i32.const 1 - local.get $1 - i32.load8_u offset=48 - local.tee $3 - local.get $3 - i32.const 3 - i32.eq - select - local.tee $3 - i32.const 3 - i32.and - i32.eqz - br_if 4 (;@11;) - local.get $3 - i32.const 2 - i32.eq - br_if 5 (;@10;) - i32.const 0 - local.set $5 - local.get $4 - br 6 (;@9;) - end - end - local.get $1 - local.get $8 - local.get $9 - call $9 - br_if 10 (;@3;) - br 11 (;@2;) - end - local.get $1 - local.get $8 - local.get $9 - call $9 - br_if 9 (;@3;) - br 10 (;@2;) - end - local.get $1 - i32.const 1 - i32.store8 offset=48 - local.get $1 - i32.const 48 - i32.store offset=4 - local.get $1 - local.get $8 - local.get $9 - call $9 - br_if 8 (;@3;) - local.get $5 - local.get $2 - i32.sub - local.set $3 - i32.const 1 - local.get $1 - i32.const 48 - i32.add - i32.load8_u - local.tee $4 - local.get $4 - i32.const 3 - i32.eq - select - local.tee $4 - i32.const 3 - i32.and - i32.eqz - br_if 3 (;@8;) - local.get $4 - i32.const 2 - i32.eq - br_if 4 (;@7;) - i32.const 0 - local.set $4 - br 5 (;@6;) - end - local.get $4 - local.set $5 - i32.const 0 - br 1 (;@9;) - end - local.get $4 - i32.const 1 - i32.add - i32.const 1 - i32.shr_u - local.set $5 - local.get $4 - i32.const 1 - i32.shr_u - end - local.set $3 - i32.const -1 - local.set $2 - local.get $1 - i32.const 4 - i32.add - local.set $4 - local.get $1 - i32.const 24 - i32.add - local.set $11 - local.get $1 - i32.const 28 - i32.add - local.set $12 - block $label$21 ;; label = @9 - loop $label$22 ;; label = @10 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - local.get $3 - i32.ge_u - br_if 1 (;@9;) - local.get $11 - i32.load - local.get $4 - i32.load - local.get $12 - i32.load - i32.load offset=16 - call_indirect (type $1) - i32.eqz - br_if 0 (;@10;) - end - br 8 (;@1;) - end - local.get $1 - i32.const 4 - i32.add - i32.load - local.set $4 - i32.const 1 - local.set $3 - local.get $1 - local.get $8 - local.get $9 - call $9 - br_if 5 (;@3;) - local.get $1 - i32.const 24 - i32.add - local.tee $2 - i32.load - local.get $10 - local.get $7 - local.get $1 - i32.const 28 - i32.add - local.tee $1 - i32.load - i32.load offset=12 - call_indirect (type $0) - br_if 5 (;@3;) - local.get $2 - i32.load - local.set $7 - i32.const -1 - local.set $2 - local.get $1 - i32.load - i32.const 16 - i32.add - local.set $1 - loop $label$23 ;; label = @9 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - local.get $5 - i32.ge_u - br_if 4 (;@5;) - local.get $7 - local.get $4 - local.get $1 - i32.load - call_indirect (type $1) - i32.eqz - br_if 0 (;@9;) - end - br 5 (;@3;) - end - local.get $3 - local.set $4 - i32.const 0 - local.set $3 - br 1 (;@6;) - end - local.get $3 - i32.const 1 - i32.add - i32.const 1 - i32.shr_u - local.set $4 - local.get $3 - i32.const 1 - i32.shr_u - local.set $3 - end - i32.const -1 - local.set $2 - local.get $1 - i32.const 4 - i32.add - local.set $5 - local.get $1 - i32.const 24 - i32.add - local.set $8 - local.get $1 - i32.const 28 - i32.add - local.set $9 - block $label$24 ;; label = @6 - loop $label$25 ;; label = @7 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - local.get $3 - i32.ge_u - br_if 1 (;@6;) - local.get $8 - i32.load - local.get $5 - i32.load - local.get $9 - i32.load - i32.load offset=16 - call_indirect (type $1) - i32.eqz - br_if 0 (;@7;) - end - br 5 (;@1;) - end - local.get $1 - i32.const 4 - i32.add - i32.load - local.set $5 - i32.const 1 - local.set $3 - local.get $1 - i32.const 24 - i32.add - local.tee $2 - i32.load - local.get $10 - local.get $7 - local.get $1 - i32.const 28 - i32.add - local.tee $1 - i32.load - i32.load offset=12 - call_indirect (type $0) - br_if 2 (;@3;) - local.get $2 - i32.load - local.set $7 - i32.const -1 - local.set $2 - local.get $1 - i32.load - i32.const 16 - i32.add - local.set $1 - loop $label$26 ;; label = @6 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - local.get $4 - i32.ge_u - br_if 2 (;@4;) - local.get $7 - local.get $5 - local.get $1 - i32.load - call_indirect (type $1) - i32.eqz - br_if 0 (;@6;) - end - br 2 (;@3;) - end - local.get $6 - i32.const 48 - i32.add - global.set $global$0 - i32.const 0 - return - end - i32.const 0 - local.set $3 - end - local.get $6 - i32.const 48 - i32.add - global.set $global$0 - local.get $3 - return - end - block (result i32) ;; label = @2 - local.get $1 - i32.load offset=24 - local.get $10 - local.get $7 - local.get $1 - i32.const 28 - i32.add - i32.load - i32.load offset=12 - call_indirect (type $0) - local.set $15 - local.get $6 - i32.const 48 - i32.add - global.set $global$0 - local.get $15 - end - return - end - local.get $6 - i32.const 48 - i32.add - global.set $global$0 - i32.const 1 - ) - (func $7 (;7;) (type $2) (param $0 i32) - nop - ) - (func $8 (;8;) (type $6) (param $0 i32) (result i64) - i64.const -2357177763932378009 - ) - (func $9 (;9;) (type $9) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - block $label$1 ;; label = @1 - block $label$2 (result i32) ;; label = @2 - local.get $1 - i32.const 1114112 - i32.ne - if ;; label = @3 - i32.const 1 - local.get $0 - i32.load offset=24 - local.get $1 - local.get $0 - i32.const 28 - i32.add - i32.load - i32.load offset=16 - call_indirect (type $1) - br_if 1 (;@2;) - drop - end - local.get $2 - i32.eqz - br_if 1 (;@1;) - local.get $0 - i32.load offset=24 - local.get $2 - i32.const 0 - local.get $0 - i32.const 28 - i32.add - i32.load - i32.load offset=12 - call_indirect (type $0) - end - return - end - i32.const 0 - ) - (func $10 (;10;) (type $3) (param $0 i32) (result i32) - (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) - global.get $global$0 - i32.const 256 - i32.sub - local.tee $1 - global.set $global$0 - local.get $1 - i64.const 4294967297 - i64.store offset=56 align=4 - local.get $1 - i64.const 4294967297 - i64.store offset=48 align=4 - local.get $1 - i64.const 4294967297 - i64.store offset=40 align=4 - local.get $1 - i64.const 4294967297 - i64.store offset=32 align=4 - local.get $1 - i64.const 4294967297 - i64.store offset=24 align=4 - local.get $1 - i64.const 4294967297 - i64.store offset=16 align=4 - local.get $1 - i64.const 4294967297 - i64.store offset=8 align=4 - local.get $1 - i64.const 4294967297 - i64.store align=4 - block $label$1 ;; label = @1 - local.get $0 - i32.const 1 - i32.add - local.tee $11 - i32.const 2 - i32.ge_u - if ;; label = @2 - block ;; label = @3 - local.get $1 - local.set $3 - i32.const 1 - local.set $2 - loop $label$3 ;; label = @4 - local.get $2 - i32.const 16 - i32.ge_u - br_if 3 (;@1;) - local.get $3 - i32.const 4 - i32.add - local.tee $4 - local.get $3 - i32.load - local.get $2 - i32.mul - i32.store - local.get $4 - local.set $3 - local.get $2 - i32.const 1 - i32.add - local.tee $4 - local.set $2 - local.get $4 - local.get $11 - i32.lt_u - br_if 0 (;@4;) - end - end - end - local.get $0 - i32.const 16 - i32.lt_u - if ;; label = @2 - block ;; label = @3 - i32.const 1 - local.set $20 - local.get $1 - local.get $0 - i32.const 2 - i32.shl - i32.add - i32.load - local.tee $9 - local.set $21 - local.get $9 - i32.const 24 - i32.ge_u - if ;; label = @4 - i32.const 24 - i32.const 25 - local.get $9 - local.get $9 - i32.const 24 - i32.div_u - local.tee $21 - i32.const 24 - i32.mul - i32.eq - select - local.set $20 - end - i32.const 0 - local.get $0 - i32.sub - local.set $40 - local.get $1 - i32.const 196 - i32.add - local.set $12 - local.get $1 - i32.const 132 - i32.add - local.set $41 - local.get $1 - i32.const 124 - i32.add - local.set $42 - local.get $1 - i32.const 68 - i32.add - local.set $11 - local.get $0 - i32.const 2 - i32.lt_u - local.set $43 - loop $label$6 ;; label = @4 - local.get $1 - i32.const 120 - i32.add - i64.const 0 - i64.store - local.get $1 - i32.const 112 - i32.add - i64.const 0 - i64.store - local.get $1 - i32.const 104 - i32.add - i64.const 0 - i64.store - local.get $1 - i32.const 96 - i32.add - i64.const 0 - i64.store - local.get $1 - i32.const 88 - i32.add - i64.const 0 - i64.store - local.get $1 - i32.const 80 - i32.add - i64.const 0 - i64.store - local.get $1 - i32.const 72 - i32.add - i64.const 0 - i64.store - local.get $1 - i64.const 0 - i64.store offset=64 - local.get $1 - i32.const 184 - i32.add - local.tee $26 - i64.const 0 - i64.store - local.get $1 - i32.const 176 - i32.add - local.tee $27 - i64.const 0 - i64.store - local.get $1 - i32.const 168 - i32.add - local.tee $28 - i64.const 0 - i64.store - local.get $1 - i32.const 160 - i32.add - local.tee $29 - i64.const 0 - i64.store - local.get $1 - i32.const 152 - i32.add - local.tee $30 - i64.const 0 - i64.store - local.get $1 - i32.const 144 - i32.add - local.tee $31 - i64.const 0 - i64.store - local.get $1 - i32.const 136 - i32.add - local.tee $32 - i64.const 0 - i64.store - local.get $1 - i64.const 0 - i64.store offset=128 - local.get $1 - i32.const 248 - i32.add - local.tee $33 - i64.const 64424509454 - i64.store align=4 - local.get $1 - i32.const 240 - i32.add - local.tee $34 - i64.const 55834574860 - i64.store align=4 - local.get $1 - i32.const 232 - i32.add - local.tee $35 - i64.const 47244640266 - i64.store align=4 - local.get $1 - i32.const 224 - i32.add - local.tee $36 - i64.const 38654705672 - i64.store align=4 - local.get $1 - i32.const 216 - i32.add - local.tee $37 - i64.const 30064771078 - i64.store align=4 - local.get $1 - i32.const 208 - i32.add - local.tee $38 - i64.const 21474836484 - i64.store align=4 - local.get $1 - i32.const 200 - i32.add - local.tee $39 - i64.const 12884901890 - i64.store align=4 - local.get $1 - i64.const 4294967296 - i64.store offset=192 align=4 - local.get $13 - local.get $21 - i32.mul - local.set $7 - block $label$7 (result i32) ;; label = @5 - block $label$8 ;; label = @6 - local.get $43 - i32.eqz - if ;; label = @7 - block ;; label = @8 - local.get $40 - local.set $23 - local.get $7 - local.set $14 - local.get $0 - local.set $15 - i32.const 0 - local.set $5 - br 2 (;@6;) - end - end - i32.const 0 - br 1 (;@5;) - end - i32.const 1 - end - local.set $2 - loop $label$10 ;; label = @5 - block $label$11 ;; label = @6 - block $label$12 ;; label = @7 - block $label$13 (result i32) ;; label = @8 - block $label$14 ;; label = @9 - block $label$15 ;; label = @10 - block $label$16 ;; label = @11 - block $label$17 ;; label = @12 - block $label$18 ;; label = @13 - block $label$19 ;; label = @14 - local.get $2 - i32.eqz - if ;; label = @15 - block ;; label = @16 - local.get $13 - i32.const 1 - i32.add - local.set $13 - local.get $9 - local.get $7 - local.get $21 - i32.add - local.tee $3 - local.get $3 - local.get $9 - i32.gt_u - select - i32.const -1 - i32.add - local.set $44 - i32.const 0 - local.set $24 - local.get $1 - i32.load offset=192 - local.tee $6 - i32.const 1 - i32.ge_s - br_if 2 (;@14;) - br 3 (;@13;) - end - end - block $label$21 ;; label = @15 - block $label$22 ;; label = @16 - block $label$23 ;; label = @17 - block $label$24 ;; label = @18 - block $label$25 ;; label = @19 - block $label$26 ;; label = @20 - block $label$27 ;; label = @21 - block $label$28 ;; label = @22 - block $label$29 ;; label = @23 - block $label$30 ;; label = @24 - block $label$31 ;; label = @25 - local.get $5 - br_table 0 (;@25;) 1 (;@24;) 2 (;@23;) - end - local.get $15 - i32.const -1 - i32.add - local.tee $4 - i32.const 16 - i32.ge_u - br_if 6 (;@18;) - local.get $1 - local.get $4 - i32.const 2 - i32.shl - local.tee $2 - i32.add - i32.load - local.tee $3 - i32.eqz - br_if 7 (;@17;) - local.get $14 - i32.const -2147483648 - i32.eq - if ;; label = @25 - local.get $3 - i32.const -1 - i32.eq - br_if 9 (;@16;) - end - local.get $1 - i32.const -64 - i32.sub - local.get $2 - i32.add - local.get $14 - local.get $3 - i32.div_s - local.tee $16 - i32.store - local.get $32 - local.get $39 - i64.load align=4 - i64.store - local.get $31 - local.get $38 - i64.load align=4 - i64.store - local.get $30 - local.get $37 - i64.load align=4 - i64.store - local.get $29 - local.get $36 - i64.load align=4 - i64.store - local.get $28 - local.get $35 - i64.load align=4 - i64.store - local.get $27 - local.get $34 - i64.load align=4 - i64.store - local.get $26 - local.get $33 - i64.load align=4 - i64.store - local.get $1 - local.get $1 - i64.load offset=192 align=4 - i64.store offset=128 - local.get $16 - local.get $23 - i32.add - local.set $45 - local.get $14 - local.get $3 - local.get $16 - i32.mul - i32.sub - local.set $14 - i32.const 0 - local.set $2 - local.get $1 - i32.const 192 - i32.add - local.set $8 - loop $label$33 ;; label = @25 - block $label$34 ;; label = @26 - local.get $2 - local.get $16 - i32.add - local.tee $3 - local.get $4 - i32.gt_u - if ;; label = @27 - block ;; label = @28 - local.get $2 - local.get $45 - i32.add - local.tee $46 - i32.const 15 - i32.gt_u - br_if 7 (;@21;) - local.get $3 - local.get $15 - i32.sub - local.set $3 - local.get $2 - i32.const 15 - i32.le_u - br_if 2 (;@26;) - br 6 (;@22;) - end - end - local.get $3 - i32.const 16 - i32.ge_u - br_if 6 (;@20;) - local.get $2 - i32.const 15 - i32.gt_u - br_if 4 (;@22;) - end - local.get $8 - local.get $1 - i32.const 128 - i32.add - local.get $3 - i32.const 2 - i32.shl - i32.add - i32.load - i32.store - local.get $8 - i32.const 4 - i32.add - local.set $8 - local.get $2 - i32.const 1 - i32.add - local.tee $2 - local.get $15 - i32.lt_u - br_if 0 (;@25;) - end - local.get $23 - i32.const 1 - i32.add - local.set $23 - local.get $4 - local.tee $15 - i32.const 1 - i32.gt_u - br_if 9 (;@15;) - i32.const 0 - local.set $2 - br 19 (;@5;) - end - local.get $26 - local.get $33 - i64.load align=4 - i64.store - local.get $27 - local.get $34 - i64.load align=4 - i64.store - local.get $28 - local.get $35 - i64.load align=4 - i64.store - local.get $29 - local.get $36 - i64.load align=4 - i64.store - local.get $30 - local.get $37 - i64.load align=4 - i64.store - local.get $31 - local.get $38 - i64.load align=4 - i64.store - local.get $32 - local.get $39 - i64.load align=4 - i64.store - local.get $1 - local.get $1 - i64.load offset=192 align=4 - i64.store offset=128 - local.get $6 - i32.const 15 - i32.gt_u - br_if 4 (;@19;) - i32.const 1 - local.set $17 - local.get $6 - local.set $10 - i32.const 0 - br 15 (;@8;) - end - local.get $7 - local.get $44 - i32.lt_u - if ;; label = @23 - block ;; label = @24 - local.get $12 - i32.load - local.set $25 - local.get $12 - local.get $6 - i32.store - local.get $1 - local.get $25 - i32.store offset=192 - local.get $11 - local.set $18 - local.get $1 - i32.load offset=68 - local.tee $2 - i32.const 1 - i32.lt_s - br_if 18 (;@6;) - i32.const 1 - local.set $19 - br 15 (;@9;) - end - end - local.get $22 - local.get $24 - i32.add - local.set $22 - local.get $13 - local.get $20 - i32.lt_u - br_if 18 (;@4;) - local.get $1 - i32.const 256 - i32.add - global.set $global$0 - local.get $22 - return - end - i32.const 1049076 - local.get $2 - call $3 - unreachable - end - i32.const 1049060 - local.get $46 - call $3 - unreachable - end - i32.const 1049044 - local.get $2 - local.get $16 - i32.add - call $3 - unreachable - end - local.get $6 - local.set $10 - br 11 (;@7;) - end - i32.const 1048980 - local.get $4 - call $3 - unreachable - end - i32.const 1048996 - call $2 - unreachable - end - i32.const 1049020 - call $2 - unreachable - end - i32.const 0 - local.set $5 - br 2 (;@12;) - end - i32.const 1 - local.set $5 - br 2 (;@11;) - end - i32.const 2 - local.set $5 - br 2 (;@10;) - end - i32.const 1 - local.set $2 - br 6 (;@5;) - end - i32.const 1 - local.set $2 - br 5 (;@5;) - end - i32.const 1 - local.set $2 - br 4 (;@5;) - end - i32.const 1 - end - local.set $2 - loop $label$37 ;; label = @8 - block $label$38 ;; label = @9 - block $label$39 ;; label = @10 - local.get $2 - i32.eqz - if ;; label = @11 - block ;; label = @12 - local.get $10 - local.tee $3 - i32.const 2 - i32.shl - local.tee $4 - local.get $1 - i32.const 128 - i32.add - i32.add - local.tee $5 - i32.load - local.tee $10 - if ;; label = @13 - block ;; label = @14 - local.get $5 - local.get $3 - i32.store - block $label$42 ;; label = @15 - local.get $3 - i32.const 3 - i32.lt_u - br_if 0 (;@15;) - local.get $3 - i32.const -1 - i32.add - i32.const 1 - i32.shr_u - local.tee $8 - i32.eqz - br_if 0 (;@15;) - local.get $4 - local.get $42 - i32.add - local.set $2 - local.get $41 - local.set $3 - loop $label$43 ;; label = @16 - local.get $3 - i32.load - local.set $4 - local.get $3 - local.get $2 - i32.load - i32.store - local.get $2 - local.get $4 - i32.store - local.get $3 - i32.const 4 - i32.add - local.set $3 - local.get $2 - i32.const -4 - i32.add - local.set $2 - local.get $8 - i32.const -1 - i32.add - local.tee $8 - br_if 0 (;@16;) - end - end - local.get $17 - i32.const 1 - i32.add - local.set $17 - local.get $10 - i32.const 16 - i32.lt_u - br_if 5 (;@9;) - br 7 (;@7;) - end - end - i32.const 0 - local.get $17 - i32.sub - local.get $17 - local.get $7 - i32.const 1 - i32.and - select - local.get $24 - i32.add - local.set $24 - i32.const 2 - local.set $5 - br 2 (;@10;) - end - end - i32.const 0 - local.set $2 - local.get $18 - i32.const 0 - i32.store - local.get $1 - local.get $6 - local.tee $4 - i32.store offset=192 - local.get $19 - i32.const 1 - i32.add - local.set $5 - local.get $12 - local.set $3 - block $label$44 ;; label = @11 - block $label$45 ;; label = @12 - loop $label$46 ;; label = @13 - local.get $2 - i32.const 2 - i32.add - i32.const 16 - i32.ge_u - br_if 1 (;@12;) - local.get $3 - local.get $3 - i32.const 4 - i32.add - local.tee $3 - i32.load - i32.store - local.get $2 - i32.const 1 - i32.add - local.tee $2 - local.get $19 - i32.lt_u - br_if 0 (;@13;) - end - local.get $5 - i32.const 16 - i32.ge_u - br_if 1 (;@11;) - local.get $5 - i32.const 2 - i32.shl - local.tee $3 - local.get $1 - i32.const 192 - i32.add - i32.add - local.get $25 - i32.store - local.get $1 - i32.const -64 - i32.sub - local.get $3 - i32.add - local.tee $18 - i32.load - local.tee $2 - local.get $19 - i32.le_s - br_if 6 (;@6;) - local.get $12 - i32.load - local.set $6 - local.get $5 - local.set $19 - local.get $4 - local.set $25 - i32.const 1 - local.set $2 - br 4 (;@8;) - end - i32.const 1049108 - local.get $2 - i32.const 2 - i32.add - call $3 - unreachable - end - i32.const 1049124 - local.get $5 - call $3 - unreachable - end - i32.const 1 - local.set $2 - br 4 (;@5;) - end - i32.const 0 - local.set $2 - br 0 (;@8;) - end - end - i32.const 1049092 - local.get $10 - call $3 - unreachable - end - local.get $7 - i32.const 1 - i32.add - local.set $7 - local.get $18 - local.get $2 - i32.const 1 - i32.add - i32.store - block $label$47 ;; label = @6 - block $label$48 ;; label = @7 - local.get $1 - i32.load offset=192 - local.tee $6 - i32.const 1 - i32.ge_s - if ;; label = @8 - block ;; label = @9 - i32.const 1 - local.set $5 - br 2 (;@7;) - end - end - i32.const 2 - local.set $5 - br 1 (;@6;) - end - i32.const 1 - local.set $2 - br 1 (;@5;) - end - i32.const 1 - local.set $2 - br 0 (;@5;) - end - end - end - end - i32.const 1049212 - local.get $0 - call $3 - unreachable - end - i32.const 1049196 - local.get $2 - call $3 - unreachable - ) - (table $0 (;0;) 4 4 funcref) - (memory $0 (;0;) 17) - (global $global$0 (;0;) (mut i32) i32.const 1048576) - (global $global$1 (;1;) i32 i32.const 1049244) - (global $global$2 (;2;) i32 i32.const 1049244) - (export "memory" (memory $0)) - (export "__heap_base" (global $global$1)) - (export "__data_end" (global $global$2)) - (export "run_fannkuch" (func $10)) - (elem (;0;) (i32.const 1) func $4 $7 $8) - (data (;0;) (i32.const 1048576) "src/lib.rs\00\00\00\00\00\00attempt to divide by zero\00\00\00\00\00\00\00attempt to divide with overflow\00index out of bounds: the len is but the index is 00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899called `Option::unwrap()` on a `None` valuesrc/libcore/option.rssrc/lib.rs") - (data (;1;) (i32.const 1048982) "\10\00\0a\00\00\00%\00\00\00\1d\00\00\00\10\00\10\00\19\00\00\00\00\00\10\00\0a\00\00\00&\00\00\00\15\00\00\000\00\10\00\1f\00\00\00\00\00\10\00\0a\00\00\00&\00\00\00\15\00\00\00\00\00\10\00\0a\00\00\00.\00\00\00\15\00\00\00\00\00\10\00\0a\00\00\000\00\00\00\15\00\00\00\00\00\10\00\0a\00\00\00-\00\00\00\11\00\00\00\00\00\10\00\0a\00\00\00E\00\00\00\17\00\00\00\00\00\10\00\0a\00\00\00q\00\00\00\22\00\00\00\00\00\10\00\0a\00\00\00s\00\00\00\11\00\00\00P\00\10\00 \00\00\00p\00\10\00\12\00\00\00\02\00\00\00\00\00\00\00\01\00\00\00\03\00\00\00J\01\10\00+\00\00\00u\01\10\00\15\00\00\00Y\01\00\00\15\00\00\00\8a\01\10\00\0a\00\00\00\08\00\00\00\09\00\00\00\8a\01\10\00\0a\00\00\00\0a\00\00\00\14") -) \ No newline at end of file diff --git a/cranelift/wasm/wasmtests/select.wat b/cranelift/wasm/wasmtests/select.wat deleted file mode 100644 index ff3599025b68..000000000000 --- a/cranelift/wasm/wasmtests/select.wat +++ /dev/null @@ -1,19 +0,0 @@ -(module - (func $untyped-select (result i32) - i32.const 42 - i32.const 24 - i32.const 1 - select) - - (func $typed-select-1 (result externref) - ref.null extern - ref.null extern - i32.const 1 - select (result externref)) - - (func $typed-select-2 (param externref) (result externref) - ref.null extern - local.get 0 - i32.const 1 - select (result externref)) -) diff --git a/cranelift/wasm/wasmtests/simd-store.wat b/cranelift/wasm/wasmtests/simd-store.wat deleted file mode 100644 index f3bdd1d5c064..000000000000 --- a/cranelift/wasm/wasmtests/simd-store.wat +++ /dev/null @@ -1,83 +0,0 @@ -(module - (func (param v128) - (v128.store (i32.const 0) (i8x16.eq (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i16x8.eq (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i32x4.eq (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i64x2.eq (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (i8x16.ne (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i16x8.ne (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i32x4.ne (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i64x2.ne (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (i8x16.lt_s (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i16x8.lt_s (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i32x4.lt_s (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i64x2.lt_s (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (i8x16.lt_u (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i16x8.lt_u (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i32x4.lt_u (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (i8x16.gt_s (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i16x8.gt_s (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i32x4.gt_s (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i64x2.gt_s (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (i8x16.gt_u (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i16x8.gt_u (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (i32x4.gt_u (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (f32x4.eq (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (f64x2.eq (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (f32x4.ne (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (f64x2.ne (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (f32x4.lt (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (f64x2.lt (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (f32x4.le (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (f64x2.le (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (f32x4.gt (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (f64x2.gt (local.get 0) (local.get 0)))) - - (func (param v128) - (v128.store (i32.const 0) (f32x4.ge (local.get 0) (local.get 0)))) - (func (param v128) - (v128.store (i32.const 0) (f64x2.ge (local.get 0) (local.get 0)))) - - (memory 0) -) diff --git a/cranelift/wasm/wasmtests/simd.wat b/cranelift/wasm/wasmtests/simd.wat deleted file mode 100644 index 0e4064899337..000000000000 --- a/cranelift/wasm/wasmtests/simd.wat +++ /dev/null @@ -1,29 +0,0 @@ -(module - (func $test_splat (result i32) - i32.const 42 - i32x4.splat - i32x4.extract_lane 0 - ) - - (func $test_insert_lane (result i32) - v128.const i64x2 0 0 - i32.const 99 - i32x4.replace_lane 1 - i32x4.extract_lane 1 - ) - - (func $test_const (result i32) - v128.const i32x4 1 2 3 4 - i32x4.extract_lane 3 - ) - - (func $test_locals (local i32 v128) - local.get 0 - i32x4.splat - local.set 1 - ) - - (export "test_splat" (func $test_splat)) - (export "test_insert_lane" (func $test_insert_lane)) - (export "test_const" (func $test_const)) -) diff --git a/cranelift/wasm/wasmtests/table-copy.wat b/cranelift/wasm/wasmtests/table-copy.wat deleted file mode 100644 index dd9e1611e61a..000000000000 --- a/cranelift/wasm/wasmtests/table-copy.wat +++ /dev/null @@ -1,22 +0,0 @@ -(module $n - (table $t (import "m" "t") 6 funcref) - - (func $i (param i32 i32 i32 i32 i32 i32) (result i32) (local.get 3)) - (func $j (param i32 i32 i32 i32 i32 i32) (result i32) (local.get 4)) - (func $k (param i32 i32 i32 i32 i32 i32) (result i32) (local.get 5)) - - (table $u (export "u") funcref (elem $i $j $k $i $j $k)) - - (func (export "copy_to_t_from_u") (param i32 i32 i32 i32) (result i32) - local.get 0 - local.get 1 - local.get 2 - local.get 3 - table.copy $t $u) - - (func (export "copy_to_u_from_t") (param i32 i32 i32 i32) (result i32) - local.get 0 - local.get 1 - local.get 2 - local.get 3 - table.copy $u $t)) diff --git a/cranelift/wasm/wasmtests/unreachable_code.wat b/cranelift/wasm/wasmtests/unreachable_code.wat deleted file mode 100644 index 38c1a315cef9..000000000000 --- a/cranelift/wasm/wasmtests/unreachable_code.wat +++ /dev/null @@ -1,77 +0,0 @@ -(module - (type (;0;) (func (param i32 i64 f64) (result f64))) - (type (;1;) (func)) - (type (;2;) (func (result f32))) - (type (;3;) (func (result f64))) - (type (;4;) (func (param f64 f64) (result f64))) - (type (;5;) (func (result i32))) - (func (result i32) - block (result i32) - unreachable - end - block - end - i32.clz - ) - (func (result i32) - loop (result i32) - unreachable - end - block - end - i32.clz - ) - (func (;0;) (type 5) (result i32) - nop - block (result i32) ;; label = @1 - block ;; label = @2 - block ;; label = @3 - nop - block ;; label = @4 - i32.const 1 - if ;; label = @5 - nop - block ;; label = @6 - nop - nop - loop (result i32) ;; label = @7 - nop - block (result i32) ;; label = @8 - nop - nop - block (result i32) ;; label = @9 - nop - unreachable - end - end - end - block (result i32) ;; label = @7 - block ;; label = @8 - nop - end - i32.const 0 - end - br_if 5 (;@1;) - drop - end - else - nop - end - nop - end - end - end - unreachable - end) - (func - block (result i32) - block (result i32) - i32.const 1 - br 1 - end - end - drop - ) - (table (;0;) 16 anyfunc) - (elem (i32.const 0)) -) diff --git a/crates/asm-macros/Cargo.toml b/crates/asm-macros/Cargo.toml index 8b9d96cc9ae3..f04d759e05c2 100644 --- a/crates/asm-macros/Cargo.toml +++ b/crates/asm-macros/Cargo.toml @@ -2,6 +2,7 @@ authors = ["The Wasmtime Project Developers"] description = "Macros for defining asm functions in Wasmtime" edition.workspace = true +rust-version.workspace = true license = "Apache-2.0 WITH LLVM-exception" name = "wasmtime-asm-macros" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/crates/asm-macros/src/lib.rs b/crates/asm-macros/src/lib.rs index efd970d6bca0..0fd62e8b382f 100644 --- a/crates/asm-macros/src/lib.rs +++ b/crates/asm-macros/src/lib.rs @@ -6,6 +6,8 @@ //! function) and additionally handles visibility across platforms. All symbols //! should be visible to Rust but not visible externally outside of a `*.so`. +#![no_std] + cfg_if::cfg_if! { if #[cfg(target_os = "macos")] { #[macro_export] diff --git a/crates/bench-api/Cargo.toml b/crates/bench-api/Cargo.toml index 83cce9065cae..b8d36dd54999 100644 --- a/crates/bench-api/Cargo.toml +++ b/crates/bench-api/Cargo.toml @@ -7,8 +7,12 @@ license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" readme = "README.md" edition.workspace = true +rust-version.workspace = true publish = false +[lints] +workspace = true + [lib] name = "wasmtime_bench_api" crate-type = ["cdylib"] @@ -18,11 +22,12 @@ doctest = false anyhow = { workspace = true } shuffling-allocator = { version = "1.1.1", optional = true } target-lexicon = { workspace = true } -wasmtime = { workspace = true } -wasmtime-cli-flags = { workspace = true, default-features = true } -wasmtime-wasi = { workspace = true, default-features = true } +wasmtime = { workspace = true, default-features = true } +wasmtime-cli-flags = { workspace = true, default-features = true, features = [ + "cranelift", +] } +wasi-common = { workspace = true, default-features = true } wasmtime-wasi-nn = { workspace = true, optional = true } -wasi-cap-std-sync = { workspace = true } cap-std = { workspace = true } clap = { workspace = true } diff --git a/crates/bench-api/src/lib.rs b/crates/bench-api/src/lib.rs index a2ec33a9f5fd..6f4eea73da8a 100644 --- a/crates/bench-api/src/lib.rs +++ b/crates/bench-api/src/lib.rs @@ -142,9 +142,9 @@ use std::os::raw::{c_int, c_void}; use std::slice; use std::{env, path::PathBuf}; use target_lexicon::Triple; +use wasi_common::{sync::WasiCtxBuilder, I32Exit, WasiCtx}; use wasmtime::{Engine, Instance, Linker, Module, Store}; use wasmtime_cli_flags::CommonOptions; -use wasmtime_wasi::{sync::WasiCtxBuilder, I32Exit, WasiCtx}; pub type ExitCode = c_int; pub const OK: ExitCode = 0; @@ -304,20 +304,20 @@ pub extern "C" fn wasm_bench_create( let stdout = std::fs::File::create(&stdout_path) .with_context(|| format!("failed to create {}", stdout_path.display()))?; let stdout = cap_std::fs::File::from_std(stdout); - let stdout = wasi_cap_std_sync::file::File::from_cap_std(stdout); + let stdout = wasi_common::sync::file::File::from_cap_std(stdout); cx.stdout(Box::new(stdout)); let stderr = std::fs::File::create(&stderr_path) .with_context(|| format!("failed to create {}", stderr_path.display()))?; let stderr = cap_std::fs::File::from_std(stderr); - let stderr = wasi_cap_std_sync::file::File::from_cap_std(stderr); + let stderr = wasi_common::sync::file::File::from_cap_std(stderr); cx.stderr(Box::new(stderr)); if let Some(stdin_path) = &stdin_path { let stdin = std::fs::File::open(stdin_path) .with_context(|| format!("failed to open {}", stdin_path.display()))?; let stdin = cap_std::fs::File::from_std(stdin); - let stdin = wasi_cap_std_sync::file::File::from_cap_std(stdin); + let stdin = wasi_common::sync::file::File::from_cap_std(stdin); cx.stdin(Box::new(stdin)); } @@ -392,7 +392,7 @@ fn to_exit_code(result: impl Into>) -> ExitCode { match result.into() { Ok(_) => OK, Err(error) => { - eprintln!("{:?}", error); + eprintln!("{error:?}"); ERR } } @@ -418,7 +418,7 @@ struct BenchState { struct HostState { wasi: WasiCtx, #[cfg(feature = "wasi-nn")] - wasi_nn: wasmtime_wasi_nn::WasiNnCtx, + wasi_nn: wasmtime_wasi_nn::witx::WasiNnCtx, } impl BenchState { @@ -435,7 +435,7 @@ impl BenchState { execution_end: extern "C" fn(*mut u8), make_wasi_cx: impl FnMut() -> Result + 'static, ) -> Result { - let mut config = options.config(Some(&Triple::host().to_string()))?; + let mut config = options.config(Some(&Triple::host().to_string()), None)?; // NB: always disable the compilation cache. config.disable_cache(); let engine = Engine::new(&config)?; @@ -460,7 +460,7 @@ impl BenchState { let fuel = options.wasm.fuel; if options.wasi.common != Some(false) { - wasmtime_wasi::add_to_linker(&mut linker, |cx| &mut cx.wasi)?; + wasi_common::sync::add_to_linker(&mut linker, |cx| &mut cx.wasi)?; } #[cfg(feature = "wasi-nn")] @@ -509,7 +509,7 @@ impl BenchState { #[cfg(feature = "wasi-nn")] wasi_nn: { let (backends, registry) = wasmtime_wasi_nn::preload(&[])?; - wasmtime_wasi_nn::WasiNnCtx::new(backends, registry) + wasmtime_wasi_nn::witx::WasiNnCtx::new(backends, registry) }, }; @@ -522,7 +522,7 @@ impl BenchState { store.set_epoch_deadline(1); } if let Some(fuel) = self.fuel { - store.add_fuel(fuel).unwrap(); + store.set_fuel(fuel).unwrap(); } let instance = self.linker.instantiate(&mut store, &module)?; diff --git a/crates/c-api-macros/Cargo.toml b/crates/c-api-macros/Cargo.toml new file mode 100644 index 000000000000..99945a8ebdc6 --- /dev/null +++ b/crates/c-api-macros/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "wasmtime-c-api-macros" +version.workspace = true +authors = ["The Wasmtime Project Developers"] +license = "Apache-2.0 WITH LLVM-exception" +edition.workspace = true +rust-version.workspace = true +description = "Support macros for `wasmtime-c-api`" +repository = "https://github.com/bytecodealliance/wasmtime" + +[lints] +workspace = true + +[lib] +proc-macro = true +test = false +doctest = false + +[dependencies] +quote = "1.0" +proc-macro2 = "1.0" diff --git a/crates/c-api-macros/src/lib.rs b/crates/c-api-macros/src/lib.rs new file mode 100644 index 000000000000..4dbd9d599599 --- /dev/null +++ b/crates/c-api-macros/src/lib.rs @@ -0,0 +1,147 @@ +//! A set of convenience macros for our wasmtime-c-api crate. +//! +//! These are intended to mirror the macros in the `wasm.h` header file and +//! largely facilitate the `declare_ref` macro. + +use proc_macro2::{Ident, TokenStream, TokenTree}; +use quote::quote; + +fn extract_ident(input: proc_macro::TokenStream) -> Ident { + let input = TokenStream::from(input); + let i = match input.into_iter().next().unwrap() { + TokenTree::Ident(i) => i, + _ => panic!("expected an ident"), + }; + let name = i.to_string(); + assert!(name.ends_with("_t")); + return i; +} + +#[proc_macro] +pub fn declare_own(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ty = extract_ident(input); + let name = ty.to_string(); + let delete = quote::format_ident!("{}_delete", &name[..name.len() - 2]); + let docs = format!("Deletes the [`{name}`]."); + + (quote! { + #[doc = #docs] + #[no_mangle] + pub extern fn #delete(_: Box<#ty>) {} + }) + .into() +} + +#[proc_macro] +pub fn declare_ty(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ty = extract_ident(input); + let name = ty.to_string(); + let prefix = &name[..name.len() - 2]; + let copy = quote::format_ident!("{}_copy", &prefix); + let docs = format!( + "Creates a new [`{name}`] which matches the provided one.\n\n\ + The caller is responsible for deleting the returned value via [`{prefix}_delete`].\n\n\ + " + ); + + (quote! { + wasmtime_c_api_macros::declare_own!(#ty); + + #[doc = #docs] + #[no_mangle] + pub extern fn #copy(src: &#ty) -> Box<#ty> { + Box::new(src.clone()) + } + }) + .into() +} + +#[proc_macro] +pub fn declare_ref(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ty = extract_ident(input); + let name = ty.to_string(); + let prefix = &name[..name.len() - 2]; + let same = quote::format_ident!("{}_same", prefix); + let same_docs = format!( + "Returns `true` if the given references are pointing to the same [`{name}`].\n\n\ + This is not yet supported and aborts the process upon use." + ); + let get_host_info = quote::format_ident!("{}_get_host_info", prefix); + let get_host_info_docs = format!( + "Returns the host information of the [`{name}`].\n\n\ + This is not yet supported and always returns `NULL`." + ); + let set_host_info = quote::format_ident!("{}_set_host_info", prefix); + let set_host_info_docs = format!( + "Sets the host information of the [`{name}`].\n\n\ + This is not yet supported and aborts the process upon use." + ); + let set_host_info_final = quote::format_ident!("{}_set_host_info_with_finalizer", prefix); + let set_host_info_final_docs = format!( + "Sets the host information finalizer of the [`{name}`].\n\n\ + This is not yet supported and aborts the process upon use." + ); + let as_ref = quote::format_ident!("{}_as_ref", prefix); + let as_ref_docs = format!( + "Returns the [`{name}`] as mutable reference.\n\n\ + This is not yet supported and aborts the process upon use." + ); + let as_ref_const = quote::format_ident!("{}_as_ref_const", prefix); + let as_ref_const_docs = format!( + "Returns the [`{name}`] as immutable reference.\n\n\ + This is not yet supported and aborts the process upon use." + ); + + (quote! { + wasmtime_c_api_macros::declare_ty!(#ty); + + #[doc = #same_docs] + #[no_mangle] + pub extern fn #same(_a: &#ty, _b: &#ty) -> bool { + eprintln!("`{}` is not implemented", stringify!(#same)); + std::process::abort(); + } + + #[doc = #get_host_info_docs] + #[no_mangle] + pub extern fn #get_host_info(a: &#ty) -> *mut std::os::raw::c_void { + std::ptr::null_mut() + } + + #[doc = #set_host_info_docs] + #[no_mangle] + pub extern fn #set_host_info(a: &#ty, info: *mut std::os::raw::c_void) { + eprintln!("`{}` is not implemented", stringify!(#set_host_info)); + std::process::abort(); + } + + #[doc = #set_host_info_final_docs] + #[no_mangle] + pub extern fn #set_host_info_final( + a: &#ty, + info: *mut std::os::raw::c_void, + finalizer: Option, + ) { + eprintln!("`{}` is not implemented", stringify!(#set_host_info_final)); + std::process::abort(); + } + + #[doc = #as_ref_docs] + #[no_mangle] + pub extern fn #as_ref(a: &#ty) -> Box { + eprintln!("`{}` is not implemented", stringify!(#as_ref)); + std::process::abort(); + } + + #[doc = #as_ref_const_docs] + #[no_mangle] + pub extern fn #as_ref_const(a: &#ty) -> Box { + eprintln!("`{}` is not implemented", stringify!(#as_ref_const)); + std::process::abort(); + } + + // TODO: implement `wasm_ref_as_#name#` + // TODO: implement `wasm_ref_as_#name#_const` + }) + .into() +} diff --git a/crates/c-api/CMakeLists.txt b/crates/c-api/CMakeLists.txt index 37ec5761106e..09e8104a2710 100644 --- a/crates/c-api/CMakeLists.txt +++ b/crates/c-api/CMakeLists.txt @@ -1,139 +1,131 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.12) +project(wasmtime C) +set(WASMTIME_USER_CARGO_BUILD_OPTIONS "" CACHE STRING "Additional cargo flags (such as --features) to apply to the build command") option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) +option(WASMTIME_ALWAYS_BUILD "If cmake should always invoke cargo to build wasmtime" ON) +option(WASMTIME_FASTEST_RUNTIME "Set flags designed to optimize runtime performance" OFF) +set(WASMTIME_TARGET "" CACHE STRING "Rust target to build for") -if(CMAKE_BUILD_TYPE STREQUAL "Release") - set(WASMTIME_BUILD_TYPE_FLAG "--release") - set(WASMTIME_BUILD_TYPE "release") -else() - set(WASMTIME_BUILD_TYPE "debug") -endif() - -if(BUILD_SHARED_LIBS) - # Copy shared library into build directory - if(WIN32) - set(WASMTIME_INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_BUILD_TYPE}/wasmtime.dll - ${CMAKE_BINARY_DIR}) - elseif(APPLE) - set(WASMTIME_INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_BUILD_TYPE}/libwasmtime.dylib - ${CMAKE_BINARY_DIR}) - else() - set(WASMTIME_INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_BUILD_TYPE}/libwasmtime.so - ${CMAKE_BINARY_DIR}) - endif() +if(WASMTIME_TARGET STREQUAL "") + execute_process(COMMAND rustc -vV OUTPUT_VARIABLE RUSTC_VERSION) + string(REGEX MATCH "host: ([^ \n]*)" RUSTC_HOST ${RUSTC_VERSION}) + string(STRIP ${CMAKE_MATCH_1} RUSTC_HOST_TARGET) + set(WASMTIME_TARGET ${RUSTC_HOST_TARGET}) endif() -if(ANDROID) - # TODO wasmtime only supports arm64-v8a right now - if(ANDROID_ABI STREQUAL "armeabi-v7a") - set(ANDROID_TARGET "armv7-linux-androideabi") - set(ANDROID_ARCH_SHORT "arm") - elseif(ANDROID_ABI STREQUAL "arm64-v8a") - set(ANDROID_TARGET "aarch64-linux-android") - set(ANDROID_ARCH_SHORT "aarch64") - elseif(ANDROID_ABI STREQUAL "x86") - set(ANDROID_TARGET "i686-linux-android") - set(ANDROID_ARCH_SHORT "i386") - elseif(ANDROID_ABI STREQUAL "x86_64") - set(ANDROID_TARGET "x86_64-linux-android") - set(ANDROID_ARCH_SHORT "x86_64") - endif() +include(cmake/features.cmake) - set(WASMTIME_BUILD_TARGET "--target=${ANDROID_TARGET}") +if(WASMTIME_FASTEST_RUNTIME) + set(WASMTIME_BUILD_TYPE_FLAG "--profile=fastest-runtime") + set(WASMTIME_BUILD_TYPE "fastest-runtime") + set(CARGO_PROFILE_PANIC CARGO_PROFILE_RELEASE_PANIC) +else() + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(WASMTIME_BUILD_TYPE "debug") + set(CARGO_PROFILE_PANIC CARGO_PROFILE_DEBUG_PANIC) + else() + set(WASMTIME_BUILD_TYPE_FLAG "--release") + set(WASMTIME_BUILD_TYPE "release") + set(CARGO_PROFILE_PANIC CARGO_PROFILE_RELEASE_PANIC) + endif() endif() -if (BUILD_SHARED_LIBS AND ANDROID) - message(FATAL_ERROR "Wasmtime cannot be built with BUILD_SHARED_LIBS on Android") -endif() +set(WASMTIME_TARGET_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_TARGET}/${WASMTIME_BUILD_TYPE}) -if(BUILD_SHARED_LIBS) - if(WIN32) - set(WASMTIME_BUILD_PRODUCT - ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_BUILD_TYPE}/wasmtime.dll.lib) - elseif(APPLE) - set(WASMTIME_BUILD_PRODUCT - ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_BUILD_TYPE}/libwasmtime.dylib) - else() - set(WASMTIME_BUILD_PRODUCT - ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_BUILD_TYPE}/libwasmtime.so) - endif() +if(WASMTIME_TARGET MATCHES "darwin") + set(WASMTIME_SHARED_FILES libwasmtime.dylib) + set(WASMTIME_STATIC_FILES libwasmtime.a) +elseif(WASMTIME_TARGET MATCHES "windows-gnu") + set(WASMTIME_SHARED_FILES libwasmtime.dll.a wasmtime.dll) + set(WASMTIME_STATIC_FILES libwasmtime.a) +elseif(WASMTIME_TARGET MATCHES "windows-msvc") + set(WASMTIME_SHARED_FILES wasmtime.dll.lib wasmtime.dll) + set(WASMTIME_STATIC_FILES wasmtime.lib) else() - if(WIN32) - set(WASMTIME_BUILD_PRODUCT - ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_BUILD_TYPE}/wasmtime.lib) - elseif(ANDROID) - set(WASMTIME_BUILD_PRODUCT - ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${ANDROID_TARGET}/${WASMTIME_BUILD_TYPE}/libwasmtime.a) - else() - set(WASMTIME_BUILD_PRODUCT - ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMTIME_BUILD_TYPE}/libwasmtime.a) - endif() + set(WASMTIME_SHARED_FILES libwasmtime.so) + set(WASMTIME_STATIC_FILES libwasmtime.a) endif() -if(ANDROID) - # Rust attempts to use libgcc.a on NDK versions r23-beta3 and up - # but it has been replaced with libunwind.a (rust-lang/rust#85806) - file(WRITE ${CMAKE_BINARY_DIR}/libgcc.a "INPUT(-lunwind)") - # The version of the clang compiler is part of the libunwind.a path - file(STRINGS ${ANDROID_TOOLCHAIN_ROOT}/AndroidVersion.txt CLANG_VERSION_FILE) - list(GET CLANG_VERSION_FILE 0 CLANG_VERSION) +list(TRANSFORM WASMTIME_SHARED_FILES PREPEND ${WASMTIME_TARGET_DIR}/) +list(TRANSFORM WASMTIME_STATIC_FILES PREPEND ${WASMTIME_TARGET_DIR}/) - # Some crates use the compiler directly, environment variables - # are set to make them use the Android compiler - set(WASMTIME_PREBUILD_COMMAND ${CMAKE_COMMAND} -E env - CC=${ANDROID_TOOLCHAIN_ROOT}/bin/clang - AR=${ANDROID_TOOLCHAIN_ROOT}/bin/llvm-ar - "RUSTFLAGS=-L ${CMAKE_SYSROOT}/usr/lib/${ANDROID_TARGET}/${ANDROID_NATIVE_API_LEVEL} \ - -L ${ANDROID_TOOLCHAIN_ROOT}/lib64/clang/${CLANG_VERSION}/lib/linux/${ANDROID_ARCH_SHORT} \ - -L ${CMAKE_BINARY_DIR} -C linker=${ANDROID_TOOLCHAIN_ROOT}/bin/ld") -endif() include(ExternalProject) find_program(WASMTIME_CARGO_BINARY cargo) +if(NOT WASMTIME_CARGO_BINARY) + message(FATAL_ERROR [["cargo" was not found. Ensure "cargo" is in PATH. Aborting...]]) +endif() ExternalProject_Add( - wasmtime-crate - DOWNLOAD_COMMAND "" - CONFIGURE_COMMAND "" - INSTALL_COMMAND "${WASMTIME_INSTALL_COMMAND}" - BUILD_COMMAND ${WASMTIME_PREBUILD_COMMAND} ${WASMTIME_CARGO_BINARY} build ${WASMTIME_BUILD_TYPE_FLAG} ${WASMTIME_BUILD_TARGET} - BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR} - BUILD_ALWAYS ON - BUILD_BYPRODUCTS ${WASMTIME_BUILD_PRODUCT}) + wasmtime-crate + DOWNLOAD_COMMAND "" + CONFIGURE_COMMAND "" + INSTALL_COMMAND "${WASMTIME_INSTALL_COMMAND}" + BUILD_COMMAND + ${CMAKE_COMMAND} -E env ${CARGO_PROFILE_PANIC}=abort + ${WASMTIME_CARGO_BINARY} build + --target ${WASMTIME_TARGET} + --package wasmtime-c-api + ${WASMTIME_BUILD_TYPE_FLAG} + ${WASMTIME_FEATURES} + ${WASMTIME_USER_CARGO_BUILD_OPTIONS} + USES_TERMINAL_BUILD TRUE + # Note that this is used as the cwd for the cargo invocation itself, build + # byproducts go in the `target` directory at the top-level. + BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR} + BUILD_ALWAYS ${WASMTIME_ALWAYS_BUILD} + BUILD_BYPRODUCTS ${WASMTIME_SHARED_FILES} ${WASMTIME_STATIC_FILES}) add_library(wasmtime INTERFACE) add_dependencies(wasmtime wasmtime-crate) if(BUILD_SHARED_LIBS) - if(NOT WIN32) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'") - endif() - target_link_libraries(wasmtime INTERFACE ${WASMTIME_BUILD_PRODUCT}) + if(NOT WASMTIME_TARGET MATCHES "windows") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'") + endif() + list(GET WASMTIME_SHARED_FILES 0 WASMTIME_SHARED_LIB_TO_LINK) + target_link_libraries(wasmtime INTERFACE ${WASMTIME_SHARED_LIB_TO_LINK}) else() - if(WIN32) - target_compile_options(wasmtime INTERFACE -DWASM_API_EXTERN= -DWASI_API_EXTERN=) - target_link_libraries(wasmtime INTERFACE ${WASMTIME_BUILD_PRODUCT} - ws2_32 advapi32 userenv ntdll shell32 ole32 bcrypt) - elseif(APPLE OR ANDROID) - target_link_libraries(wasmtime INTERFACE ${WASMTIME_BUILD_PRODUCT}) - else() - target_link_libraries(wasmtime INTERFACE ${WASMTIME_BUILD_PRODUCT} - pthread dl m) - endif() + if(WASMTIME_TARGET MATCHES "windows") + target_compile_options(wasmtime INTERFACE -DWASM_API_EXTERN= -DWASI_API_EXTERN=) + target_link_libraries(wasmtime INTERFACE ${WASMTIME_STATIC_FILES} + ws2_32 advapi32 userenv ntdll shell32 ole32 bcrypt) + elseif(WASMTIME_TARGET MATCHES "darwin") + target_link_libraries(wasmtime INTERFACE ${WASMTIME_STATIC_FILES}) + else() + target_link_libraries(wasmtime INTERFACE ${WASMTIME_STATIC_FILES} + pthread dl m) + endif() endif() -target_include_directories(wasmtime INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/include) +target_include_directories(wasmtime INTERFACE ${CMAKE_BINARY_DIR}/include) +set(WASMTIME_HEADER_DST ${CMAKE_BINARY_DIR}/include) +include(cmake/install-headers.cmake) include(GNUInstallDirs) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/wasmtime.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/wasi.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/doc-wasm.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/include/wasm.h - ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/include/wasm.hh - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/wasmtime - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(FILES ${WASMTIME_BUILD_PRODUCT} - DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/cmake/install-headers.cmake) +install(FILES ${WASMTIME_SHARED_FILES} ${WASMTIME_STATIC_FILES} + DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +if(WASMTIME_TARGET MATCHES "darwin") + # Postprocess the macOS dylib a bit to have a more reasonable `LC_ID_DYLIB` + # directive than the default one that comes out of the linker when typically + # doing `cargo build`. For more info see #984 + set(INSTALLED_LIB ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libwasmtime.dylib) + install(CODE "execute_process(COMMAND install_name_tool -id \"@rpath/libwasmtime.dylib\" ${INSTALLED_LIB})") +endif() + +set(DOXYGEN_CONF_IN ${CMAKE_CURRENT_SOURCE_DIR}/doxygen.conf.in) +set(DOXYGEN_CONF_OUT ${CMAKE_BINARY_DIR}/doxygen.conf) +configure_file(${DOXYGEN_CONF_IN} ${DOXYGEN_CONF_OUT}) +add_custom_target(doc + COMMAND doxygen ${DOXYGEN_CONF_OUT} + DEPENDS ${WASMTIME_GENERATED_CONF_H} ${DOXYGEN_CONF_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +add_dependencies(doc headers-to-doc) + +file(GLOB headers "include/*.h") +add_custom_target(headers-to-doc + COMMAND + ${CMAKE_COMMAND} + -DWASMTIME_HEADER_DST=${CMAKE_BINARY_DIR}/include + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/install-headers.cmake + DEPENDS ${headers}) diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml index 47ec57d6c8ec..9b1abe546ebb 100644 --- a/crates/c-api/Cargo.toml +++ b/crates/c-api/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wasmtime-c-api" +name = "wasmtime-c-api-impl" version.workspace = true authors.workspace = true description = "C API to expose the Wasmtime runtime" @@ -7,38 +7,53 @@ license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" readme = "README.md" edition.workspace = true -publish = false +rust-version.workspace = true +links = "wasmtime-c-api" +include = ["include", "src", "build.rs", "CMakeLists.txt", "cmake", "doxygen.conf.in"] + +[lints] +workspace = true [lib] -name = "wasmtime" -crate-type = ["staticlib", "cdylib", "rlib"] -doc = false +name = "wasmtime_c_api" test = false doctest = false [dependencies] -env_logger = { workspace = true } +env_logger = { workspace = true, optional = true } anyhow = { workspace = true } once_cell = { workspace = true } -wasmtime = { workspace = true, features = ['cranelift'] } -wasmtime-c-api-macros = { path = "macros" } +wasmtime = { workspace = true, features = ['runtime', 'gc', 'std'] } +wasmtime-c-api-macros = { workspace = true } +log = { workspace = true } +tracing = { workspace = true } # Optional dependency for the `wat2wasm` API wat = { workspace = true, optional = true } # Optional dependencies for the `wasi` feature -wasi-cap-std-sync = { workspace = true, optional = true } -wasmtime-wasi = { workspace = true, default-features = true, optional = true } cap-std = { workspace = true, optional = true } -wasi-common = { workspace = true, optional = true } +tokio = { workspace = true, optional = true, features = ["fs"] } +wasmtime-wasi = { workspace = true, optional = true, features = ["preview1"] } # Optional dependencies for the `async` feature futures = { workspace = true, optional = true } [features] -default = ['jitdump', 'wat', 'wasi', 'cache', 'parallel-compilation', 'async'] +# WASMTIME_FEATURE_LIST async = ['wasmtime/async', 'futures'] -jitdump = ["wasmtime/jitdump"] +profiling = ["wasmtime/profiling"] cache = ["wasmtime/cache"] parallel-compilation = ['wasmtime/parallel-compilation'] -wasi = ['wasi-cap-std-sync', 'wasmtime-wasi', 'cap-std', 'wasi-common'] +wasi = ['cap-std', 'wasmtime-wasi', 'tokio'] +logging = ['dep:env_logger'] +disable-logging = ["log/max_level_off", "tracing/max_level_off"] +coredump = ["wasmtime/coredump"] +addr2line = ["wasmtime/addr2line"] +demangle = ["wasmtime/demangle"] +threads = ["wasmtime/threads"] +gc = ["wasmtime/gc"] +cranelift = ['wasmtime/cranelift'] +winch = ['wasmtime/winch'] +# ... if you add a line above this be sure to change the other locations +# marked WASMTIME_FEATURE_LIST diff --git a/crates/c-api/README.md b/crates/c-api/README.md index 5d7149dd18ac..7a858cab86e2 100644 --- a/crates/c-api/README.md +++ b/crates/c-api/README.md @@ -1,4 +1,68 @@ # Wasmtime's C API -For more information you can find the documentation for this library -[online](https://bytecodealliance.github.io/wasmtime/c-api/). +## API Documentation + +[The API documentation for the Wasmtime C library is hosted +here.](https://bytecodealliance.github.io/wasmtime/c-api/). + +## Using in a C Project + +### Using a Pre-Built Static or Dynamic Library + +Each release on Wasmtime's [GitHub Releases +page](https://github.com/bytecodealliance/wasmtime/releases) has pre-built +binaries for both static and dynamic libraries for a variety of architectures +and operating systems attached, as well as header files you can include. + +### Building Wasmtime's C API from Source + +To use Wasmtime from a C or C++ project, you must have +[CMake](https://cmake.org/) and [a Rust +toolchain](https://www.rust-lang.org/tools/install) installed. + +From the root of the Wasmtime repository, run the following commands: + +``` +$ cmake -S crates/c-api -B target/c-api --install-prefix "$(pwd)/artifacts" +$ cmake --build target/c-api +$ cmake --install target/c-api +``` + +These commands will produce the following files: + +* `artifacts/lib/libwasmtime.{a,lib}`: Static Wasmtime library. Exact extension + depends on your operating system. + +* `artifacts/lib/libwasmtime.{so,dylib,dll}`: Dynamic Wasmtime library. Exact + extension depends on your operating system. + +* `artifacts/include/**.h`: Header files for working with Wasmtime. + +## Using in a Rust Project + +If you have a Rust crate that contains bindings to a C or C++ library that uses Wasmtime, you can link the Wasmtime C API using Cargo. + +1. Add a dependency on the `wasmtime-c-api-impl` crate to your `Cargo.toml`. Note that package name differs from the library name. + +```toml +[dependencies] +wasmtime-c-api = { version = "16.0.0", package = "wasmtime-c-api-impl" } +``` + +2. In your `build.rs` file, when compiling your C/C++ source code, add the C `wasmtime-c-api` headers to the include path: + +```rust +fn main() { + let mut cfg = cc::Build::new(); + + // Add to the include path the wasmtime headers and the standard + // Wasm C API headers. + cfg + .include(std::env::var("DEP_WASMTIME_C_API_INCLUDE").unwrap()); + + // Compile your C code. + cfg + .file("src/your_c_code.c") + .compile("your_library"); +} +``` diff --git a/crates/c-api/artifact/Cargo.toml b/crates/c-api/artifact/Cargo.toml new file mode 100644 index 000000000000..d3f5e816e56a --- /dev/null +++ b/crates/c-api/artifact/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "wasmtime-c-api" +version.workspace = true +authors.workspace = true +description = "C API to expose the Wasmtime runtime" +license = "Apache-2.0 WITH LLVM-exception" +repository = "https://github.com/bytecodealliance/wasmtime" +readme = "README.md" +edition.workspace = true +rust-version.workspace = true +publish = false + +[lib] +name = "wasmtime" +crate-type = ["staticlib", "cdylib"] +doc = false +test = false +doctest = false + +[dependencies] +wasmtime-c-api = { path = '..', package = 'wasmtime-c-api-impl' } + +[features] +# WASMTIME_FEATURE_LIST +default = [ + 'profiling', + 'wat', + 'wasi', + 'cache', + 'parallel-compilation', + 'async', + 'coredump', + 'addr2line', + 'demangle', + 'threads', + 'gc', + 'cranelift', + 'winch', + # ... if you add a line above this be sure to change the other locations + # marked WASMTIME_FEATURE_LIST +] +async = ['wasmtime-c-api/async'] +profiling = ["wasmtime-c-api/profiling"] +cache = ["wasmtime-c-api/cache"] +parallel-compilation = ['wasmtime-c-api/parallel-compilation'] +wasi = ['wasmtime-c-api/wasi'] +logging = ['wasmtime-c-api/logging'] +disable-logging = ["wasmtime-c-api/disable-logging"] +coredump = ["wasmtime-c-api/coredump"] +addr2line = ["wasmtime-c-api/addr2line"] +demangle = ["wasmtime-c-api/demangle"] +wat = ["wasmtime-c-api/wat"] +threads = ["wasmtime-c-api/threads"] +gc = ["wasmtime-c-api/gc"] +cranelift = ["wasmtime-c-api/cranelift"] +winch = ["wasmtime-c-api/winch"] +# ... if you add a line above this be sure to read the comment at the end of +# `default` diff --git a/crates/c-api/artifact/src/lib.rs b/crates/c-api/artifact/src/lib.rs new file mode 100644 index 000000000000..43929bb8605f --- /dev/null +++ b/crates/c-api/artifact/src/lib.rs @@ -0,0 +1 @@ +pub use wasmtime_c_api::*; diff --git a/crates/c-api/build.rs b/crates/c-api/build.rs new file mode 100644 index 000000000000..6db41dec75cc --- /dev/null +++ b/crates/c-api/build.rs @@ -0,0 +1,45 @@ +use std::env; +use std::process::Command; + +// WASMTIME_FEATURE_LIST +const FEATURES: &[&str] = &[ + "ASYNC", + "PROFILING", + "CACHE", + "PARALLEL_COMPILATION", + "WASI", + "LOGGING", + "DISABLE_LOGGING", + "COREDUMP", + "ADDR2LINE", + "DEMANGLE", + "THREADS", + "GC", + "CRANELIFT", + "WINCH", +]; +// ... if you add a line above this be sure to change the other locations +// marked WASMTIME_FEATURE_LIST + +fn main() { + println!("cargo:rerun-if-changed=cmake/features.cmake"); + println!("cargo:rerun-if-changed=cmake/install-headers.cmake"); + println!("cargo:rerun-if-changed=include"); + + let out_dir = std::env::var("OUT_DIR").unwrap(); + let mut cmake = Command::new("cmake"); + cmake.arg("-DWASMTIME_DISABLE_ALL_FEATURES=ON"); + cmake.arg(format!("-DCMAKE_INSTALL_PREFIX={out_dir}")); + for f in FEATURES { + if env::var_os(format!("CARGO_FEATURE_{f}")).is_some() { + cmake.arg(format!("-DWASMTIME_FEATURE_{f}=ON")); + } + } + + cmake.arg("-P").arg("cmake/install-headers.cmake"); + + let status = cmake.status().expect("failed to spawn `cmake`"); + assert!(status.success()); + + println!("cargo:include={out_dir}/include"); +} diff --git a/crates/c-api/cmake/features.cmake b/crates/c-api/cmake/features.cmake new file mode 100644 index 000000000000..44ae4b9b5ec3 --- /dev/null +++ b/crates/c-api/cmake/features.cmake @@ -0,0 +1,45 @@ +set(WASMTIME_FEATURES "--no-default-features") + +option(WASMTIME_DISABLE_ALL_FEATURES + "disable all features by default instead of enabling them" + OFF) + +macro(feature rust_name default) + string(TOUPPER "wasmtime_feature_${rust_name}" cmake_name) + string(REPLACE "-" "_" cmake_name ${cmake_name}) + if(${default}) + if(${WASMTIME_DISABLE_ALL_FEATURES}) + set(feature_default OFF) + else() + set(feature_default ON) + endif() + else() + set(feature_default OFF) + endif() + + option(${cmake_name} "enable the Cargo feature ${rust_name}" ${feature_default}) + + if(${cmake_name}) + list(APPEND WASMTIME_FEATURES "--features=${rust_name}") + message(STATUS "Enabling feature ${rust_name}") + endif() +endmacro() + +# WASMTIME_FEATURE_LIST +feature(profiling ON) +feature(wat ON) +feature(cache ON) +feature(parallel-compilation ON) +feature(wasi ON) +feature(logging ON) +feature(disable-logging OFF) +feature(coredump ON) +feature(addr2line ON) +feature(demangle ON) +feature(threads ON) +feature(gc ON) +feature(async ON) +feature(cranelift ON) +feature(winch ON) +# ... if you add a line above this be sure to change the other locations +# marked WASMTIME_FEATURE_LIST diff --git a/crates/c-api/cmake/install-headers.cmake b/crates/c-api/cmake/install-headers.cmake new file mode 100644 index 000000000000..528e20823048 --- /dev/null +++ b/crates/c-api/cmake/install-headers.cmake @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.12) + +include(${CMAKE_CURRENT_LIST_DIR}/features.cmake) + +if(WASMTIME_HEADER_DST) + set(dst "${WASMTIME_HEADER_DST}") +else() + set(dst "${CMAKE_INSTALL_PREFIX}/include") +endif() +set(include_src "${CMAKE_CURRENT_LIST_DIR}/../include") + +message(STATUS "Installing: ${dst}/wasmtime/conf.h") +file(READ "${include_src}/wasmtime/conf.h.in" conf_h) +file(CONFIGURE OUTPUT "${dst}/wasmtime/conf.h" CONTENT "${conf_h}" + NEWLINE_STYLE CRLF) +file(INSTALL "${include_src}/" + DESTINATION "${dst}" + FILES_MATCHING REGEX "\\.hh?$") diff --git a/crates/c-api/doxygen.conf b/crates/c-api/doxygen.conf deleted file mode 100644 index 4e43ca4076de..000000000000 --- a/crates/c-api/doxygen.conf +++ /dev/null @@ -1,2583 +0,0 @@ -# Doxyfile 1.9.1 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the configuration -# file that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# https://www.gnu.org/software/libiconv/ for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = Wasmtime - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = - -# With the PROJECT_LOGO tag one can specify a logo or an icon that is included -# in the documentation. The maximum height of the logo should not exceed 55 -# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy -# the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = - -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII -# characters to appear in the names of generated files. If set to NO, non-ASCII -# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode -# U+3044. -# The default value is: NO. - -ALLOW_UNICODE_NAMES = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = NO - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line -# such as -# /*************** -# as being the beginning of a Javadoc-style comment "banner". If set to NO, the -# Javadoc-style will behave just like regular comments and it will not be -# interpreted by doxygen. -# The default value is: NO. - -JAVADOC_BANNER = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# By default Python docstrings are displayed as preformatted text and doxygen's -# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the -# doxygen's special commands can be used and the contents of the docstring -# documentation blocks is shown as doxygen documentation. -# The default value is: YES. - -PYTHON_DOCSTRING = YES - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new -# page for each member. If set to NO, the documentation of a member will be part -# of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice -# sources only. Doxygen will then generate output that is more tailored for that -# language. For instance, namespaces will be presented as modules, types will be -# separated into more groups, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_SLICE = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: -# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser -# tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files). For instance to make doxygen treat .inc files -# as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. -# -# Note: For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. When specifying no_extension you should add -# * to the FILE_PATTERNS. -# -# Note see also the list of default file extension mappings. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See https://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up -# to that level are automatically included in the table of contents, even if -# they do not have an id attribute. -# Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. -# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. - -TOC_INCLUDE_HEADINGS = 5 - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# If one adds a struct or class to a group and this option is enabled, then also -# any nested class or struct is added to the same group. By default this option -# is disabled and one has to add nested compounds explicitly via \ingroup. -# The default value is: NO. - -GROUP_NESTED_COMPOUNDS = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use -# during processing. When set to 0 doxygen will based this on the number of -# cores available in the system. You can set it explicitly to a value larger -# 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 -# encounter. Generating dot graphs in parallel is controlled by the -# DOT_NUM_THREADS setting. -# Minimum value: 0, maximum value: 32, default value: 1. - -NUM_PROC_THREADS = 1 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual -# methods of a class will be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIV_VIRTUAL = NO - -# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO, -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. If set to YES, local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO, only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If this flag is set to YES, the name of an unnamed parameter in a declaration -# will be determined by the corresponding definition. By default unnamed -# parameters remain unnamed in the output. -# The default value is: YES. - -RESOLVE_UNNAMED_PARAMS = YES - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# declarations. If set to NO, these declarations will be included in the -# documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO, these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# With the correct setting of option CASE_SENSE_NAMES doxygen will better be -# able to match the capabilities of the underlying filesystem. In case the -# filesystem is case sensitive (i.e. it supports files in the same directory -# whose names only differ in casing), the option must be set to YES to properly -# deal with such files in case they appear in the input. For filesystems that -# are not case sensitive the option should be be set to NO to properly deal with -# output files written for symbols that only differ in casing, such as for two -# classes, one named CLASS and the other named Class, and to also support -# references to files without having to specify the exact matching casing. On -# Windows (including Cygwin) and MacOS, users should typically set this option -# to NO, whereas on Linux or other Unix flavors it should typically be set to -# YES. -# The default value is: system dependent. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES, the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will -# append additional text to a page's title, such as Class Reference. If set to -# YES the compound reference will be hidden. -# The default value is: NO. - -HIDE_COMPOUND_REFERENCE= NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo -# list. This list is created by putting \todo commands in the documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test -# list. This list is created by putting \test commands in the documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES, the -# list will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. See also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS -# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but -# at the end of the doxygen process doxygen will return with a non-zero status. -# Possible values are: NO, YES and FAIL_ON_WARNINGS. -# The default value is: NO. - -WARN_AS_ERROR = YES - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING -# Note: If this tag is empty the current directory is searched. - -INPUT = include wasm-c-api/include - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: -# https://www.gnu.org/software/libiconv/) for the list of possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. -# -# Note the list of default checked file patterns might differ from the list of -# default file extension mappings. -# -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, -# *.ucf, *.qsf and *.ice. - -FILE_PATTERNS = *.h - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = assertions \ - WASM_DECLARE_* \ - WASM_API_EXTERN \ - WASI_API_EXTERN \ - WASMTIME_DECLARE_OWN \ - WASI_DECLARE_OWN \ - WASMTIME_CONFIG_PROP \ - own \ - wasm_*_enum - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = ../../examples - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = *.c - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = YES - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# entity all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see https://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefore more robust against future updates. -# Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra style sheet files is of importance (e.g. the last -# style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# https://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - -# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML -# documentation will contain a main index with vertical navigation menus that -# are dynamically created via JavaScript. If disabled, the navigation index will -# consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have JavaScript, -# like the Qt help browser. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_MENUS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: -# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To -# create a documentation set, doxygen will generate a Makefile in the HTML -# output directory. Running make will produce the docset in that directory and -# running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy -# genXcode/_index.html for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: -# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the main .chm file (NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated -# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it -# enables the Previous and Next buttons. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location (absolute path -# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to -# run qhelpgenerator on the generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = YES - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg -# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see -# https://inkscape.org) to generate formulas as SVG images instead of PNGs for -# the HTML output. These images will generally look nicer at scaled resolutions. -# Possible values are: png (the default) and svg (looks nicer but requires the -# pdf2svg or inkscape tool). -# The default value is: png. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FORMULA_FORMAT = png - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands -# to create new LaTeX commands to be used in formulas as building blocks. See -# the section "Including formulas" for details. - -FORMULA_MACROFILE = - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side JavaScript for the rendering -# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /
; + auto copy() const -> own
; + + auto type() const -> own; + auto get(size_t index) const -> own; + auto set(size_t index, const Ref*) -> bool; + auto size() const -> size_t; + auto grow(size_t delta, const Ref* init = nullptr) -> bool; +}; + + +// Memory Instances + +class WASM_API_EXTERN Memory : public Extern { + friend class destroyer; + void destroy(); + +protected: + Memory() = default; + ~Memory() = default; + +public: + static auto make(Store*, const MemoryType*) -> own; + auto copy() const -> own; + + using pages_t = uint32_t; + + static const size_t page_size = 0x10000; + + auto type() const -> own; + auto data() const -> byte_t*; + auto data_size() const -> size_t; + auto size() const -> pages_t; + auto grow(pages_t delta) -> bool; +}; + + +// Module Instances + +class WASM_API_EXTERN Instance : public Ref { + friend class destroyer; + void destroy(); + +protected: + Instance() = default; + ~Instance() = default; + +public: + static auto make( + Store*, const Module*, const vec&, own* = nullptr + ) -> own; + auto copy() const -> own; + + auto exports() const -> ownvec; +}; + + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace wasm + +#endif // #ifdef WASM_HH diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 32c0478e5753..0cbae0c9738f 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -57,7 +57,8 @@ * * * Linux - `-lpthread -ldl -lm` * * macOS - no extra flags needed - * * Windows - `ws2_32.lib advapi32.lib userenv.lib ntdll.lib shell32.lib ole32.lib bcrypt.lib` + * * Windows - `ws2_32.lib advapi32.lib userenv.lib ntdll.lib shell32.lib + * ole32.lib bcrypt.lib` * * ## Building from Source * @@ -89,7 +90,7 @@ * issue](https://github.com/bytecodealliance/wasmtime/issues/new). * * * [C embedding - * examples](https://bytecodealliance.github.io/wasmtime/examples-c-embed.html) + * examples](https://bytecodealliance.github.io/wasmtime/lang-c.html) * are available online and are tested from the Wasmtime repository itself. * * * [Contribution documentation for @@ -151,9 +152,7 @@ * provided access to it. For example in a host function created with * #wasmtime_func_new you can use #wasmtime_context_t in the host function * callback. This is because an argument, a #wasmtime_caller_t, provides access - * to #wasmtime_context_t. On the other hand a destructor passed to - * #wasmtime_externref_new, however, cannot use a #wasmtime_context_t because - * it was not provided access to one. Doing so may lead to memory unsafety. + * to #wasmtime_context_t. * * ### Stores * @@ -181,6 +180,9 @@ #define WASMTIME_API_H #include +#include +// clang-format off +// IWYU pragma: begin_exports #include #include #include @@ -191,20 +193,24 @@ #include #include #include +#include +#include #include #include #include #include #include +// IWYU pragma: end_exports +// clang-format on /** * \brief Wasmtime version string. */ -#define WASMTIME_VERSION "14.0.0" +#define WASMTIME_VERSION "25.0.0" /** * \brief Wasmtime major version number. */ -#define WASMTIME_VERSION_MAJOR 14 +#define WASMTIME_VERSION_MAJOR 25 /** * \brief Wasmtime minor version number. */ @@ -218,14 +224,16 @@ extern "C" { #endif +#ifdef WASMTIME_FEATURE_WAT + /** - * \brief Converts from the text format of WebAssembly to to the binary format. + * \brief Converts from the text format of WebAssembly to the binary format. * - * \param wat this it the input pointer with the WebAssembly Text Format inside of - * it. This will be parsed and converted to the binary format. + * \param wat this it the input pointer with the WebAssembly Text Format inside + * of it. This will be parsed and converted to the binary format. * \param wat_len this it the length of `wat`, in bytes. - * \param ret if the conversion is successful, this byte vector is filled in with - * the WebAssembly binary format. + * \param ret if the conversion is successful, this byte vector is filled in + * with the WebAssembly binary format. * * \return a non-null error if parsing fails, or returns `NULL`. If parsing * fails then `ret` isn't touched. @@ -233,14 +241,13 @@ extern "C" { * This function does not take ownership of `wat`, and the caller is expected to * deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_wat2wasm( - const char *wat, - size_t wat_len, - wasm_byte_vec_t *ret -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_wat2wasm(const char *wat, size_t wat_len, wasm_byte_vec_t *ret); + +#endif #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_API_H diff --git a/crates/c-api/include/wasmtime/async.h b/crates/c-api/include/wasmtime/async.h index dbe4312dc118..c0f6a14b4fd6 100644 --- a/crates/c-api/include/wasmtime/async.h +++ b/crates/c-api/include/wasmtime/async.h @@ -6,23 +6,27 @@ * Async functionality in Wasmtime is well documented here: * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support * - * All WebAssembly executes synchronously, but an async support enables the Wasm code - * be executed on a seperate stack, so it can be paused and resumed. There are three - * mechanisms for yielding control from wasm to the caller: fuel, epochs, and async host functions. - * - * When WebAssembly is executed, a #wasmtime_call_future_t is returned. This struct represents the - * state of the execution and each call to #wasmtime_call_future_poll will execute the WebAssembly - * code on a seperate stack until the function returns or yields control back to the caller. - * - * It's expected these futures are pulled in a loop until completed, at which point the future - * should be deleted. Functions that return a #wasmtime_call_future_t are special in that all - * parameters to that function should not be modified in any way and must be kept alive until - * the future is deleted. This includes concurrent calls for a single store - another function - * on a store should not be called while there is a #wasmtime_call_future_t alive. - * - * As for asynchronous host calls - the reverse contract is upheld. Wasmtime will keep all parameters - * to the function alive and unmodified until the #wasmtime_func_async_continuation_callback_t returns - * true. + * All WebAssembly executes synchronously, but an async support enables the Wasm + * code be executed on a separate stack, so it can be paused and resumed. There + * are three mechanisms for yielding control from wasm to the caller: fuel, + * epochs, and async host functions. + * + * When WebAssembly is executed, a `wasmtime_call_future_t` is returned. This + * struct represents the state of the execution and each call to + * `wasmtime_call_future_poll` will execute the WebAssembly code on a separate + * stack until the function returns or yields control back to the caller. + * + * It's expected these futures are pulled in a loop until completed, at which + * point the future should be deleted. Functions that return a + * `wasmtime_call_future_t` are special in that all parameters to that function + * should not be modified in any way and must be kept alive until the future is + * deleted. This includes concurrent calls for a single store - another function + * on a store should not be called while there is a `wasmtime_call_future_t` + * alive. + * + * As for asynchronous host calls - the reverse contract is upheld. Wasmtime + * will keep all parameters to the function alive and unmodified until the + * `wasmtime_func_async_continuation_callback_t` returns true. * */ @@ -30,22 +34,27 @@ #define WASMTIME_ASYNC_H #include -#include +#include #include -#include +#include #include #include +#include + +#ifdef WASMTIME_FEATURE_ASYNC #ifdef __cplusplus extern "C" { #endif /** - * \brief Whether or not to enable support for asynchronous functions in Wasmtime. + * \brief Whether or not to enable support for asynchronous functions in + * Wasmtime. * - * When enabled, the config can optionally define host functions with async. - * Instances created and functions called with this Config must be called through their asynchronous APIs, however. - * For example using wasmtime_func_call will panic when used with this config. + * When enabled, the config can optionally define host functions with async. + * Instances created and functions called with this Config must be called + * through their asynchronous APIs, however. For example using + * wasmtime_func_call will panic when used with this config. * * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support @@ -55,13 +64,15 @@ WASMTIME_CONFIG_PROP(void, async_support, bool) /** * \brief Configures the size of the stacks used for asynchronous execution. * - * This setting configures the size of the stacks that are allocated for asynchronous execution. + * This setting configures the size of the stacks that are allocated for + * asynchronous execution. * * The value cannot be less than max_wasm_stack. * - * The amount of stack space guaranteed for host functions is async_stack_size - max_wasm_stack, so take care - * not to set these two values close to one another; doing so may cause host functions to overflow the stack - * and abort the process. + * The amount of stack space guaranteed for host functions is async_stack_size - + * max_wasm_stack, so take care not to set these two values close to one + * another; doing so may cause host functions to overflow the stack and abort + * the process. * * By default this option is 2 MiB. * @@ -71,37 +82,43 @@ WASMTIME_CONFIG_PROP(void, async_support, bool) WASMTIME_CONFIG_PROP(void, async_stack_size, uint64_t) /** - * \brief Configures a Store to yield execution of async WebAssembly code periodically. + * \brief Configures a Store to yield execution of async WebAssembly code + * periodically. + * + * When a Store is configured to consume fuel with + * `wasmtime_config_consume_fuel` this method will configure what happens when + * fuel runs out. Specifically executing WebAssembly will be suspended and + * control will be yielded back to the caller. * - * When a Store is configured to consume fuel with `wasmtime_config_consume_fuel` - * this method will configure what happens when fuel runs out. Specifically executing - * WebAssembly will be suspended and control will be yielded back to the caller. + * This is only suitable with use of a store associated with an async config + * because only then are futures used and yields are possible. * - * This is only suitable with use of a store associated with an async config because - * only then are futures used and yields are possible. + * \param context the context for the store to configure. + * \param interval the amount of fuel at which to yield. A value of 0 will + * disable yielding. */ -WASM_API_EXTERN void wasmtime_context_out_of_fuel_async_yield( - wasmtime_context_t *context, - uint64_t injection_count, - uint64_t fuel_to_inject); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_context_fuel_async_yield_interval(wasmtime_context_t *context, + uint64_t interval); /** - * \brief Configures epoch-deadline expiration to yield to the async caller and the update the deadline. + * \brief Configures epoch-deadline expiration to yield to the async caller and + * the update the deadline. * - * This is only suitable with use of a store associated with an async config because - * only then are futures used and yields are possible. + * This is only suitable with use of a store associated with an async config + * because only then are futures used and yields are possible. * * See the Rust documentation for more: * https://docs.wasmtime.dev/api/wasmtime/struct.Store.html#method.epoch_deadline_async_yield_and_update */ -WASM_API_EXTERN void wasmtime_context_epoch_deadline_async_yield_and_update( - wasmtime_context_t *context, - uint64_t delta); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_context_epoch_deadline_async_yield_and_update( + wasmtime_context_t *context, uint64_t delta); /** * The callback to determine a continuation's current state. * - * Return true if the host call has completed, otherwise false will + * Return true if the host call has completed, otherwise false will * continue to yield WebAssembly execution. */ typedef bool (*wasmtime_func_async_continuation_callback_t)(void *env); @@ -121,59 +138,58 @@ typedef struct wasmtime_async_continuation_t { /** * \brief Callback signature for #wasmtime_linker_define_async_func. * - * This is a host function that returns a continuation to be called later. + * This is a host function that returns a continuation to be called later. * * All the arguments to this function will be kept alive until the continuation * returns that it has errored or has completed. * - * \param env user-provided argument passed to #wasmtime_linker_define_async_func + * \param env user-provided argument passed to + * #wasmtime_linker_define_async_func * \param caller a temporary object that can only be used during this function - * call. Used to acquire #wasmtime_context_t or caller's state + * call. Used to acquire #wasmtime_context_t or caller's state * \param args the arguments provided to this function invocation * \param nargs how many arguments are provided * \param results where to write the results of this function * \param nresults how many results must be produced - * \param trap_ret if assigned a not `NULL` value then the called function will - * trap with the returned error. Note that ownership of trap is transferred - * to wasmtime. - * \param continuation_ret the returned continuation that determines when the - * async function has completed executing. + * \param trap_ret if assigned a not `NULL` value then the called + * function will trap with the returned error. Note that ownership of + * trap is transferred to wasmtime. + * \param continuation_ret the returned continuation + * that determines when the async function has completed executing. * * Only supported for async stores. * * See #wasmtime_func_callback_t for more information. */ typedef void (*wasmtime_func_async_callback_t)( - void *env, - wasmtime_caller_t *caller, - const wasmtime_val_t *args, - size_t nargs, - wasmtime_val_t *results, - size_t nresults, - wasm_trap_t** trap_ret, - wasmtime_async_continuation_t * continuation_ret); + void *env, wasmtime_caller_t *caller, const wasmtime_val_t *args, + size_t nargs, wasmtime_val_t *results, size_t nresults, + wasm_trap_t **trap_ret, wasmtime_async_continuation_t *continuation_ret); /** * \brief The structure representing a asynchronously running function. * - * This structure is always owned by the caller and must be deleted using #wasmtime_call_future_delete. + * This structure is always owned by the caller and must be deleted using + * #wasmtime_call_future_delete. * - * Functions that return this type require that the parameters to the function are unmodified until - * this future is destroyed. + * Functions that return this type require that the parameters to the function + * are unmodified until this future is destroyed. */ typedef struct wasmtime_call_future wasmtime_call_future_t; /** * \brief Executes WebAssembly in the function. * - * Returns true if the function call has completed. After this function returns true, it should *not* be - * called again for a given future. + * Returns true if the function call has completed. After this function returns + * true, it should *not* be called again for a given future. * - * This function returns false if execution has yielded either due to being out of fuel - * (see wasmtime_store_out_of_fuel_async_yield), or the epoch has been incremented enough - * (see wasmtime_store_epoch_deadline_async_yield_and_update). The function may also return false if - * asynchronous host functions have been called, which then calling this function will call the - * continuation from the async host function. + * This function returns false if execution has yielded either due to being out + * of fuel (see wasmtime_context_fuel_async_yield_interval), or the epoch has + * been incremented enough (see + * wasmtime_context_epoch_deadline_async_yield_and_update). The function may + * also return false if asynchronous host functions have been called, which then + * calling this function will call the continuation from the async host + * function. * * For more see the information at * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#asynchronous-wasm @@ -184,86 +200,83 @@ WASM_API_EXTERN bool wasmtime_call_future_poll(wasmtime_call_future_t *future); /** * /brief Frees the underlying memory for a future. * - * All wasmtime_call_future_t are owned by the caller and should be deleted using this function. + * All wasmtime_call_future_t are owned by the caller and should be deleted + * using this function. */ -WASM_API_EXTERN void wasmtime_call_future_delete(wasmtime_call_future_t *future); +WASM_API_EXTERN void +wasmtime_call_future_delete(wasmtime_call_future_t *future); /** - * \brief Invokes this function with the params given, returning the results asynchronously. + * \brief Invokes this function with the params given, returning the results + * asynchronously. * - * This function is the same as wasmtime_func_call except that it is asynchronous. - * This is only compatible with stores associated with an asynchronous config. + * This function is the same as wasmtime_func_call except that it is + * asynchronous. This is only compatible with stores associated with an + * asynchronous config. * - * The result is a future that is owned by the caller and must be deleted via #wasmtime_call_future_delete. + * The result is a future that is owned by the caller and must be deleted via + * #wasmtime_call_future_delete. * - * The `args` and `results` pointers may be `NULL` if the corresponding length is zero. - * The `trap_ret` and `error_ret` pointers may *not* be `NULL`. + * The `args` and `results` pointers may be `NULL` if the corresponding length + * is zero. The `trap_ret` and `error_ret` pointers may *not* be `NULL`. * - * Does not take ownership of #wasmtime_val_t arguments or #wasmtime_val_t results, - * and all parameters to this function must be kept alive and not modified until the - * returned #wasmtime_call_future_t is deleted. This includes the context and store - * parameters. Only a single future can be alive for a given store at a single time - * (meaning only call this function after the previous call's future was deleted). + * Does not take ownership of #wasmtime_val_t arguments or #wasmtime_val_t + * results, and all parameters to this function must be kept alive and not + * modified until the returned #wasmtime_call_future_t is deleted. This includes + * the context and store parameters. Only a single future can be alive for a + * given store at a single time (meaning only call this function after the + * previous call's future was deleted). * * See the header documentation for for more information. * * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/struct.Func.html#method.call_async */ -WASM_API_EXTERN wasmtime_call_future_t* wasmtime_func_call_async( - wasmtime_context_t *context, - const wasmtime_func_t *func, - const wasmtime_val_t *args, - size_t nargs, - wasmtime_val_t *results, - size_t nresults, - wasm_trap_t** trap_ret, - wasmtime_error_t** error_ret); +WASM_API_EXTERN wasmtime_call_future_t *wasmtime_func_call_async( + wasmtime_context_t *context, const wasmtime_func_t *func, + const wasmtime_val_t *args, size_t nargs, wasmtime_val_t *results, + size_t nresults, wasm_trap_t **trap_ret, wasmtime_error_t **error_ret); /** * \brief Defines a new async function in this linker. * - * This function behaves similar to #wasmtime_linker_define_func, except it supports async - * callbacks. + * This function behaves similar to #wasmtime_linker_define_func, except it + * supports async callbacks. * * The callback `cb` will be invoked on another stack (fiber for Windows). */ WASM_API_EXTERN wasmtime_error_t *wasmtime_linker_define_async_func( - wasmtime_linker_t *linker, - const char *module, - size_t module_len, - const char *name, - size_t name_len, - const wasm_functype_t *ty, - wasmtime_func_async_callback_t cb, - void *data, - void (*finalizer)(void *)); + wasmtime_linker_t *linker, const char *module, size_t module_len, + const char *name, size_t name_len, const wasm_functype_t *ty, + wasmtime_func_async_callback_t cb, void *data, void (*finalizer)(void *)); /** - * \brief Instantiates a #wasm_module_t with the items defined in this linker for an async store. + * \brief Instantiates a #wasm_module_t with the items defined in this linker + * for an async store. * - * This is the same as #wasmtime_linker_instantiate but used for async stores - * (which requires functions are called asynchronously). The returning #wasmtime_call_future_t - * must be polled using #wasmtime_call_future_poll, and is owned and must be deleted using #wasmtime_call_future_delete. + * This is the same as #wasmtime_linker_instantiate but used for async stores + * (which requires functions are called asynchronously). The returning + * #wasmtime_call_future_t must be polled using #wasmtime_call_future_poll, and + * is owned and must be deleted using #wasmtime_call_future_delete. * - * The `trap_ret` and `error_ret` pointers may *not* be `NULL` and the returned memory is owned by the caller. + * The `trap_ret` and `error_ret` pointers may *not* be `NULL` and the returned + * memory is owned by the caller. * - * All arguments to this function must outlive the returned future and be unmodified until the future is deleted. + * All arguments to this function must outlive the returned future and be + * unmodified until the future is deleted. */ WASM_API_EXTERN wasmtime_call_future_t *wasmtime_linker_instantiate_async( - const wasmtime_linker_t *linker, - wasmtime_context_t *store, - const wasmtime_module_t *module, - wasmtime_instance_t *instance, - wasm_trap_t** trap_ret, - wasmtime_error_t** error_ret); + const wasmtime_linker_t *linker, wasmtime_context_t *store, + const wasmtime_module_t *module, wasmtime_instance_t *instance, + wasm_trap_t **trap_ret, wasmtime_error_t **error_ret); /** * \brief Instantiates instance within the given store. * * This will also run the function's startup function, if there is one. * - * For more information on async instantiation see #wasmtime_linker_instantiate_async. + * For more information on async instantiation see + * #wasmtime_linker_instantiate_async. * * \param instance_pre the pre-initialized instance * \param store the store in which to create the instance @@ -271,20 +284,91 @@ WASM_API_EXTERN wasmtime_call_future_t *wasmtime_linker_instantiate_async( * \param trap_ret where to store the returned trap * \param error_ret where to store the returned trap * - * The `trap_ret` and `error_ret` pointers may *not* be `NULL` and the returned memory is owned by the caller. + * The `trap_ret` and `error_ret` pointers may *not* be `NULL` and the returned + * memory is owned by the caller. * - * All arguments to this function must outlive the returned future and be unmodified until the future is deleted. + * All arguments to this function must outlive the returned future and be + * unmodified until the future is deleted. */ WASM_API_EXTERN wasmtime_call_future_t *wasmtime_instance_pre_instantiate_async( - const wasmtime_instance_pre_t* instance_pre, - wasmtime_context_t *store, - wasmtime_instance_t *instance, - wasm_trap_t** trap_ret, - wasmtime_error_t** error_ret); + const wasmtime_instance_pre_t *instance_pre, wasmtime_context_t *store, + wasmtime_instance_t *instance, wasm_trap_t **trap_ret, + wasmtime_error_t **error_ret); + +/** + * A callback to get the top of the stack address and the length of the stack, + * excluding guard pages. + * + * For more information about the parameters see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.StackMemory.html + */ +typedef uint8_t *(*wasmtime_stack_memory_get_callback_t)(void *env, + size_t *out_len); + +/** + * A Stack instance created from a #wasmtime_new_stack_memory_callback_t. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.StackMemory.html + */ +typedef struct { + /// User provided value to be passed to get_memory and grow_memory + void *env; + /// Callback to get the memory and size of this LinearMemory + wasmtime_stack_memory_get_callback_t get_stack_memory; + /// An optional finalizer for env + void (*finalizer)(void *); +} wasmtime_stack_memory_t; + +/** + * A callback to create a new StackMemory from the specified parameters. + * + * The result should be written to `stack_ret` and wasmtime will own the values + * written into that struct. + * + * This callback must be thread-safe. + * + * For more information about the parameters see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.StackCreator.html#tymethod.new_stack + */ +typedef wasmtime_error_t *(*wasmtime_new_stack_memory_callback_t)( + void *env, size_t size, wasmtime_stack_memory_t *stack_ret); + +/** + * A representation of custom stack creator. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.StackCreator.html + */ +typedef struct { + /// User provided value to be passed to new_stack + void *env; + /// The callback to create a new stack, must be thread safe + wasmtime_new_stack_memory_callback_t new_stack; + /// An optional finalizer for env. + void (*finalizer)(void *); +} wasmtime_stack_creator_t; + +/** + * Sets a custom stack creator. + * + * Custom memory creators are used when creating creating async instance stacks + * for the on-demand instance allocation strategy. + * + * The config does **not** take ownership of the #wasmtime_stack_creator_t + * passed in, but instead copies all the values in the struct. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.with_host_stack + */ +WASM_API_EXTERN void +wasmtime_config_host_stack_creator_set(wasm_config_t *, + wasmtime_stack_creator_t *); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif -#endif // WASMTIME_ASYNC_H +#endif // WASMTIME_FEATURE_ASYNC +#endif // WASMTIME_ASYNC_H diff --git a/crates/c-api/include/wasmtime/conf.h.in b/crates/c-api/include/wasmtime/conf.h.in new file mode 100644 index 000000000000..c04cfca886ad --- /dev/null +++ b/crates/c-api/include/wasmtime/conf.h.in @@ -0,0 +1,33 @@ +/** + * \file wasmtime/conf.h + * + * \brief Build-time defines for how the C API was built. + */ + +#ifndef WASMTIME_CONF_H +#define WASMTIME_CONF_H + +// WASMTIME_FEATURE_LIST +#cmakedefine WASMTIME_FEATURE_PROFILING +#cmakedefine WASMTIME_FEATURE_WAT +#cmakedefine WASMTIME_FEATURE_CACHE +#cmakedefine WASMTIME_FEATURE_PARALLEL_COMPILATION +#cmakedefine WASMTIME_FEATURE_WASI +#cmakedefine WASMTIME_FEATURE_LOGGING +#cmakedefine WASMTIME_FEATURE_DISABLE_LOGGING +#cmakedefine WASMTIME_FEATURE_COREDUMP +#cmakedefine WASMTIME_FEATURE_ADDR2LINE +#cmakedefine WASMTIME_FEATURE_DEMANGLE +#cmakedefine WASMTIME_FEATURE_THREADS +#cmakedefine WASMTIME_FEATURE_GC +#cmakedefine WASMTIME_FEATURE_ASYNC +#cmakedefine WASMTIME_FEATURE_CRANELIFT +#cmakedefine WASMTIME_FEATURE_WINCH +// ... if you add a line above this be sure to change the other locations +// marked WASMTIME_FEATURE_LIST + +#if defined(WASMTIME_FEATURE_CRANELIFT) || defined(WASMTIME_FEATURE_WINCH) +#define WASMTIME_FEATURE_COMPILER +#endif + +#endif // WASMTIME_CONF_H diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h index 38f1edc1b504..0cd970ccc9b3 100644 --- a/crates/c-api/include/wasmtime/config.h +++ b/crates/c-api/include/wasmtime/config.h @@ -8,6 +8,7 @@ #define WASMTIME_CONFIG_H #include +#include #include #ifdef __cplusplus @@ -86,8 +87,8 @@ enum wasmtime_profiling_strategy_enum { // ProfilingStrategy WASMTIME_PROFILING_STRATEGY_PERFMAP, }; -#define WASMTIME_CONFIG_PROP(ret, name, ty) \ - WASM_API_EXTERN ret wasmtime_config_##name##_set(wasm_config_t*, ty); +#define WASMTIME_CONFIG_PROP(ret, name, ty) \ + WASM_API_EXTERN ret wasmtime_config_##name##_set(wasm_config_t *, ty); /** * \brief Configures whether DWARF debug information is constructed at runtime @@ -140,6 +141,8 @@ WASMTIME_CONFIG_PROP(void, epoch_interruption, bool) */ WASMTIME_CONFIG_PROP(void, max_wasm_stack, size_t) +#ifdef WASMTIME_FEATURE_THREADS + /** * \brief Configures whether the WebAssembly threading proposal is enabled. * @@ -149,6 +152,15 @@ WASMTIME_CONFIG_PROP(void, max_wasm_stack, size_t) */ WASMTIME_CONFIG_PROP(void, wasm_threads, bool) +#endif // WASMTIME_FEATURE_THREADS + +/** + * \brief Configures whether the WebAssembly tail call proposal is enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_tail_call, bool) + /** * \brief Configures whether the WebAssembly reference types proposal is * enabled. @@ -157,6 +169,21 @@ WASMTIME_CONFIG_PROP(void, wasm_threads, bool) */ WASMTIME_CONFIG_PROP(void, wasm_reference_types, bool) +/** + * \brief Configures whether the WebAssembly typed function reference types + * proposal is enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_function_references, bool) + +/** + * \brief Configures whether the WebAssembly GC proposal is enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_gc, bool) + /** * \brief Configures whether the WebAssembly SIMD proposal is * enabled. @@ -213,6 +240,8 @@ WASMTIME_CONFIG_PROP(void, wasm_multi_memory, bool) */ WASMTIME_CONFIG_PROP(void, wasm_memory64, bool) +#ifdef WASMTIME_FEATURE_COMPILER + /** * \brief Configures how JIT code will be compiled. * @@ -220,14 +249,23 @@ WASMTIME_CONFIG_PROP(void, wasm_memory64, bool) */ WASMTIME_CONFIG_PROP(void, strategy, wasmtime_strategy_t) +#endif // WASMTIME_FEATURE_COMPILER + +#ifdef WASMTIME_FEATURE_PARALLEL_COMPILATION + /** - * \brief Configure whether wasmtime should compile a module using multiple threads. + * \brief Configure whether wasmtime should compile a module using multiple + * threads. * * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.parallel_compilation. */ WASMTIME_CONFIG_PROP(void, parallel_compilation, bool) +#endif // WASMTIME_FEATURE_PARALLEL_COMPILATION + +#ifdef WASMTIME_FEATURE_COMPILER + /** * \brief Configures whether Cranelift's debug verifier is enabled. * @@ -239,13 +277,15 @@ WASMTIME_CONFIG_PROP(void, parallel_compilation, bool) WASMTIME_CONFIG_PROP(void, cranelift_debug_verifier, bool) /** - * \brief Configures whether Cranelift should perform a NaN-canonicalization pass. + * \brief Configures whether Cranelift should perform a NaN-canonicalization + * pass. * * When Cranelift is used as a code generation backend this will configure * it to replace NaNs with a single canonical value. This is useful for users * requiring entirely deterministic WebAssembly computation. * - * This is not required by the WebAssembly spec, so it is not enabled by default. + * This is not required by the WebAssembly spec, so it is not enabled by + * default. * * The default value for this is `false` */ @@ -258,6 +298,8 @@ WASMTIME_CONFIG_PROP(void, cranelift_nan_canonicalization, bool) */ WASMTIME_CONFIG_PROP(void, cranelift_opt_level, wasmtime_opt_level_t) +#endif // WASMTIME_FEATURE_COMPILER + /** * \brief Configures the profiling strategy used for JIT code. * @@ -300,7 +342,8 @@ WASMTIME_CONFIG_PROP(void, static_memory_guard_size, uint64_t) WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t) /** - * \brief Configures the size, in bytes, of the extra virtual memory space reserved after a “dynamic” memory for growing into. + * \brief Configures the size, in bytes, of the extra virtual memory space + * reserved after a “dynamic” memory for growing into. * * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.dynamic_memory_reserved_for_growth @@ -308,7 +351,8 @@ WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t) WASMTIME_CONFIG_PROP(void, dynamic_memory_reserved_for_growth, uint64_t) /** - * \brief Configures whether to generate native unwind information (e.g. .eh_frame on Linux). + * \brief Configures whether to generate native unwind information (e.g. + * .eh_frame on Linux). * * This option defaults to true. * @@ -317,6 +361,8 @@ WASMTIME_CONFIG_PROP(void, dynamic_memory_reserved_for_growth, uint64_t) */ WASMTIME_CONFIG_PROP(void, native_unwind_info, bool) +#ifdef WASMTIME_FEATURE_CACHE + /** * \brief Enables Wasmtime's cache and loads configuration from the specified * path. @@ -329,7 +375,12 @@ WASMTIME_CONFIG_PROP(void, native_unwind_info, bool) * An error is returned if the cache configuration could not be loaded or if the * cache could not be enabled. */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_config_cache_config_load(wasm_config_t*, const char*); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_config_cache_config_load(wasm_config_t *, const char *); + +#endif // WASMTIME_FEATURE_CACHE + +#ifdef WASMTIME_FEATURE_COMPILER /** * \brief Configures the target triple that this configuration will produce @@ -343,7 +394,7 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_config_cache_config_load(wasm_config_ * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.config */ -WASMTIME_CONFIG_PROP(wasmtime_error_t*, target, const char*) +WASMTIME_CONFIG_PROP(wasmtime_error_t *, target, const char *) /** * \brief Enables a target-specific flag in Cranelift. @@ -354,7 +405,8 @@ WASMTIME_CONFIG_PROP(wasmtime_error_t*, target, const char*) * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_enable */ -WASM_API_EXTERN void wasmtime_config_cranelift_flag_enable(wasm_config_t*, const char*); +WASM_API_EXTERN void wasmtime_config_cranelift_flag_enable(wasm_config_t *, + const char *); /** * \brief Sets a target-specific flag in Cranelift to the specified value. @@ -365,22 +417,34 @@ WASM_API_EXTERN void wasmtime_config_cranelift_flag_enable(wasm_config_t*, const * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_set */ -WASM_API_EXTERN void wasmtime_config_cranelift_flag_set(wasm_config_t*, const char *key, const char *value); +WASM_API_EXTERN void wasmtime_config_cranelift_flag_set(wasm_config_t *, + const char *key, + const char *value); + +#endif // WASMTIME_FEATURE_COMPILER +/** + * \brief Configures whether, when on macOS, Mach ports are used for exception + * handling instead of traditional Unix-based signal handling. + * + * This option defaults to true, using Mach ports by default. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.macos_use_mach_ports + */ +WASMTIME_CONFIG_PROP(void, macos_use_mach_ports, bool) /** * Return the data from a LinearMemory instance. * - * The size in bytes as well as the maximum number of bytes that can be allocated should be - * returned as well. + * The size in bytes as well as the maximum number of bytes that can be + * allocated should be returned as well. * * For more information about see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html */ -typedef uint8_t *(*wasmtime_memory_get_callback_t)( - void *env, - size_t *byte_size, - size_t *maximum_byte_size); +typedef uint8_t *(*wasmtime_memory_get_callback_t)(void *env, size_t *byte_size, + size_t *maximum_byte_size); /** * Grow the memory to the `new_size` in bytes. @@ -388,9 +452,8 @@ typedef uint8_t *(*wasmtime_memory_get_callback_t)( * For more information about the parameters see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html#tymethod.grow_to */ -typedef wasmtime_error_t *(*wasmtime_memory_grow_callback_t)( - void *env, - size_t new_size); +typedef wasmtime_error_t *(*wasmtime_memory_grow_callback_t)(void *env, + size_t new_size); /** * A LinearMemory instance created from a #wasmtime_new_memory_callback_t. @@ -398,7 +461,7 @@ typedef wasmtime_error_t *(*wasmtime_memory_grow_callback_t)( * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html */ -typedef struct { +typedef struct wasmtime_linear_memory { /// User provided value to be passed to get_memory and grow_memory void *env; /// Callback to get the memory and size of this LinearMemory @@ -406,14 +469,14 @@ typedef struct { /// Callback to request growing the memory wasmtime_memory_grow_callback_t grow_memory; /// An optional finalizer for env - void (*finalizer)(void*); + void (*finalizer)(void *); } wasmtime_linear_memory_t; /** * A callback to create a new LinearMemory from the specified parameters. * - * The result should be written to `memory_ret` and wasmtime will own the values written - * into that struct. + * The result should be written to `memory_ret` and wasmtime will own the values + * written into that struct. * * This callback must be thread-safe. * @@ -421,48 +484,64 @@ typedef struct { * https://docs.wasmtime.dev/api/wasmtime/trait.MemoryCreator.html#tymethod.new_memory */ typedef wasmtime_error_t *(*wasmtime_new_memory_callback_t)( - void *env, - const wasm_memorytype_t *ty, - size_t minimum, - size_t maximum, - size_t reserved_size_in_bytes, - size_t guard_size_in_bytes, + void *env, const wasm_memorytype_t *ty, size_t minimum, size_t maximum, + size_t reserved_size_in_bytes, size_t guard_size_in_bytes, wasmtime_linear_memory_t *memory_ret); /** - * A representation of custom memory creator and methods for an instance of LinearMemory. + * A representation of custom memory creator and methods for an instance of + * LinearMemory. * * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/trait.MemoryCreator.html */ -typedef struct { +typedef struct wasmtime_memory_creator { /// User provided value to be passed to new_memory - void* env; + void *env; /// The callback to create new memory, must be thread safe wasmtime_new_memory_callback_t new_memory; /// An optional finalizer for env. - void (*finalizer)(void*); + void (*finalizer)(void *); } wasmtime_memory_creator_t; /** * Sets a custom memory creator. * - * Custom memory creators are used when creating host Memory objects or when creating instance - * linear memories for the on-demand instance allocation strategy. + * Custom memory creators are used when creating host Memory objects or when + * creating instance linear memories for the on-demand instance allocation + * strategy. * - * The config does **not** take ownership of the #wasmtime_memory_creator_t passed in, but - * instead copies all the values in the struct. + * The config does **not** take ownership of the #wasmtime_memory_creator_t + * passed in, but instead copies all the values in the struct. * * For more information see the Rust documentation at * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.with_host_memory */ -WASM_API_EXTERN void wasmtime_config_host_memory_creator_set( - wasm_config_t*, - wasmtime_memory_creator_t*); +WASM_API_EXTERN void +wasmtime_config_host_memory_creator_set(wasm_config_t *, + wasmtime_memory_creator_t *); + +/** + * \brief Configures whether copy-on-write memory-mapped data is used to + * initialize a linear memory. + * + * Initializing linear memory via a copy-on-write mapping can drastically + * improve instantiation costs of a WebAssembly module because copying memory is + * deferred. Additionally if a page of memory is only ever read from WebAssembly + * and never written too then the same underlying page of data will be reused + * between all instantiations of a module meaning that if a module is + * instantiated many times this can lower the overall memory required needed to + * run that module. + * + * This option defaults to true. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.memory_init_cow + */ +WASMTIME_CONFIG_PROP(void, memory_init_cow, bool) #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_CONFIG_H - diff --git a/crates/c-api/include/wasmtime/engine.h b/crates/c-api/include/wasmtime/engine.h index 1b8336f4605e..6b666bf5f983 100644 --- a/crates/c-api/include/wasmtime/engine.h +++ b/crates/c-api/include/wasmtime/engine.h @@ -13,6 +13,14 @@ extern "C" { #endif +/** + * \brief Create a new reference to the same underlying engine. + * + * This function clones the reference-counted pointer to the internal object, + * and must be freed using #wasm_engine_delete. + */ +WASM_API_EXTERN wasm_engine_t *wasmtime_engine_clone(wasm_engine_t *engine); + /** * \brief Increments the engine-local epoch variable. * @@ -28,9 +36,7 @@ extern "C" { WASM_API_EXTERN void wasmtime_engine_increment_epoch(wasm_engine_t *engine); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_ENGINE_H - - diff --git a/crates/c-api/include/wasmtime/error.h b/crates/c-api/include/wasmtime/error.h index 517755206759..ad9172543629 100644 --- a/crates/c-api/include/wasmtime/error.h +++ b/crates/c-api/include/wasmtime/error.h @@ -33,7 +33,7 @@ typedef struct wasmtime_error wasmtime_error_t; /** * \brief Creates a new error with the provided message. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_error_new(const char*); +WASM_API_EXTERN wasmtime_error_t *wasmtime_error_new(const char *); /** * \brief Deletes an error. @@ -48,10 +48,8 @@ WASM_API_EXTERN void wasmtime_error_delete(wasmtime_error_t *error); * uninitialized before this function is called and the caller is responsible * for deallocating it with #wasm_byte_vec_delete afterwards. */ -WASM_API_EXTERN void wasmtime_error_message( - const wasmtime_error_t *error, - wasm_name_t *message -); +WASM_API_EXTERN void wasmtime_error_message(const wasmtime_error_t *error, + wasm_name_t *message); /** * \brief Attempts to extract a WASI-specific exit status from this error. @@ -60,7 +58,8 @@ WASM_API_EXTERN void wasmtime_error_message( * If `true` is returned then the exit status is returned through the `status` * pointer. If `false` is returned then this is not a wasi exit trap. */ -WASM_API_EXTERN bool wasmtime_error_exit_status(const wasmtime_error_t*, int *status); +WASM_API_EXTERN bool wasmtime_error_exit_status(const wasmtime_error_t *, + int *status); /** * \brief Attempts to extract a WebAssembly trace from this error. @@ -69,10 +68,11 @@ WASM_API_EXTERN bool wasmtime_error_exit_status(const wasmtime_error_t*, int *st * as input. The `out` argument will be filled in with the wasm trace, if * present. */ -WASM_API_EXTERN void wasmtime_error_wasm_trace(const wasmtime_error_t*, wasm_frame_vec_t *out); +WASM_API_EXTERN void wasmtime_error_wasm_trace(const wasmtime_error_t *, + wasm_frame_vec_t *out); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_ERROR_H diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h index 29dcb217feb6..347f2b51bddd 100644 --- a/crates/c-api/include/wasmtime/extern.h +++ b/crates/c-api/include/wasmtime/extern.h @@ -8,6 +8,7 @@ #define WASMTIME_EXTERN_H #include +#include #include #ifdef __cplusplus @@ -16,58 +17,62 @@ extern "C" { /// \brief Representation of a function in Wasmtime. /// -/// Functions are represented with a 64-bit identifying integer in Wasmtime. -/// They do not have any destructor associated with them. Functions cannot -/// interoperate between #wasmtime_store_t instances and if the wrong function -/// is passed to the wrong store then it may trigger an assertion to abort the -/// process. +/// Functions in Wasmtime are represented as an index into a store and don't +/// have any data or destructor associated with the #wasmtime_func_t value. +/// Functions cannot interoperate between #wasmtime_store_t instances and if the +/// wrong function is passed to the wrong store then it may trigger an assertion +/// to abort the process. typedef struct wasmtime_func { - /// Internal identifier of what store this belongs to, never zero. + /// Internal identifier of what store this belongs to. + /// + /// This field may be zero when used in conjunction with #wasmtime_val_t + /// to represent a null `funcref` value in WebAssembly. For a valid function + /// this field is otherwise never zero. uint64_t store_id; - /// Internal index within the store. - size_t index; + /// Private field for Wasmtime, undefined if `store_id` is zero. + size_t __private; } wasmtime_func_t; /// \brief Representation of a table in Wasmtime. /// -/// Tables are represented with a 64-bit identifying integer in Wasmtime. -/// They do not have any destructor associated with them. Tables cannot -/// interoperate between #wasmtime_store_t instances and if the wrong table -/// is passed to the wrong store then it may trigger an assertion to abort the -/// process. +/// Tables in Wasmtime are represented as an index into a store and don't +/// have any data or destructor associated with the #wasmtime_table_t value. +/// Tables cannot interoperate between #wasmtime_store_t instances and if the +/// wrong table is passed to the wrong store then it may trigger an assertion +/// to abort the process. typedef struct wasmtime_table { /// Internal identifier of what store this belongs to, never zero. uint64_t store_id; - /// Internal index within the store. - size_t index; + /// Private field for Wasmtime. + size_t __private; } wasmtime_table_t; /// \brief Representation of a memory in Wasmtime. /// -/// Memories are represented with a 64-bit identifying integer in Wasmtime. -/// They do not have any destructor associated with them. Memories cannot -/// interoperate between #wasmtime_store_t instances and if the wrong memory -/// is passed to the wrong store then it may trigger an assertion to abort the -/// process. +/// Memories in Wasmtime are represented as an index into a store and don't +/// have any data or destructor associated with the #wasmtime_memory_t value. +/// Memories cannot interoperate between #wasmtime_store_t instances and if the +/// wrong memory is passed to the wrong store then it may trigger an assertion +/// to abort the process. typedef struct wasmtime_memory { /// Internal identifier of what store this belongs to, never zero. uint64_t store_id; - /// Internal index within the store. - size_t index; + /// Private field for Wasmtime. + size_t __private; } wasmtime_memory_t; /// \brief Representation of a global in Wasmtime. /// -/// Globals are represented with a 64-bit identifying integer in Wasmtime. -/// They do not have any destructor associated with them. Globals cannot -/// interoperate between #wasmtime_store_t instances and if the wrong global -/// is passed to the wrong store then it may trigger an assertion to abort the -/// process. +/// Globals in Wasmtime are represented as an index into a store and don't +/// have any data or destructor associated with the #wasmtime_global_t value. +/// Globals cannot interoperate between #wasmtime_store_t instances and if the +/// wrong global is passed to the wrong store then it may trigger an assertion +/// to abort the process. typedef struct wasmtime_global { /// Internal identifier of what store this belongs to, never zero. uint64_t store_id; - /// Internal index within the store. - size_t index; + /// Private field for Wasmtime. + size_t __private; } wasmtime_global_t; /// \brief Discriminant of #wasmtime_extern_t @@ -85,6 +90,9 @@ typedef uint8_t wasmtime_extern_kind_t; /// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a /// memory #define WASMTIME_EXTERN_MEMORY 3 +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// shared memory +#define WASMTIME_EXTERN_SHAREDMEMORY 4 /** * \typedef wasmtime_extern_union_t @@ -97,14 +105,16 @@ typedef uint8_t wasmtime_extern_kind_t; * various kinds of items an extern wasm item can be. */ typedef union wasmtime_extern_union { - /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_FUNC - wasmtime_func_t func; - /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_GLOBAL - wasmtime_global_t global; - /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_TABLE - wasmtime_table_t table; - /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MEMORY - wasmtime_memory_t memory; + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_FUNC + wasmtime_func_t func; + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_GLOBAL + wasmtime_global_t global; + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_TABLE + wasmtime_table_t table; + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MEMORY + wasmtime_memory_t memory; + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_SHAREDMEMORY + struct wasmtime_sharedmemory *sharedmemory; } wasmtime_extern_union_t; /** @@ -121,10 +131,10 @@ typedef union wasmtime_extern_union { * deallocate the value. */ typedef struct wasmtime_extern { - /// Discriminant of which field of #of is valid. - wasmtime_extern_kind_t kind; - /// Container for the extern item's value. - wasmtime_extern_union_t of; + /// Discriminant of which field of #of is valid. + wasmtime_extern_kind_t kind; + /// Container for the extern item's value. + wasmtime_extern_union_t of; } wasmtime_extern_t; /// \brief Deletes a #wasmtime_extern_t. @@ -135,11 +145,11 @@ void wasmtime_extern_delete(wasmtime_extern_t *val); /// /// Does not take ownership of `context` or `val`, but the returned /// #wasm_externtype_t is an owned value that needs to be deleted. -wasm_externtype_t *wasmtime_extern_type(wasmtime_context_t *context, wasmtime_extern_t *val); +wasm_externtype_t *wasmtime_extern_type(wasmtime_context_t *context, + wasmtime_extern_t *val); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_EXTERN_H - diff --git a/crates/c-api/include/wasmtime/func.h b/crates/c-api/include/wasmtime/func.h index c5b927fd7923..41ff907c37cc 100644 --- a/crates/c-api/include/wasmtime/func.h +++ b/crates/c-api/include/wasmtime/func.h @@ -8,9 +8,9 @@ #define WASMTIME_FUNC_H #include -#include -#include #include +#include +#include #ifdef __cplusplus extern "C" { @@ -54,13 +54,9 @@ typedef struct wasmtime_caller wasmtime_caller_t; * should be raised in WebAssembly. It's expected that in this case the caller * relinquishes ownership of the trap and it is passed back to the engine. */ -typedef wasm_trap_t* (*wasmtime_func_callback_t)( - void *env, - wasmtime_caller_t* caller, - const wasmtime_val_t *args, - size_t nargs, - wasmtime_val_t *results, - size_t nresults); +typedef wasm_trap_t *(*wasmtime_func_callback_t)( + void *env, wasmtime_caller_t *caller, const wasmtime_val_t *args, + size_t nargs, wasmtime_val_t *results, size_t nresults); /** * \brief Creates a new host-defined function. @@ -78,14 +74,11 @@ typedef wasm_trap_t* (*wasmtime_func_callback_t)( * * The returned function can only be used with the specified `store`. */ -WASM_API_EXTERN void wasmtime_func_new( - wasmtime_context_t *store, - const wasm_functype_t* type, - wasmtime_func_callback_t callback, - void *env, - void (*finalizer)(void*), - wasmtime_func_t *ret -); +WASM_API_EXTERN void wasmtime_func_new(wasmtime_context_t *store, + const wasm_functype_t *type, + wasmtime_func_callback_t callback, + void *env, void (*finalizer)(void *), + wasmtime_func_t *ret); /** * \brief Callback signature for #wasmtime_func_new_unchecked. @@ -120,10 +113,8 @@ WASM_API_EXTERN void wasmtime_func_new( * array. Results are also written starting at index 0, which will overwrite * the arguments. */ -typedef wasm_trap_t* (*wasmtime_func_unchecked_callback_t)( - void *env, - wasmtime_caller_t* caller, - wasmtime_val_raw_t *args_and_results, +typedef wasm_trap_t *(*wasmtime_func_unchecked_callback_t)( + void *env, wasmtime_caller_t *caller, wasmtime_val_raw_t *args_and_results, size_t num_args_and_results); /** @@ -151,23 +142,18 @@ typedef wasm_trap_t* (*wasmtime_func_unchecked_callback_t)( * existence). */ WASM_API_EXTERN void wasmtime_func_new_unchecked( - wasmtime_context_t *store, - const wasm_functype_t* type, - wasmtime_func_unchecked_callback_t callback, - void *env, - void (*finalizer)(void*), - wasmtime_func_t *ret -); + wasmtime_context_t *store, const wasm_functype_t *type, + wasmtime_func_unchecked_callback_t callback, void *env, + void (*finalizer)(void *), wasmtime_func_t *ret); /** * \brief Returns the type of the function specified * * The returned #wasm_functype_t is owned by the caller. */ -WASM_API_EXTERN wasm_functype_t* wasmtime_func_type( - const wasmtime_context_t *store, - const wasmtime_func_t *func -); +WASM_API_EXTERN wasm_functype_t * +wasmtime_func_type(const wasmtime_context_t *store, + const wasmtime_func_t *func); /** * \brief Call a WebAssembly function. @@ -204,15 +190,11 @@ WASM_API_EXTERN wasm_functype_t* wasmtime_func_type( * Does not take ownership of #wasmtime_val_t arguments. Gives ownership of * #wasmtime_val_t results. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call( - wasmtime_context_t *store, - const wasmtime_func_t *func, - const wasmtime_val_t *args, - size_t nargs, - wasmtime_val_t *results, - size_t nresults, - wasm_trap_t **trap -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_func_call(wasmtime_context_t *store, const wasmtime_func_t *func, + const wasmtime_val_t *args, size_t nargs, + wasmtime_val_t *results, size_t nresults, + wasm_trap_t **trap); /** * \brief Call a WebAssembly function in an "unchecked" fashion. @@ -243,13 +225,11 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call( * faster than that function, but the tradeoff is that embeddings must uphold * more invariants rather than relying on Wasmtime to check them for you. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call_unchecked( - wasmtime_context_t *store, - const wasmtime_func_t *func, - wasmtime_val_raw_t *args_and_results, - size_t args_and_results_len, - wasm_trap_t **trap -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_func_call_unchecked(wasmtime_context_t *store, + const wasmtime_func_t *func, + wasmtime_val_raw_t *args_and_results, + size_t args_and_results_len, wasm_trap_t **trap); /** * \brief Loads a #wasmtime_extern_t from the caller's context @@ -269,17 +249,16 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call_unchecked( * Returns a nonzero value if the export was found, or 0 if the export wasn't * found. If the export wasn't found then `item` isn't written to. */ -WASM_API_EXTERN bool wasmtime_caller_export_get( - wasmtime_caller_t *caller, - const char *name, - size_t name_len, - wasmtime_extern_t *item -); +WASM_API_EXTERN bool wasmtime_caller_export_get(wasmtime_caller_t *caller, + const char *name, + size_t name_len, + wasmtime_extern_t *item); /** * \brief Returns the store context of the caller object. */ -WASM_API_EXTERN wasmtime_context_t* wasmtime_caller_context(wasmtime_caller_t* caller); +WASM_API_EXTERN wasmtime_context_t * +wasmtime_caller_context(wasmtime_caller_t *caller); /** * \brief Converts a `raw` nonzero `funcref` value from #wasmtime_val_raw_t @@ -294,21 +273,18 @@ WASM_API_EXTERN wasmtime_context_t* wasmtime_caller_context(wasmtime_caller_t* c * #wasmtime_context_t that they were produced from. Providing arbitrary values * to `raw` here or cross-context values with `context` is UB. */ -WASM_API_EXTERN void wasmtime_func_from_raw( - wasmtime_context_t* context, - void *raw, - wasmtime_func_t *ret); +WASM_API_EXTERN void wasmtime_func_from_raw(wasmtime_context_t *context, + void *raw, wasmtime_func_t *ret); /** * \brief Converts a `func` which belongs to `context` into a `usize` * parameter that is suitable for insertion into a #wasmtime_val_raw_t. */ -WASM_API_EXTERN void *wasmtime_func_to_raw( - wasmtime_context_t* context, - const wasmtime_func_t *func); +WASM_API_EXTERN void *wasmtime_func_to_raw(wasmtime_context_t *context, + const wasmtime_func_t *func); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_FUNC_H diff --git a/crates/c-api/include/wasmtime/global.h b/crates/c-api/include/wasmtime/global.h index 4e244285a57c..8b20af9150b8 100644 --- a/crates/c-api/include/wasmtime/global.h +++ b/crates/c-api/include/wasmtime/global.h @@ -33,22 +33,18 @@ extern "C" { * This function does not take ownership of any of its arguments but error is * owned by the caller. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_global_new( - wasmtime_context_t *store, - const wasm_globaltype_t *type, - const wasmtime_val_t *val, - wasmtime_global_t *ret -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_global_new(wasmtime_context_t *store, const wasm_globaltype_t *type, + const wasmtime_val_t *val, wasmtime_global_t *ret); /** * \brief Returns the wasm type of the specified global. * * The returned #wasm_globaltype_t is owned by the caller. */ -WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type( - const wasmtime_context_t *store, - const wasmtime_global_t *global -); +WASM_API_EXTERN wasm_globaltype_t * +wasmtime_global_type(const wasmtime_context_t *store, + const wasmtime_global_t *global); /** * \brief Get the value of the specified global. @@ -58,13 +54,11 @@ WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type( * \param out where to store the value in this global. * * This function returns ownership of the contents of `out`, so - * #wasmtime_val_delete may need to be called on the value. + * #wasmtime_val_unroot may need to be called on the value. */ -WASM_API_EXTERN void wasmtime_global_get( - wasmtime_context_t *store, - const wasmtime_global_t *global, - wasmtime_val_t *out -); +WASM_API_EXTERN void wasmtime_global_get(wasmtime_context_t *store, + const wasmtime_global_t *global, + wasmtime_val_t *out); /** * \brief Sets a global to a new value. @@ -76,16 +70,15 @@ WASM_API_EXTERN void wasmtime_global_get( * This function may return an error if `global` is not mutable or if `val` has * the wrong type for `global`. * - * THis does not take ownership of any argument but returns ownership of the error. + * THis does not take ownership of any argument but returns ownership of the + * error. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_global_set( - wasmtime_context_t *store, - const wasmtime_global_t *global, - const wasmtime_val_t *val -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_global_set(wasmtime_context_t *store, const wasmtime_global_t *global, + const wasmtime_val_t *val); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_GLOBAL_H diff --git a/crates/c-api/include/wasmtime/instance.h b/crates/c-api/include/wasmtime/instance.h index 72f398ec9cff..0d63cfb7a421 100644 --- a/crates/c-api/include/wasmtime/instance.h +++ b/crates/c-api/include/wasmtime/instance.h @@ -62,14 +62,11 @@ typedef struct wasmtime_instance { * This function does not take ownership of any of its arguments, but all return * values are owned by the caller. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new( - wasmtime_context_t *store, - const wasmtime_module_t *module, - const wasmtime_extern_t* imports, - size_t nimports, - wasmtime_instance_t *instance, - wasm_trap_t **trap -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_instance_new(wasmtime_context_t *store, + const wasmtime_module_t *module, + const wasmtime_extern_t *imports, size_t nimports, + wasmtime_instance_t *instance, wasm_trap_t **trap); /** * \brief Get an export by name from an instance. @@ -87,12 +84,8 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new( * #wasmtime_extern_t. */ WASM_API_EXTERN bool wasmtime_instance_export_get( - wasmtime_context_t *store, - const wasmtime_instance_t *instance, - const char *name, - size_t name_len, - wasmtime_extern_t *item -); + wasmtime_context_t *store, const wasmtime_instance_t *instance, + const char *name, size_t name_len, wasmtime_extern_t *item); /** * \brief Get an export by index from an instance. @@ -113,16 +106,12 @@ WASM_API_EXTERN bool wasmtime_instance_export_get( * #wasmtime_context_t. */ WASM_API_EXTERN bool wasmtime_instance_export_nth( - wasmtime_context_t *store, - const wasmtime_instance_t *instance, - size_t index, - char **name, - size_t *name_len, - wasmtime_extern_t *item -); + wasmtime_context_t *store, const wasmtime_instance_t *instance, + size_t index, char **name, size_t *name_len, wasmtime_extern_t *item); /** - * \brief A #wasmtime_instance_t, pre-instantiation, that is ready to be instantiated. + * \brief A #wasmtime_instance_t, pre-instantiation, that is ready to be + * instantiated. * * Must be deleted using #wasmtime_instance_pre_delete. * @@ -135,7 +124,7 @@ typedef struct wasmtime_instance_pre wasmtime_instance_pre_t; * \brief Delete a previously created wasmtime_instance_pre_t. */ WASM_API_EXTERN void -wasmtime_instance_pre_delete(wasmtime_instance_pre_t *instance); +wasmtime_instance_pre_delete(wasmtime_instance_pre_t *instance_pre); /** * \brief Instantiates instance within the given store. @@ -160,14 +149,21 @@ wasmtime_instance_pre_delete(wasmtime_instance_pre_t *instance); * This function does not take ownership of any of its arguments, and all return * values are owned by the caller. */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_instance_pre_instantiate( - const wasmtime_instance_pre_t* instance_pre, - wasmtime_store_t *store, - wasmtime_instance_t* instance, - wasm_trap_t **trap_ptr); +WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_pre_instantiate( + const wasmtime_instance_pre_t *instance_pre, wasmtime_store_t *store, + wasmtime_instance_t *instance, wasm_trap_t **trap_ptr); + +/** + * \brief Get the module (as a shallow clone) for a instance_pre. + * + * The returned module is owned by the caller and the caller **must** + * delete it via `wasmtime_module_delete`. + */ +WASM_API_EXTERN wasmtime_module_t * +wasmtime_instance_pre_module(wasmtime_instance_pre_t *instance_pre); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_INSTANCE_H diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h index 3215efe9ff6c..0a793d1e6fc9 100644 --- a/crates/c-api/include/wasmtime/linker.h +++ b/crates/c-api/include/wasmtime/linker.h @@ -8,9 +8,10 @@ #define WASMTIME_LINKER_H #include +#include #include -#include #include +#include #ifdef __cplusplus extern "C" { @@ -40,12 +41,21 @@ typedef struct wasmtime_linker wasmtime_linker_t; * This function does not take ownership of the engine argument, and the caller * is expected to delete the returned linker. */ -WASM_API_EXTERN wasmtime_linker_t* wasmtime_linker_new(wasm_engine_t* engine); +WASM_API_EXTERN wasmtime_linker_t *wasmtime_linker_new(wasm_engine_t *engine); + +/** + * \brief Clones existing linker. + * + * This function does not take ownership of the linker argument, and the caller + * is expected to delete the returned linker. + */ +WASM_API_EXTERN wasmtime_linker_t * +wasmtime_linker_clone(wasmtime_linker_t *linker); /** * \brief Deletes a linker */ -WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t* linker); +WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t *linker); /** * \brief Configures whether this linker allows later definitions to shadow @@ -53,7 +63,8 @@ WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t* linker); * * By default this setting is `false`. */ -WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker, bool allow_shadowing); +WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t *linker, + bool allow_shadowing); /** * \brief Defines a new item in this linker. @@ -72,15 +83,10 @@ WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker, * For more information about name resolution consult the [Rust * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define( - wasmtime_linker_t *linker, - wasmtime_context_t *store, - const char *module, - size_t module_len, - const char *name, - size_t name_len, - const wasmtime_extern_t *item -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_linker_define(wasmtime_linker_t *linker, wasmtime_context_t *store, + const char *module, size_t module_len, const char *name, + size_t name_len, const wasmtime_extern_t *item); /** * \brief Defines a new function in this linker. @@ -92,7 +98,8 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define( * \param name_len the byte length of `name` * \param ty the type of the function that's being defined * \param cb the host callback to invoke when the function is called - * \param data the host-provided data to provide as the first argument to the callback + * \param data the host-provided data to provide as the first argument to the + * callback * \param finalizer an optional finalizer for the `data` argument. * * \return On success `NULL` is returned, otherwise an error is returned which @@ -107,17 +114,10 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define( * * For more information about host callbacks see #wasmtime_func_new. */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_func( - wasmtime_linker_t *linker, - const char *module, - size_t module_len, - const char *name, - size_t name_len, - const wasm_functype_t *ty, - wasmtime_func_callback_t cb, - void *data, - void (*finalizer)(void*) -); +WASM_API_EXTERN wasmtime_error_t *wasmtime_linker_define_func( + wasmtime_linker_t *linker, const char *module, size_t module_len, + const char *name, size_t name_len, const wasm_functype_t *ty, + wasmtime_func_callback_t cb, void *data, void (*finalizer)(void *)); /** * \brief Defines a new function in this linker. @@ -128,17 +128,13 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_func( * information as well as #wasmtime_func_new_unchecked for why this is an * unsafe API. */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_func_unchecked( - wasmtime_linker_t *linker, - const char *module, - size_t module_len, - const char *name, - size_t name_len, - const wasm_functype_t *ty, - wasmtime_func_unchecked_callback_t cb, - void *data, - void (*finalizer)(void*) -); +WASM_API_EXTERN wasmtime_error_t *wasmtime_linker_define_func_unchecked( + wasmtime_linker_t *linker, const char *module, size_t module_len, + const char *name, size_t name_len, const wasm_functype_t *ty, + wasmtime_func_unchecked_callback_t cb, void *data, + void (*finalizer)(void *)); + +#ifdef WASMTIME_FEATURE_WASI /** * \brief Defines WASI functions in this linker. @@ -157,9 +153,10 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_func_unchecked( * For more information about name resolution consult the [Rust * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi( - wasmtime_linker_t *linker -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_linker_define_wasi(wasmtime_linker_t *linker); + +#endif // WASMTIME_FEATURE_WASI /** * \brief Defines an instance under the specified name in this linker. @@ -180,13 +177,9 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi( * For more information about name resolution consult the [Rust * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_instance( - wasmtime_linker_t *linker, - wasmtime_context_t *store, - const char *name, - size_t name_len, - const wasmtime_instance_t *instance -); +WASM_API_EXTERN wasmtime_error_t *wasmtime_linker_define_instance( + wasmtime_linker_t *linker, wasmtime_context_t *store, const char *name, + size_t name_len, const wasmtime_instance_t *instance); /** * \brief Instantiates a #wasm_module_t with the items defined in this linker. @@ -210,13 +203,11 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_instance( * defined in the linker than an error is returned. (or if the previously * defined item is of the wrong type). */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate( - const wasmtime_linker_t *linker, - wasmtime_context_t *store, - const wasmtime_module_t *module, - wasmtime_instance_t *instance, - wasm_trap_t **trap -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_linker_instantiate(const wasmtime_linker_t *linker, + wasmtime_context_t *store, + const wasmtime_module_t *module, + wasmtime_instance_t *instance, wasm_trap_t **trap); /** * \brief Defines automatic instantiations of a #wasm_module_t in this linker. @@ -237,13 +228,10 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate( * For more information see the [Rust * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.module). */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_module( - wasmtime_linker_t *linker, - wasmtime_context_t *store, - const char *name, - size_t name_len, - const wasmtime_module_t *module -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_linker_module(wasmtime_linker_t *linker, wasmtime_context_t *store, + const char *name, size_t name_len, + const wasmtime_module_t *module); /** * \brief Acquires the "default export" of the named module in this linker. @@ -260,13 +248,10 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_module( * For more information see the [Rust * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.get_default). */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_default( - const wasmtime_linker_t *linker, - wasmtime_context_t *store, - const char *name, - size_t name_len, - wasmtime_func_t *func -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_linker_get_default(const wasmtime_linker_t *linker, + wasmtime_context_t *store, const char *name, + size_t name_len, wasmtime_func_t *func); /** * \brief Loads an item by name from this linker. @@ -282,18 +267,14 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_default( * \return A nonzero value if the item is defined, in which case `item` is also * filled in. Otherwise zero is returned. */ -WASM_API_EXTERN bool wasmtime_linker_get( - const wasmtime_linker_t *linker, - wasmtime_context_t *store, - const char *module, - size_t module_len, - const char *name, - size_t name_len, - wasmtime_extern_t *item -); +WASM_API_EXTERN bool wasmtime_linker_get(const wasmtime_linker_t *linker, + wasmtime_context_t *store, + const char *module, size_t module_len, + const char *name, size_t name_len, + wasmtime_extern_t *item); /** - * \brief Preform all the checks for instantiating `module` with the linker, + * \brief Perform all the checks for instantiating `module` with the linker, * except that instantiation doesn't actually finish. * * \param linker the linker used to instantiate the provided module. @@ -305,13 +286,13 @@ WASM_API_EXTERN bool wasmtime_linker_get( * For more information see the Rust documentation at: * https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.instantiate_pre */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate_pre( - const wasmtime_linker_t *linker, - const wasmtime_module_t *module, - wasmtime_instance_t **instance_pre); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_linker_instantiate_pre(const wasmtime_linker_t *linker, + const wasmtime_module_t *module, + wasmtime_instance_pre_t **instance_pre); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_LINKER_H diff --git a/crates/c-api/include/wasmtime/memory.h b/crates/c-api/include/wasmtime/memory.h index cda42c2b855a..53e82fcc65ed 100644 --- a/crates/c-api/include/wasmtime/memory.h +++ b/crates/c-api/include/wasmtime/memory.h @@ -8,9 +8,9 @@ #define WASMTIME_MEMORY_H #include +#include #include #include -#include #ifdef __cplusplus extern "C" { @@ -22,7 +22,10 @@ extern "C" { * Note that this function is preferred over #wasm_memorytype_new for * compatibility with the memory64 proposal. */ -WASM_API_EXTERN wasm_memorytype_t *wasmtime_memorytype_new(uint64_t min, bool max_present, uint64_t max, bool is_64); +WASM_API_EXTERN wasm_memorytype_t *wasmtime_memorytype_new(uint64_t min, + bool max_present, + uint64_t max, + bool is_64); /** * \brief Returns the minimum size, in pages, of the specified memory type. @@ -30,7 +33,8 @@ WASM_API_EXTERN wasm_memorytype_t *wasmtime_memorytype_new(uint64_t min, bool ma * Note that this function is preferred over #wasm_memorytype_limits for * compatibility with the memory64 proposal. */ -WASM_API_EXTERN uint64_t wasmtime_memorytype_minimum(const wasm_memorytype_t *ty); +WASM_API_EXTERN uint64_t +wasmtime_memorytype_minimum(const wasm_memorytype_t *ty); /** * \brief Returns the maximum size, in pages, of the specified memory type. @@ -42,13 +46,19 @@ WASM_API_EXTERN uint64_t wasmtime_memorytype_minimum(const wasm_memorytype_t *ty * Note that this function is preferred over #wasm_memorytype_limits for * compatibility with the memory64 proposal. */ -WASM_API_EXTERN bool wasmtime_memorytype_maximum(const wasm_memorytype_t *ty, uint64_t *max); +WASM_API_EXTERN bool wasmtime_memorytype_maximum(const wasm_memorytype_t *ty, + uint64_t *max); /** * \brief Returns whether this type of memory represents a 64-bit memory. */ WASM_API_EXTERN bool wasmtime_memorytype_is64(const wasm_memorytype_t *ty); +/** + * \brief Returns whether this type of memory represents a shared memory. + */ +WASM_API_EXTERN bool wasmtime_memorytype_isshared(const wasm_memorytype_t *ty); + /** * \brief Creates a new WebAssembly linear memory * @@ -59,43 +69,34 @@ WASM_API_EXTERN bool wasmtime_memorytype_is64(const wasm_memorytype_t *ty); * If an error happens when creating the memory it's returned and owned by the * caller. If an error happens then `ret` is not filled in. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new( - wasmtime_context_t *store, - const wasm_memorytype_t* ty, - wasmtime_memory_t *ret -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_memory_new(wasmtime_context_t *store, const wasm_memorytype_t *ty, + wasmtime_memory_t *ret); /** * \brief Returns the type of the memory specified */ -WASM_API_EXTERN wasm_memorytype_t* wasmtime_memory_type( - const wasmtime_context_t *store, - const wasmtime_memory_t *memory -); +WASM_API_EXTERN wasm_memorytype_t * +wasmtime_memory_type(const wasmtime_context_t *store, + const wasmtime_memory_t *memory); /** * \brief Returns the base pointer in memory where the linear memory starts. */ -WASM_API_EXTERN uint8_t *wasmtime_memory_data( - const wasmtime_context_t *store, - const wasmtime_memory_t *memory -); +WASM_API_EXTERN uint8_t *wasmtime_memory_data(const wasmtime_context_t *store, + const wasmtime_memory_t *memory); /** * \brief Returns the byte length of this linear memory. */ WASM_API_EXTERN size_t wasmtime_memory_data_size( - const wasmtime_context_t *store, - const wasmtime_memory_t *memory -); + const wasmtime_context_t *store, const wasmtime_memory_t *memory); /** * \brief Returns the length, in WebAssembly pages, of this linear memory */ -WASM_API_EXTERN uint64_t wasmtime_memory_size( - const wasmtime_context_t *store, - const wasmtime_memory_t *memory -); +WASM_API_EXTERN uint64_t wasmtime_memory_size(const wasmtime_context_t *store, + const wasmtime_memory_t *memory); /** * \brief Attempts to grow the specified memory by `delta` pages. @@ -109,15 +110,12 @@ WASM_API_EXTERN uint64_t wasmtime_memory_size( * returned. Otherwise `prev_size` is set to the previous size of the memory, in * WebAssembly pages, and `NULL` is returned. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_grow( - wasmtime_context_t *store, - const wasmtime_memory_t *memory, - uint64_t delta, - uint64_t *prev_size -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_memory_grow(wasmtime_context_t *store, const wasmtime_memory_t *memory, + uint64_t delta, uint64_t *prev_size); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_MEMORY_H diff --git a/crates/c-api/include/wasmtime/module.h b/crates/c-api/include/wasmtime/module.h index 222239e81f66..286f784bd561 100644 --- a/crates/c-api/include/wasmtime/module.h +++ b/crates/c-api/include/wasmtime/module.h @@ -27,6 +27,8 @@ extern "C" { */ typedef struct wasmtime_module wasmtime_module_t; +#ifdef WASMTIME_FEATURE_COMPILER + /** * \brief Compiles a WebAssembly binary into a #wasmtime_module_t * @@ -41,12 +43,12 @@ typedef struct wasmtime_module wasmtime_module_t; * This function does not take ownership of any of its arguments, but the * returned error and module are owned by the caller. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( - wasm_engine_t *engine, - const uint8_t *wasm, - size_t wasm_len, - wasmtime_module_t **ret -); +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new(wasm_engine_t *engine, + const uint8_t *wasm, + size_t wasm_len, + wasmtime_module_t **ret); + +#endif // WASMTIME_FEATURE_COMPILER /** * \brief Deletes a module. @@ -62,18 +64,16 @@ WASM_API_EXTERN wasmtime_module_t *wasmtime_module_clone(wasmtime_module_t *m); /** * \brief Same as #wasm_module_imports, but for #wasmtime_module_t. */ -WASM_API_EXTERN void wasmtime_module_imports( - const wasmtime_module_t *module, - wasm_importtype_vec_t *out -); +WASM_API_EXTERN void wasmtime_module_imports(const wasmtime_module_t *module, + wasm_importtype_vec_t *out); /** * \brief Same as #wasm_module_exports, but for #wasmtime_module_t. */ -WASM_API_EXTERN void wasmtime_module_exports( - const wasmtime_module_t *module, - wasm_exporttype_vec_t *out -); +WASM_API_EXTERN void wasmtime_module_exports(const wasmtime_module_t *module, + wasm_exporttype_vec_t *out); + +#ifdef WASMTIME_FEATURE_COMPILER /** * \brief Validate a WebAssembly binary. @@ -87,18 +87,16 @@ WASM_API_EXTERN void wasmtime_module_exports( * If the binary validates then `NULL` is returned, otherwise the error returned * describes why the binary did not validate. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_module_validate( - wasm_engine_t *engine, - const uint8_t *wasm, - size_t wasm_len -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_module_validate(wasm_engine_t *engine, const uint8_t *wasm, + size_t wasm_len); /** * \brief This function serializes compiled module artifacts as blob data. * * \param module the module - * \param ret if the conversion is successful, this byte vector is filled in with - * the serialized compiled module. + * \param ret if the conversion is successful, this byte vector is filled in + * with the serialized compiled module. * * \return a non-null error if parsing fails, or returns `NULL`. If parsing * fails then `ret` isn't touched. @@ -106,10 +104,10 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_module_validate( * This function does not take ownership of `module`, and the caller is * expected to deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. */ -WASM_API_EXTERN wasmtime_error_t* wasmtime_module_serialize( - wasmtime_module_t* module, - wasm_byte_vec_t *ret -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_module_serialize(wasmtime_module_t *module, wasm_byte_vec_t *ret); + +#endif // WASMTIME_FEATURE_COMPILER /** * \brief Build a module from serialized data. @@ -119,14 +117,11 @@ WASM_API_EXTERN wasmtime_error_t* wasmtime_module_serialize( * * This function is not safe to receive arbitrary user input. See the Rust * documentation for more information on what inputs are safe to pass in here - * (e.g. only that of #wasmtime_module_serialize) + * (e.g. only that of `wasmtime_module_serialize`) */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize( - wasm_engine_t *engine, - const uint8_t *bytes, - size_t bytes_len, - wasmtime_module_t **ret -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_module_deserialize(wasm_engine_t *engine, const uint8_t *bytes, + size_t bytes_len, wasmtime_module_t **ret); /** * \brief Deserialize a module from an on-disk file. @@ -140,31 +135,29 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize( * * This function is not safe to receive arbitrary user input. See the Rust * documentation for more information on what inputs are safe to pass in here - * (e.g. only that of #wasmtime_module_serialize) + * (e.g. only that of `wasmtime_module_serialize`) */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize_file( - wasm_engine_t *engine, - const char *path, - wasmtime_module_t **ret -); - +WASM_API_EXTERN wasmtime_error_t * +wasmtime_module_deserialize_file(wasm_engine_t *engine, const char *path, + wasmtime_module_t **ret); /** - * \brief Returns the range of bytes in memory where this module’s compilation image resides. + * \brief Returns the range of bytes in memory where this module’s compilation + * image resides. * - * The compilation image for a module contains executable code, data, debug information, etc. - * This is roughly the same as the wasmtime_module_serialize but not the exact same. + * The compilation image for a module contains executable code, data, debug + * information, etc. This is roughly the same as the wasmtime_module_serialize + * but not the exact same. * - * For more details see: https://docs.wasmtime.dev/api/wasmtime/struct.Module.html#method.image_range + * For more details see: + * https://docs.wasmtime.dev/api/wasmtime/struct.Module.html#method.image_range */ -WASM_API_EXTERN void wasmtime_module_image_range( - const wasmtime_module_t *module, - size_t *start, - size_t *end -); +WASM_API_EXTERN void +wasmtime_module_image_range(const wasmtime_module_t *module, void **start, + void **end); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_MODULE_H diff --git a/crates/c-api/include/wasmtime/profiling.h b/crates/c-api/include/wasmtime/profiling.h new file mode 100644 index 000000000000..55e95d37233e --- /dev/null +++ b/crates/c-api/include/wasmtime/profiling.h @@ -0,0 +1,123 @@ +/** + * \file wasmtime/profiling.h + * + * \brief API for Wasmtime guest profiler + */ + +#ifndef WASMTIME_PROFILING_H +#define WASMTIME_PROFILING_H + +#include +#include +#include + +#ifdef WASMTIME_FEATURE_PROFILING + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Collects basic profiling data for a single WebAssembly guest. + * + * To use this, you’ll need to arrange to call #wasmtime_guestprofiler_sample at + * regular intervals while the guest is on the stack. The most straightforward + * way to do that is to call it from a callback registered with + * #wasmtime_store_epoch_deadline_callback. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.GuestProfiler.html + */ +typedef struct wasmtime_guestprofiler wasmtime_guestprofiler_t; + +/** + * \brief Deletes profiler without finishing it. + * + * \param guestprofiler profiler that is being deleted + */ +WASM_API_EXTERN void wasmtime_guestprofiler_delete( + /* own */ wasmtime_guestprofiler_t *guestprofiler); + +/** + * \typedef wasmtime_guestprofiler_modules_t + * \brief Alias to #wasmtime_guestprofiler_modules + * + * \struct #wasmtime_guestprofiler_modules + * \brief Tuple of name and module for passing into #wasmtime_guestprofiler_new. + */ +typedef struct wasmtime_guestprofiler_modules { + const wasm_name_t *name; //!< Name recorded in the profile. + const wasmtime_module_t + *mod; //!< Module that is being allowed to appear in captured stack trace. +} wasmtime_guestprofiler_modules_t; + +/** + * \brief Begin profiling a new guest. + * + * \param module_name name recorded in the profile + * \param interval_nanos intended sampling interval in nanoseconds recorded in + * the profile + * \param modules modules and associated names that will appear in + * captured stack traces, pointer to the first element + * \param modules_len count of elements in `modules` + * + * \return Created profiler that is owned by the caller. + * + * This function does not take ownership of the arguments. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.GuestProfiler.html#method.new + */ +WASM_API_EXTERN /* own */ wasmtime_guestprofiler_t *wasmtime_guestprofiler_new( + const wasm_name_t *module_name, uint64_t interval_nanos, + const wasmtime_guestprofiler_modules_t *modules, size_t modules_len); + +/** + * \brief Add a sample to the profile. + * + * \param guestprofiler the profiler the sample is being added to + * \param store store that is being used to collect the backtraces + * \param delta_nanos CPU time in nanoseconds that was used by this guest + * since the previous sample + * + * Zero can be passed as `delta_nanos` if recording CPU usage information + * is not needed. + * This function does not take ownership of the arguments. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.GuestProfiler.html#method.sample + */ +WASM_API_EXTERN void +wasmtime_guestprofiler_sample(wasmtime_guestprofiler_t *guestprofiler, + const wasmtime_store_t *store, + uint64_t delta_nanos); + +/** + * \brief Writes out the captured profile. + * + * \param guestprofiler the profiler which is being finished and deleted + * \param out pointer to where #wasm_byte_vec_t containing generated + * file will be written + * + * \return Returns #wasmtime_error_t owned by the caller in case of error, + * `NULL` otherwise. + * + * This function takes ownership of `guestprofiler`, even when error is + * returned. + * Only when returning without error `out` is filled with #wasm_byte_vec_t owned + * by the caller. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.GuestProfiler.html#method.finish + */ +WASM_API_EXTERN /* own */ wasmtime_error_t * +wasmtime_guestprofiler_finish(/* own */ wasmtime_guestprofiler_t *guestprofiler, + /* own */ wasm_byte_vec_t *out); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_FEATURE_PROFILING + +#endif // WASMTIME_PROFILING_H diff --git a/crates/c-api/include/wasmtime/sharedmemory.h b/crates/c-api/include/wasmtime/sharedmemory.h new file mode 100644 index 000000000000..3065a0ef1ad2 --- /dev/null +++ b/crates/c-api/include/wasmtime/sharedmemory.h @@ -0,0 +1,108 @@ +/** + * \file wasmtime/sharedmemory.h + * + * Wasmtime API for interacting with wasm shared memories. + */ + +#ifndef WASMTIME_SHAREDMEMORY_H +#define WASMTIME_SHAREDMEMORY_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Interface for shared memories. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.SharedMemory.html + */ +typedef struct wasmtime_sharedmemory wasmtime_sharedmemory_t; + +#ifdef WASMTIME_FEATURE_THREADS + +/** + * \brief Creates a new WebAssembly shared linear memory + * + * \param engine engine that created shared memory is associated with + * \param ty the type of the memory to create + * \param ret where to store the returned memory + * + * If an error happens when creating the memory it's returned and owned by the + * caller. If an error happens then `ret` is not filled in. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_sharedmemory_new(const wasm_engine_t *engine, + const wasm_memorytype_t *ty, + wasmtime_sharedmemory_t **ret); + +#endif // WASMTIME_FEATURE_THREADS + +/** + * \brief Deletes shared linear memory + * + * \param memory memory to be deleted + */ +WASM_API_EXTERN void +wasmtime_sharedmemory_delete(wasmtime_sharedmemory_t *memory); + +/** + * \brief Clones shared linear memory + * + * \param memory memory to be cloned + * + * This function makes shallow clone, ie. copy of reference counted + * memory handle. + */ +WASM_API_EXTERN wasmtime_sharedmemory_t * +wasmtime_sharedmemory_clone(const wasmtime_sharedmemory_t *memory); + +/** + * \brief Returns the type of the shared memory specified + */ +WASM_API_EXTERN wasm_memorytype_t * +wasmtime_sharedmemory_type(const wasmtime_sharedmemory_t *memory); + +/** + * \brief Returns the base pointer in memory where + the shared linear memory starts. + */ +WASM_API_EXTERN uint8_t * +wasmtime_sharedmemory_data(const wasmtime_sharedmemory_t *memory); + +/** + * \brief Returns the byte length of this shared linear memory. + */ +WASM_API_EXTERN size_t +wasmtime_sharedmemory_data_size(const wasmtime_sharedmemory_t *memory); + +/** + * \brief Returns the length, in WebAssembly pages, of this shared linear memory + */ +WASM_API_EXTERN uint64_t +wasmtime_sharedmemory_size(const wasmtime_sharedmemory_t *memory); + +/** + * \brief Attempts to grow the specified shared memory by `delta` pages. + * + * \param memory the memory to grow + * \param delta the number of pages to grow by + * \param prev_size where to store the previous size of memory + * + * If memory cannot be grown then `prev_size` is left unchanged and an error is + * returned. Otherwise `prev_size` is set to the previous size of the memory, in + * WebAssembly pages, and `NULL` is returned. + */ +WASM_API_EXTERN wasmtime_error_t * +wasmtime_sharedmemory_grow(const wasmtime_sharedmemory_t *memory, + uint64_t delta, uint64_t *prev_size); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_SHAREDMEMORY_H diff --git a/crates/c-api/include/wasmtime/store.h b/crates/c-api/include/wasmtime/store.h index c86ac57234e6..65613041c93c 100644 --- a/crates/c-api/include/wasmtime/store.h +++ b/crates/c-api/include/wasmtime/store.h @@ -7,8 +7,9 @@ #ifndef WASMTIME_STORE_H #define WASMTIME_STORE_H -#include #include +#include +#include #include #ifdef __cplusplus @@ -43,8 +44,8 @@ typedef struct wasmtime_store wasmtime_store_t; * \brief An interior pointer into a #wasmtime_store_t which is used as * "context" for many functions. * - * This context pointer is used pervasively throught Wasmtime's API. This can be - * acquired from #wasmtime_store_context or #wasmtime_caller_context. The + * This context pointer is used pervasively throughout Wasmtime's API. This can + * be acquired from #wasmtime_store_context or #wasmtime_caller_context. The * context pointer for a store is the same for the entire lifetime of a store, * so it can safely be stored adjacent to a #wasmtime_store_t itself. * @@ -68,16 +69,15 @@ typedef struct wasmtime_context wasmtime_context_t; * This function creates a fresh store with the provided configuration settings. * The returned store must be deleted with #wasmtime_store_delete. */ -WASM_API_EXTERN wasmtime_store_t *wasmtime_store_new( - wasm_engine_t *engine, - void *data, - void (*finalizer)(void*) -); +WASM_API_EXTERN wasmtime_store_t *wasmtime_store_new(wasm_engine_t *engine, + void *data, + void (*finalizer)(void *)); /** * \brief Returns the interior #wasmtime_context_t pointer to this store */ -WASM_API_EXTERN wasmtime_context_t *wasmtime_store_context(wasmtime_store_t *store); +WASM_API_EXTERN wasmtime_context_t * +wasmtime_store_context(wasmtime_store_t *store); /** * \brief Provides limits for a store. Used by hosts to limit resource @@ -108,14 +108,11 @@ WASM_API_EXTERN wasmtime_context_t *wasmtime_store_context(wasmtime_store_t *sto * resources in the future, this does not retroactively attempt to apply * limits to the store. */ -WASM_API_EXTERN void wasmtime_store_limiter( - wasmtime_store_t *store, - int64_t memory_size, - int64_t table_elements, - int64_t instances, - int64_t tables, - int64_t memories -); +WASM_API_EXTERN void wasmtime_store_limiter(wasmtime_store_t *store, + int64_t memory_size, + int64_t table_elements, + int64_t instances, int64_t tables, + int64_t memories); /** * \brief Deletes a store. @@ -125,7 +122,8 @@ WASM_API_EXTERN void wasmtime_store_delete(wasmtime_store_t *store); /** * \brief Returns the user-specified data associated with the specified store */ -WASM_API_EXTERN void *wasmtime_context_get_data(const wasmtime_context_t* context); +WASM_API_EXTERN void * +wasmtime_context_get_data(const wasmtime_context_t *context); /** * \brief Overwrites the user-specified data associated with this store. @@ -134,7 +132,8 @@ WASM_API_EXTERN void *wasmtime_context_get_data(const wasmtime_context_t* contex * and the original finalizer will be executed for the provided data when the * store is deleted. */ -WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void *data); +WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t *context, + void *data); /** * \brief Perform garbage collection within the given context. @@ -145,10 +144,10 @@ WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void * * The `context` argument must not be NULL. */ -WASM_API_EXTERN void wasmtime_context_gc(wasmtime_context_t* context); +WASM_API_EXTERN void wasmtime_context_gc(wasmtime_context_t *context); /** - * \brief Adds fuel to this context's store for wasm to consume while executing. + * \brief Set fuel to this context's store for wasm to consume while executing. * * For this method to work fuel consumption must be enabled via * #wasmtime_config_consume_fuel_set. By default a store starts with 0 fuel @@ -156,53 +155,28 @@ WASM_API_EXTERN void wasmtime_context_gc(wasmtime_context_t* context); * This function must be called for the store to have * some fuel to allow WebAssembly to execute. * - * Note that at this time when fuel is entirely consumed it will cause - * wasm to trap. More usages of fuel are planned for the future. + * Note that when fuel is entirely consumed it will cause wasm to trap. * * If fuel is not enabled within this store then an error is returned. If fuel * is successfully added then NULL is returned. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t *store, uint64_t fuel); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_context_set_fuel(wasmtime_context_t *store, uint64_t fuel); /** - * \brief Returns the amount of fuel consumed by this context's store execution - * so far. + * \brief Returns the amount of fuel remaining in this context's store. * * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set - * then this function will return false. Otherwise true is returned and the + * then this function will return an error. Otherwise `NULL` is returned and the * fuel parameter is filled in with fuel consumed so far. * * Also note that fuel, if enabled, must be originally configured via - * #wasmtime_context_add_fuel. + * #wasmtime_context_set_fuel. */ -WASM_API_EXTERN bool wasmtime_context_fuel_consumed(const wasmtime_context_t *context, uint64_t *fuel); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_context_get_fuel(const wasmtime_context_t *context, uint64_t *fuel); -/** - * \brief Returns the amount of fuel remaining in this context's store execution - * before engine traps execution. - * - * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set - * then this function will return false. Otherwise true is returned and the - * fuel parameter is filled in with remaining fuel. - * - * Also note that fuel, if enabled, must be originally configured via - * #wasmtime_context_add_fuel. - */ -WASM_API_EXTERN bool wasmtime_context_fuel_remaining(const wasmtime_context_t *context, uint64_t *fuel); - -/** - * \brief Attempt to manually consume fuel from the store. - * - * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set then - * this function will return an error. Otherwise this will attempt to consume - * the specified amount of `fuel` from the store. If successful the remaining - * amount of fuel is stored into `remaining`. If `fuel` couldn't be consumed - * then an error is returned. - * - * Also note that fuel, if enabled, must be originally configured via - * #wasmtime_context_add_fuel. - */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_context_consume_fuel(wasmtime_context_t *context, uint64_t fuel, uint64_t *remaining); +#ifdef WASMTIME_FEATURE_WASI /** * \brief Configures WASI state within the specified store. @@ -215,7 +189,10 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_context_consume_fuel(wasmtime_context * of `wasi`. The caller should no longer use `wasi` after calling this function * (even if an error is returned). */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi); + +#endif // WASMTIME_FEATURE_WASI /** * \brief Configures the relative deadline at which point WebAssembly code will @@ -227,25 +204,46 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t * * See also #wasmtime_config_epoch_interruption_set and * #wasmtime_store_epoch_deadline_callback. */ -WASM_API_EXTERN void wasmtime_context_set_epoch_deadline(wasmtime_context_t *context, uint64_t ticks_beyond_current); +WASM_API_EXTERN void +wasmtime_context_set_epoch_deadline(wasmtime_context_t *context, + uint64_t ticks_beyond_current); + +/// \brief An enum for the behavior before extending the epoch deadline. +typedef uint8_t wasmtime_update_deadline_kind_t; +/// \brief Directly continue to updating the deadline and executing WebAssembly. +#define WASMTIME_UPDATE_DEADLINE_CONTINUE 0 +/// \brief Yield control (via async support) then update the deadline. +#define WASMTIME_UPDATE_DEADLINE_YIELD 1 /** * \brief Configures epoch deadline callback to C function. * * This function configures a store-local callback function that will be * called when the running WebAssembly function has exceeded its epoch - * deadline. That function can return a #wasmtime_error_t to terminate - * the function, or set the delta argument and return NULL to update the - * epoch deadline and resume function execution. + * deadline. That function can: + * - return a #wasmtime_error_t to terminate the function + * - set the delta argument and return NULL to update the + * epoch deadline delta and resume function execution. + * - set the delta argument, update the epoch deadline, + * set update_kind to WASMTIME_UPDATE_DEADLINE_YIELD, + * and return NULL to yield (via async support) and + * resume function execution. + * + * To use WASMTIME_UPDATE_DEADLINE_YIELD async support must be enabled + * for this store. * * See also #wasmtime_config_epoch_interruption_set and * #wasmtime_context_set_epoch_deadline. */ -WASM_API_EXTERN void wasmtime_store_epoch_deadline_callback(wasmtime_store_t *store, wasmtime_error_t* (*func)(wasmtime_context_t*, void*, uint64_t*), void *data); +WASM_API_EXTERN void wasmtime_store_epoch_deadline_callback( + wasmtime_store_t *store, + wasmtime_error_t *(*func)(wasmtime_context_t *context, void *data, + uint64_t *epoch_deadline_delta, + wasmtime_update_deadline_kind_t *update_kind), + void *data, void (*finalizer)(void *)); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_STORE_H - diff --git a/crates/c-api/include/wasmtime/table.h b/crates/c-api/include/wasmtime/table.h index 630cee9ecae6..9ec8f3796c80 100644 --- a/crates/c-api/include/wasmtime/table.h +++ b/crates/c-api/include/wasmtime/table.h @@ -8,9 +8,9 @@ #define WASMTIME_TABLE_H #include +#include #include #include -#include #include #ifdef __cplusplus @@ -29,22 +29,19 @@ extern "C" { * ownership of returned error. This function may return an error if the `init` * value does not match `ty`, for example. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new( - wasmtime_context_t *store, - const wasm_tabletype_t *ty, - const wasmtime_val_t *init, - wasmtime_table_t *table -); +WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new(wasmtime_context_t *store, + const wasm_tabletype_t *ty, + const wasmtime_val_t *init, + wasmtime_table_t *table); /** * \brief Returns the type of this table. * * The caller has ownership of the returned #wasm_tabletype_t */ -WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type( - const wasmtime_context_t *store, - const wasmtime_table_t *table -); +WASM_API_EXTERN wasm_tabletype_t * +wasmtime_table_type(const wasmtime_context_t *store, + const wasmtime_table_t *table); /** * \brief Gets a value in a table. @@ -58,12 +55,9 @@ WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type( * returned then `val` is filled in and is owned by the caller. Otherwise zero * is returned because the `index` is out-of-bounds. */ -WASM_API_EXTERN bool wasmtime_table_get( - wasmtime_context_t *store, - const wasmtime_table_t *table, - uint32_t index, - wasmtime_val_t *val -); +WASM_API_EXTERN bool wasmtime_table_get(wasmtime_context_t *store, + const wasmtime_table_t *table, + uint32_t index, wasmtime_val_t *val); /** * \brief Sets a value in a table. @@ -78,20 +72,15 @@ WASM_API_EXTERN bool wasmtime_table_get( * This function can fail if `value` has the wrong type for the table, or if * `index` is out of bounds. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set( - wasmtime_context_t *store, - const wasmtime_table_t *table, - uint32_t index, - const wasmtime_val_t *value -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_table_set(wasmtime_context_t *store, const wasmtime_table_t *table, + uint32_t index, const wasmtime_val_t *value); /** * \brief Returns the size, in elements, of the specified table */ -WASM_API_EXTERN uint32_t wasmtime_table_size( - const wasmtime_context_t *store, - const wasmtime_table_t *table -); +WASM_API_EXTERN uint32_t wasmtime_table_size(const wasmtime_context_t *store, + const wasmtime_table_t *table); /** * \brief Grows a table. @@ -110,17 +99,13 @@ WASM_API_EXTERN uint32_t wasmtime_table_size( * * This function does not take ownership of any of its arguments. */ -WASM_API_EXTERN wasmtime_error_t *wasmtime_table_grow( - wasmtime_context_t *store, - const wasmtime_table_t *table, - uint32_t delta, - const wasmtime_val_t *init, - uint32_t *prev_size -); +WASM_API_EXTERN wasmtime_error_t * +wasmtime_table_grow(wasmtime_context_t *store, const wasmtime_table_t *table, + uint32_t delta, const wasmtime_val_t *init, + uint32_t *prev_size); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_TABLE_H - diff --git a/crates/c-api/include/wasmtime/trap.h b/crates/c-api/include/wasmtime/trap.h index 2d2e20407c20..0d2385bb008b 100644 --- a/crates/c-api/include/wasmtime/trap.h +++ b/crates/c-api/include/wasmtime/trap.h @@ -28,7 +28,8 @@ enum wasmtime_trap_code_enum { WASMTIME_TRAP_CODE_STACK_OVERFLOW, /// An out-of-bounds memory access. WASMTIME_TRAP_CODE_MEMORY_OUT_OF_BOUNDS, - /// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address. + /// A wasm atomic operation was presented with a not-naturally-aligned + /// linear-memory address. WASMTIME_TRAP_CODE_HEAP_MISALIGNED, /// An out-of-bounds access to a table. WASMTIME_TRAP_CODE_TABLE_OUT_OF_BOUNDS, @@ -69,7 +70,8 @@ WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(const char *msg, size_t msg_len); * an instruction trap -- traps can also be created using wasm_trap_new, * or occur with WASI modules exiting with a certain exit code. */ -WASM_API_EXTERN bool wasmtime_trap_code(const wasm_trap_t*, wasmtime_trap_code_t *code); +WASM_API_EXTERN bool wasmtime_trap_code(const wasm_trap_t *, + wasmtime_trap_code_t *code); /** * \brief Returns a human-readable name for this frame's function. @@ -79,7 +81,8 @@ WASM_API_EXTERN bool wasmtime_trap_code(const wasm_trap_t*, wasmtime_trap_code_t * * The lifetime of the returned name is the same as the #wasm_frame_t itself. */ -WASM_API_EXTERN const wasm_name_t *wasmtime_frame_func_name(const wasm_frame_t*); +WASM_API_EXTERN const wasm_name_t * +wasmtime_frame_func_name(const wasm_frame_t *); /** * \brief Returns a human-readable name for this frame's module. @@ -89,11 +92,11 @@ WASM_API_EXTERN const wasm_name_t *wasmtime_frame_func_name(const wasm_frame_t*) * * The lifetime of the returned name is the same as the #wasm_frame_t itself. */ -WASM_API_EXTERN const wasm_name_t *wasmtime_frame_module_name(const wasm_frame_t*); - +WASM_API_EXTERN const wasm_name_t * +wasmtime_frame_module_name(const wasm_frame_t *); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_TRAP_H diff --git a/crates/c-api/include/wasmtime/val.h b/crates/c-api/include/wasmtime/val.h index 4c4081336a68..bdac5b9c7675 100644 --- a/crates/c-api/include/wasmtime/val.h +++ b/crates/c-api/include/wasmtime/val.h @@ -7,6 +7,7 @@ #ifndef WASMTIME_VAL_H #define WASMTIME_VAL_H +#include #include #include @@ -14,6 +15,141 @@ extern "C" { #endif +/** + * \typedef wasmtime_anyref_t + * \brief Convenience alias for #wasmtime_anyref + * + * \struct wasmtime_anyref + * \brief A WebAssembly value in the `any` hierarchy of GC types. + * + * This structure represents an `anyref` that WebAssembly can create and + * pass back to the host. The host can also create values to pass to a guest. + * + * Note that this structure does not itself contain the data that it refers to. + * Instead to contains metadata to point back within a #wasmtime_context_t, so + * referencing the internal data requires using a `wasmtime_context_t`. + * + * Anyref values are required to be explicitly unrooted via + * #wasmtime_anyref_unroot to enable them to be garbage-collected. + * + * Null anyref values are represented by this structure and can be tested and + * created with the `wasmtime_anyref_is_null` and `wasmtime_anyref_set_null` + * functions. + */ +typedef struct wasmtime_anyref { + /// 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; +} wasmtime_anyref_t; + +/// \brief Helper function to initialize the `ref` provided to a null anyref +/// value. +static inline void wasmtime_anyref_set_null(wasmtime_anyref_t *ref) { + ref->store_id = 0; +} + +/// \brief Helper function to return whether the provided `ref` points to a null +/// `anyref` value. +/// +/// Note that `ref` itself should not be null as null is represented internally +/// within a #wasmtime_anyref_t value. +static inline bool wasmtime_anyref_is_null(const wasmtime_anyref_t *ref) { + return ref->store_id == 0; +} + +/** + * \brief Creates a new reference pointing to the same data that `anyref` + * points to (depending on the configured collector this might increase a + * reference count or create a new GC root). + * + * The returned reference is stored in `out`. + */ +WASM_API_EXTERN void wasmtime_anyref_clone(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref, + wasmtime_anyref_t *out); + +/** + * \brief Unroots the `ref` provided within the `context`. + * + * This API is required to enable the `ref` value provided to be + * garbage-collected. This API itself does not necessarily garbage-collect the + * value, but it's possible to collect it in the future after this. + * + * This may modify `ref` and the contents of `ref` are left in an undefined + * state after this API is called and it should no longer be used. + * + * Note that null or i32 anyref values do not need to be unrooted but are still + * valid to pass to this function. + */ +WASM_API_EXTERN void wasmtime_anyref_unroot(wasmtime_context_t *context, + wasmtime_anyref_t *ref); + +/** + * \brief Converts a raw `anyref` value coming from #wasmtime_val_raw_t into + * a #wasmtime_anyref_t. + * + * The provided `out` pointer is filled in with a reference converted from + * `raw`. + */ +WASM_API_EXTERN void wasmtime_anyref_from_raw(wasmtime_context_t *context, + uint32_t raw, + wasmtime_anyref_t *out); + +/** + * \brief Converts a #wasmtime_anyref_t to a raw value suitable for storing + * into a #wasmtime_val_raw_t. + * + * Note that the returned underlying value is not tracked by Wasmtime's garbage + * collector until it enters WebAssembly. This means that a GC may release the + * context's reference to the raw value, making the raw value invalid within the + * context of the store. Do not perform a GC between calling this function and + * passing it to WebAssembly. + */ +WASM_API_EXTERN uint32_t wasmtime_anyref_to_raw(wasmtime_context_t *context, + const wasmtime_anyref_t *ref); + +/** + * \brief Create a new `i31ref` value. + * + * Creates a new `i31ref` value (which is a subtype of `anyref`) and returns a + * pointer to it. + * + * If `i31val` does not fit in 31 bits, it is wrapped. + */ +WASM_API_EXTERN void wasmtime_anyref_from_i31(wasmtime_context_t *context, + uint32_t i31val, + wasmtime_anyref_t *out); + +/** + * \brief Get the `anyref`'s underlying `i31ref` value, zero extended, if any. + * + * If the given `anyref` is an instance of `i31ref`, then its value is zero + * extended to 32 bits, written to `dst`, and `true` is returned. + * + * If the given `anyref` is not an instance of `i31ref`, then `false` is + * returned and `dst` is left unmodified. + */ +WASM_API_EXTERN bool wasmtime_anyref_i31_get_u(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref, + uint32_t *dst); + +/** + * \brief Get the `anyref`'s underlying `i31ref` value, sign extended, if any. + * + * If the given `anyref` is an instance of `i31ref`, then its value is sign + * extended to 32 bits, written to `dst`, and `true` is returned. + * + * If the given `anyref` is not an instance of `i31ref`, then `false` is + * returned and `dst` is left unmodified. + */ +WASM_API_EXTERN bool wasmtime_anyref_i31_get_s(wasmtime_context_t *context, + const wasmtime_anyref_t *anyref, + int32_t *dst); + /** * \typedef wasmtime_externref_t * \brief Convenience alias for #wasmtime_externref @@ -24,24 +160,65 @@ extern "C" { * This structure represents an `externref` that can be passed to WebAssembly. * It cannot be forged by WebAssembly itself and is guaranteed to have been * created by the host. + * + * This structure is similar to #wasmtime_anyref_t but represents the + * `externref` type in WebAssembly. This can be created on the host from + * arbitrary host pointers/destructors. Note that this value is itself a + * reference into a #wasmtime_context_t and must be explicitly unrooted to + * enable garbage collection. + * + * Note that null is represented with this structure and created with + * `wasmtime_externref_set_null`. Null can be tested for with the + * `wasmtime_externref_is_null` function. */ -typedef struct wasmtime_externref wasmtime_externref_t; +typedef struct wasmtime_externref { + /// 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; +} wasmtime_externref_t; + +/// \brief Helper function to initialize the `ref` provided to a null externref +/// value. +static inline void wasmtime_externref_set_null(wasmtime_externref_t *ref) { + ref->store_id = 0; +} + +/// \brief Helper function to return whether the provided `ref` points to a null +/// `externref` value. +/// +/// Note that `ref` itself should not be null as null is represented internally +/// within a #wasmtime_externref_t value. +static inline bool wasmtime_externref_is_null(const wasmtime_externref_t *ref) { + return ref->store_id == 0; +} /** * \brief Create a new `externref` value. * - * Creates a new `externref` value wrapping the provided data, returning the - * pointer to the externref. + * Creates a new `externref` value wrapping the provided data, returning whether + * it was created or not. * + * \param context the store context to allocate this externref within * \param data the host-specific data to wrap * \param finalizer an optional finalizer for `data` + * \param out where to store the created value. * * When the reference is reclaimed, the wrapped data is cleaned up with the * provided `finalizer`. * - * The returned value must be deleted with #wasmtime_externref_delete + * If `true` is returned then `out` has been filled in and must be unrooted + * in the future with #wasmtime_externref_unroot. If `false` is returned then + * the host wasn't able to create more GC values at this time. Performing a GC + * may free up enough space to try again. */ -WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_new(void *data, void (*finalizer)(void*)); +WASM_API_EXTERN bool wasmtime_externref_new(wasmtime_context_t *context, + void *data, + void (*finalizer)(void *), + wasmtime_externref_t *out); /** * \brief Get an `externref`'s wrapped data @@ -49,28 +226,46 @@ WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_new(void *data, void (* * Returns the original `data` passed to #wasmtime_externref_new. It is required * that `data` is not `NULL`. */ -WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_externref_t *data); +WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_context_t *context, + const wasmtime_externref_t *data); /** - * \brief Creates a shallow copy of the `externref` argument, returning a - * separately owned pointer (increases the reference count). + * \brief Creates a new reference pointing to the same data that `ref` points + * to (depending on the configured collector this might increase a reference + * count or create a new GC root). + * + * The `out` parameter stores the cloned reference. This reference must + * eventually be unrooted with #wasmtime_externref_unroot in the future to + * enable GC'ing it. */ -WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_clone(wasmtime_externref_t *ref); +WASM_API_EXTERN void wasmtime_externref_clone(wasmtime_context_t *context, + const wasmtime_externref_t *ref, + wasmtime_externref_t *out); /** - * \brief Decrements the reference count of the `ref`, deleting it if it's the - * last reference. + * \brief Unroots the pointer `ref` from the `context` provided. + * + * This function will enable future garbage collection of the value pointed to + * by `ref` once there are no more references. The `ref` value may be mutated in + * place by this function and its contents are undefined after this function + * returns. It should not be used until after re-initializing it. + * + * Note that null externref values do not need to be unrooted but are still + * valid to pass to this function. */ -WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_externref_t *ref); +WASM_API_EXTERN void wasmtime_externref_unroot(wasmtime_context_t *context, + wasmtime_externref_t *ref); /** * \brief Converts a raw `externref` value coming from #wasmtime_val_raw_t into * a #wasmtime_externref_t. * - * Note that the returned #wasmtime_externref_t is an owned value that must be - * deleted via #wasmtime_externref_delete by the caller if it is non-null. + * The `out` reference is filled in with the non-raw version of this externref. + * It must eventually be unrooted with #wasmtime_externref_unroot. */ -WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_from_raw(wasmtime_context_t *context, void *raw); +WASM_API_EXTERN void wasmtime_externref_from_raw(wasmtime_context_t *context, + uint32_t raw, + wasmtime_externref_t *out); /** * \brief Converts a #wasmtime_externref_t to a raw value suitable for storing @@ -82,9 +277,8 @@ WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_from_raw(wasmtime_conte * context of the store. Do not perform a GC between calling this function and * passing it to WebAssembly. */ -WASM_API_EXTERN void *wasmtime_externref_to_raw( - wasmtime_context_t *context, - const wasmtime_externref_t *ref); +WASM_API_EXTERN uint32_t wasmtime_externref_to_raw( + wasmtime_context_t *context, const wasmtime_externref_t *ref); /// \brief Discriminant stored in #wasmtime_val::kind typedef uint8_t wasmtime_valkind_t; @@ -98,10 +292,15 @@ typedef uint8_t wasmtime_valkind_t; #define WASMTIME_F64 3 /// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a v128 #define WASMTIME_V128 4 -/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a funcref +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a +/// funcref #define WASMTIME_FUNCREF 5 -/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an externref +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an +/// externref #define WASMTIME_EXTERNREF 6 +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an +/// anyref +#define WASMTIME_ANYREF 7 /// \brief A 128-bit value representing the WebAssembly `v128` type. Bytes are /// stored in little-endian order. @@ -126,20 +325,37 @@ typedef union wasmtime_valunion { float32_t f32; /// Field used if #wasmtime_val_t::kind is #WASMTIME_F64 float64_t f64; + /// Field used if #wasmtime_val_t::kind is #WASMTIME_ANYREF + 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_FUNCREF /// - /// If this value represents a `ref.null func` value then the `store_id` field - /// is set to zero. + /// Use `wasmtime_funcref_is_null` to test whether this is a null function + /// reference. wasmtime_func_t funcref; - /// Field used if #wasmtime_val_t::kind is #WASMTIME_EXTERNREF - /// - /// If this value represents a `ref.null extern` value then this pointer will - /// be `NULL`. - wasmtime_externref_t *externref; /// Field used if #wasmtime_val_t::kind is #WASMTIME_V128 wasmtime_v128 v128; } wasmtime_valunion_t; +/// \brief Initialize a `wasmtime_func_t` value as a null function reference. +/// +/// This function will initialize the `func` provided to be a null function +/// reference. Used in conjunction with #wasmtime_val_t and +/// #wasmtime_valunion_t. +static inline void wasmtime_funcref_set_null(wasmtime_func_t *func) { + func->store_id = 0; +} + +/// \brief Helper function to test whether the `func` provided is a null +/// function reference. +/// +/// This function is used with #wasmtime_val_t and #wasmtime_valunion_t and its +/// `funcref` field. This will test whether the field represents a null funcref. +static inline bool wasmtime_funcref_is_null(const wasmtime_func_t *func) { + return func->store_id == 0; +} + /** * \typedef wasmtime_val_raw_t * \brief Convenience alias for #wasmtime_val_raw @@ -174,13 +390,14 @@ typedef union wasmtime_val_raw { /// /// Note that this field is always stored in a little-endian format. wasmtime_v128 v128; - /// Field for when this val is a WebAssembly `funcref` value. + /// Field for when this val is a WebAssembly `anyref` value. /// - /// If this is set to 0 then it's a null funcref, otherwise this must be - /// passed to `wasmtime_func_from_raw` to determine the `wasmtime_func_t`. + /// If this is set to 0 then it's a null anyref, otherwise this must be + /// passed to `wasmtime_anyref_from_raw` to determine the + /// `wasmtime_anyref_t`. /// /// Note that this field is always stored in a little-endian format. - void *funcref; + uint32_t anyref; /// Field for when this val is a WebAssembly `externref` value. /// /// If this is set to 0 then it's a null externref, otherwise this must be @@ -188,9 +405,26 @@ typedef union wasmtime_val_raw { /// `wasmtime_externref_t`. /// /// Note that this field is always stored in a little-endian format. - void *externref; + uint32_t externref; + /// Field for when this val is a WebAssembly `funcref` value. + /// + /// If this is set to 0 then it's a null funcref, otherwise this must be + /// passed to `wasmtime_func_from_raw` to determine the `wasmtime_func_t`. + /// + /// Note that this field is always stored in a little-endian format. + void *funcref; } wasmtime_val_raw_t; +// Assert that the shape of this type is as expected since it needs to match +// Rust. +static inline void __wasmtime_val_assertions() { + static_assert(sizeof(wasmtime_valunion_t) == 16, "should be 16-bytes large"); + static_assert(__alignof(wasmtime_valunion_t) == 8, + "should be 8-byte aligned"); + static_assert(sizeof(wasmtime_val_raw_t) == 16, "should be 16 bytes large"); + static_assert(__alignof(wasmtime_val_raw_t) == 8, "should be 8-byte aligned"); +} + /** * \typedef wasmtime_val_t * \brief Convenience alias for #wasmtime_val_t @@ -198,11 +432,11 @@ typedef union wasmtime_val_raw { * \union wasmtime_val * \brief Container for different kinds of wasm values. * - * Note that this structure may contain an owned value, namely - * #wasmtime_externref_t, depending on the context in which this is used. APIs - * which consume a #wasmtime_val_t do not take ownership, but APIs that return - * #wasmtime_val_t require that #wasmtime_val_delete is called to deallocate - * the value. + * Note that this structure may contain an owned value, namely rooted GC + * references, depending on the context in which this is used. APIs which + * consume a #wasmtime_val_t do not take ownership, but APIs that return + * #wasmtime_val_t require that #wasmtime_val_unroot is called to clean up + * any possible GC roots in the value. */ typedef struct wasmtime_val { /// Discriminant of which field of #of is valid. @@ -212,21 +446,32 @@ typedef struct wasmtime_val { } wasmtime_val_t; /** - * \brief Deletes an owned #wasmtime_val_t. + * \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 + * function leaves `val` in an undefined state and it should not be used again + * without re-initializing. * - * Note that this only deletes the contents, not the memory that `val` points to - * itself (which is owned by the caller). + * This method does not need to be called for integers, floats, v128, or + * funcref values. */ -WASM_API_EXTERN void wasmtime_val_delete(wasmtime_val_t *val); +WASM_API_EXTERN void wasmtime_val_unroot(wasmtime_context_t *context, + wasmtime_val_t *val); /** - * \brief Copies `src` into `dst`. + * \brief Clones the value pointed to by `src` into the `dst` provided. + * + * This function will clone any rooted GC values in `src` and have them + * newly rooted inside of `dst`. When using this API the `dst` should be + * later unrooted with #wasmtime_val_unroot if it contains GC values. */ -WASM_API_EXTERN void wasmtime_val_copy(wasmtime_val_t *dst, const wasmtime_val_t *src); +WASM_API_EXTERN void wasmtime_val_clone(wasmtime_context_t *context, + const wasmtime_val_t *src, + wasmtime_val_t *dst); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif #endif // WASMTIME_VAL_H - diff --git a/crates/c-api/macros/Cargo.toml b/crates/c-api/macros/Cargo.toml deleted file mode 100644 index 33de12aa0ad4..000000000000 --- a/crates/c-api/macros/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "wasmtime-c-api-macros" -version = "0.0.0" -authors = ["The Wasmtime Project Developers"] -license = "Apache-2.0 WITH LLVM-exception" -edition.workspace = true -publish = false - -[lib] -proc-macro = true -test = false -doctest = false - -[dependencies] -quote = "1.0" -proc-macro2 = "1.0" diff --git a/crates/c-api/macros/src/lib.rs b/crates/c-api/macros/src/lib.rs deleted file mode 100644 index 9eb9bea4e31d..000000000000 --- a/crates/c-api/macros/src/lib.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! A set of convenience macros for our wasmtime-c-api crate. -//! -//! These are intended to mirror the macros in the `wasm.h` header file and -//! largely facilitate the `declare_ref` macro. - -extern crate proc_macro; - -use proc_macro2::{Ident, TokenStream, TokenTree}; -use quote::quote; - -fn extract_ident(input: proc_macro::TokenStream) -> Ident { - let input = TokenStream::from(input); - let i = match input.into_iter().next().unwrap() { - TokenTree::Ident(i) => i, - _ => panic!("expected an ident"), - }; - let name = i.to_string(); - assert!(name.ends_with("_t")); - return i; -} - -#[proc_macro] -pub fn declare_own(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let ty = extract_ident(input); - let name = ty.to_string(); - let delete = quote::format_ident!("{}_delete", &name[..name.len() - 2]); - - (quote! { - #[no_mangle] - pub extern fn #delete(_: Box<#ty>) {} - }) - .into() -} - -#[proc_macro] -pub fn declare_ty(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let ty = extract_ident(input); - let name = ty.to_string(); - let copy = quote::format_ident!("{}_copy", &name[..name.len() - 2]); - - (quote! { - wasmtime_c_api_macros::declare_own!(#ty); - - #[no_mangle] - pub extern fn #copy(src: &#ty) -> Box<#ty> { - Box::new(src.clone()) - } - }) - .into() -} - -#[proc_macro] -pub fn declare_ref(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let ty = extract_ident(input); - let name = ty.to_string(); - let prefix = &name[..name.len() - 2]; - let copy = quote::format_ident!("{}_copy", prefix); - let same = quote::format_ident!("{}_same", prefix); - let get_host_info = quote::format_ident!("{}_get_host_info", prefix); - let set_host_info = quote::format_ident!("{}_set_host_info", prefix); - let set_host_info_final = quote::format_ident!("{}_set_host_info_with_finalizer", prefix); - let as_ref = quote::format_ident!("{}_as_ref", prefix); - let as_ref_const = quote::format_ident!("{}_as_ref_const", prefix); - - (quote! { - wasmtime_c_api_macros::declare_own!(#ty); - - #[no_mangle] - pub extern fn #copy(src: &#ty) -> Box<#ty> { - Box::new(src.clone()) - } - - #[no_mangle] - pub extern fn #same(_a: &#ty, _b: &#ty) -> bool { - eprintln!("`{}` is not implemented", stringify!(#same)); - std::process::abort(); - } - - #[no_mangle] - pub extern fn #get_host_info(a: &#ty) -> *mut std::os::raw::c_void { - std::ptr::null_mut() - } - - #[no_mangle] - pub extern fn #set_host_info(a: &#ty, info: *mut std::os::raw::c_void) { - eprintln!("`{}` is not implemented", stringify!(#set_host_info)); - std::process::abort(); - } - - #[no_mangle] - pub extern fn #set_host_info_final( - a: &#ty, - info: *mut std::os::raw::c_void, - finalizer: Option, - ) { - eprintln!("`{}` is not implemented", stringify!(#set_host_info_final)); - std::process::abort(); - } - - #[no_mangle] - pub extern fn #as_ref(a: &#ty) -> Box { - eprintln!("`{}` is not implemented", stringify!(#as_ref)); - std::process::abort(); - } - - #[no_mangle] - pub extern fn #as_ref_const(a: &#ty) -> Box { - eprintln!("`{}` is not implemented", stringify!(#as_ref_const)); - std::process::abort(); - } - - // TODO: implement `wasm_ref_as_#name#` - // TODO: implement `wasm_ref_as_#name#_const` - }) - .into() -} diff --git a/crates/c-api/src/async.rs b/crates/c-api/src/async.rs index 23572db32e22..ef577d87cded 100644 --- a/crates/c-api/src/async.rs +++ b/crates/c-api/src/async.rs @@ -1,16 +1,21 @@ use std::ffi::c_void; use std::future::Future; use std::mem::{self, MaybeUninit}; +use std::num::NonZeroU64; +use std::ops::Range; use std::pin::Pin; +use std::sync::Arc; use std::task::{Context, Poll}; use std::{ptr, str}; - -use wasmtime::{AsContextMut, Caller, Func, Instance, Result, Trap, Val}; +use wasmtime::{ + AsContextMut, Func, Instance, Result, RootScope, StackCreator, StackMemory, Trap, Val, +}; use crate::{ bad_utf8, handle_result, to_str, translate_args, wasm_config_t, wasm_functype_t, wasm_trap_t, wasmtime_caller_t, wasmtime_error_t, wasmtime_instance_pre_t, wasmtime_linker_t, - wasmtime_module_t, wasmtime_val_t, wasmtime_val_union, CStoreContextMut, WASMTIME_I32, + wasmtime_module_t, wasmtime_val_t, wasmtime_val_union, WasmtimeCaller, WasmtimeStoreContextMut, + WASMTIME_I32, }; #[no_mangle] @@ -25,19 +30,21 @@ pub extern "C" fn wasmtime_config_async_stack_size_set(c: &mut wasm_config_t, si #[no_mangle] pub extern "C" fn wasmtime_context_epoch_deadline_async_yield_and_update( - mut store: CStoreContextMut<'_>, + mut store: WasmtimeStoreContextMut<'_>, delta: u64, ) { store.epoch_deadline_async_yield_and_update(delta); } #[no_mangle] -pub extern "C" fn wasmtime_context_out_of_fuel_async_yield( - mut store: CStoreContextMut<'_>, - injection_count: u64, - fuel_to_inject: u64, -) { - store.out_of_fuel_async_yield(injection_count, fuel_to_inject); +pub extern "C" fn wasmtime_context_fuel_async_yield_interval( + mut store: WasmtimeStoreContextMut<'_>, + interval: Option, +) -> Option> { + handle_result( + store.fuel_async_yield_interval(interval.map(|n| n.get())), + |()| {}, + ) } pub type wasmtime_func_async_callback_t = extern "C" fn( @@ -80,17 +87,23 @@ impl Future for wasmtime_async_continuation_t { } } -pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool; - -struct CallbackData { - env: *mut c_void, +/// Internal structure to add Send/Sync to a c_void member. +/// +/// This is useful in closures that need to capture some C data. +#[derive(Debug)] +struct CallbackDataPtr { + pub ptr: *mut std::ffi::c_void, } -unsafe impl Send for CallbackData {} + +unsafe impl Send for CallbackDataPtr {} +unsafe impl Sync for CallbackDataPtr {} + +pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool; async fn invoke_c_async_callback<'a>( cb: wasmtime_func_async_callback_t, - data: CallbackData, - mut caller: Caller<'a, crate::StoreData>, + data: CallbackDataPtr, + mut caller: WasmtimeCaller<'a>, params: &'a [Val], results: &'a mut [Val], ) -> Result<()> { @@ -100,7 +113,12 @@ async fn invoke_c_async_callback<'a>( let mut hostcall_val_storage = mem::take(&mut caller.data_mut().hostcall_val_storage); debug_assert!(hostcall_val_storage.is_empty()); hostcall_val_storage.reserve(params.len() + results.len()); - hostcall_val_storage.extend(params.iter().cloned().map(|p| wasmtime_val_t::from_val(p))); + hostcall_val_storage.extend( + params + .iter() + .cloned() + .map(|p| wasmtime_val_t::from_val_unscoped(&mut caller, p)), + ); hostcall_val_storage.extend((0..results.len()).map(|_| wasmtime_val_t { kind: WASMTIME_I32, of: wasmtime_val_union { i32: 0 }, @@ -108,7 +126,7 @@ async fn invoke_c_async_callback<'a>( let (params, out_results) = hostcall_val_storage.split_at_mut(params.len()); // Invoke the C function pointer. - // The result will be a continutation which we will wrap in a Future. + // The result will be a continuation which we will wrap in a Future. let mut caller = wasmtime_caller_t { caller }; let mut trap = None; extern "C" fn panic_callback(_: *mut c_void) -> bool { @@ -120,7 +138,7 @@ async fn invoke_c_async_callback<'a>( finalizer: None, }; cb( - data.env, + data.ptr, &mut caller, params.as_ptr(), params.len(), @@ -138,7 +156,7 @@ async fn invoke_c_async_callback<'a>( // Translate the `wasmtime_val_t` results into the `results` space for (i, result) in out_results.iter().enumerate() { unsafe { - results[i] = result.to_val(); + results[i] = result.to_val_unscoped(&mut caller.caller); } } // Move our `vals` storage back into the store now that we no longer @@ -154,7 +172,7 @@ unsafe fn c_async_callback_to_rust_fn( data: *mut c_void, finalizer: Option, ) -> impl for<'a> Fn( - Caller<'a, crate::StoreData>, + WasmtimeCaller<'a>, &'a [Val], &'a mut [Val], ) -> Box> + Send + 'a> @@ -164,7 +182,7 @@ unsafe fn c_async_callback_to_rust_fn( let foreign = crate::ForeignData { data, finalizer }; move |caller, params, results| { let _ = &foreign; // move entire foreign into this closure - let data = CallbackData { env: foreign.data }; + let data = CallbackDataPtr { ptr: foreign.data }; Box::new(invoke_c_async_callback( callback, data, caller, params, results, )) @@ -201,25 +219,24 @@ fn handle_call_error( } async fn do_func_call_async( - mut store: CStoreContextMut<'_>, + mut store: RootScope>, func: &Func, args: impl ExactSizeIterator, results: &mut [MaybeUninit], trap_ret: &mut *mut wasm_trap_t, err_ret: &mut *mut wasmtime_error_t, ) { - let mut store = store.as_context_mut(); - let mut params = mem::take(&mut store.data_mut().wasm_val_storage); + let mut params = mem::take(&mut store.as_context_mut().data_mut().wasm_val_storage); let (wt_params, wt_results) = translate_args(&mut params, args, results.len()); let result = func.call_async(&mut store, wt_params, wt_results).await; match result { Ok(()) => { for (slot, val) in results.iter_mut().zip(wt_results.iter()) { - crate::initialize(slot, wasmtime_val_t::from_val(val.clone())); + crate::initialize(slot, wasmtime_val_t::from_val(&mut store, *val)); } params.truncate(0); - store.data_mut().wasm_val_storage = params; + store.as_context_mut().data_mut().wasm_val_storage = params; } Err(err) => handle_call_error(err, trap_ret, err_ret), } @@ -227,7 +244,7 @@ async fn do_func_call_async( #[no_mangle] pub unsafe extern "C" fn wasmtime_func_call_async<'a>( - store: CStoreContextMut<'a>, + store: WasmtimeStoreContextMut<'a>, func: &'a Func, args: *const wasmtime_val_t, nargs: usize, @@ -236,12 +253,19 @@ pub unsafe extern "C" fn wasmtime_func_call_async<'a>( trap_ret: &'a mut *mut wasm_trap_t, err_ret: &'a mut *mut wasmtime_error_t, ) -> Box> { + let mut scope = RootScope::new(store); let args = crate::slice_from_raw_parts(args, nargs) .iter() - .map(|i| i.to_val()); + .map(|i| i.to_val(&mut scope)) + .collect::>(); let results = crate::slice_from_raw_parts_mut(results, nresults); let fut = Box::pin(do_func_call_async( - store, func, args, results, trap_ret, err_ret, + scope, + func, + args.into_iter(), + results, + trap_ret, + err_ret, )); Box::new(wasmtime_call_future_t { underlying: fut }) } @@ -258,7 +282,7 @@ pub unsafe extern "C" fn wasmtime_linker_define_async_func( data: *mut c_void, finalizer: Option, ) -> Option> { - let ty = ty.ty().ty.clone(); + let ty = ty.ty().ty(linker.linker.engine()); let module = to_str!(module, module_len); let name = to_str!(name, name_len); let cb = c_async_callback_to_rust_fn(callback, data, finalizer); @@ -271,7 +295,7 @@ pub unsafe extern "C" fn wasmtime_linker_define_async_func( async fn do_linker_instantiate_async( linker: &wasmtime_linker_t, - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, module: &wasmtime_module_t, instance_ptr: &mut Instance, trap_ret: &mut *mut wasm_trap_t, @@ -287,7 +311,7 @@ async fn do_linker_instantiate_async( #[no_mangle] pub extern "C" fn wasmtime_linker_instantiate_async<'a>( linker: &'a wasmtime_linker_t, - store: CStoreContextMut<'a>, + store: WasmtimeStoreContextMut<'a>, module: &'a wasmtime_module_t, instance_ptr: &'a mut Instance, trap_ret: &'a mut *mut wasm_trap_t, @@ -306,7 +330,7 @@ pub extern "C" fn wasmtime_linker_instantiate_async<'a>( async fn do_instance_pre_instantiate_async( instance_pre: &wasmtime_instance_pre_t, - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, instance_ptr: &mut Instance, trap_ret: &mut *mut wasm_trap_t, err_ret: &mut *mut wasmtime_error_t, @@ -321,7 +345,7 @@ async fn do_instance_pre_instantiate_async( #[no_mangle] pub extern "C" fn wasmtime_instance_pre_instantiate_async<'a>( instance_pre: &'a wasmtime_instance_pre_t, - store: CStoreContextMut<'a>, + store: WasmtimeStoreContextMut<'a>, instance_ptr: &'a mut Instance, trap_ret: &'a mut *mut wasm_trap_t, err_ret: &'a mut *mut wasmtime_error_t, @@ -335,3 +359,92 @@ pub extern "C" fn wasmtime_instance_pre_instantiate_async<'a>( )); Box::new(crate::wasmtime_call_future_t { underlying: fut }) } + +pub type wasmtime_stack_memory_get_callback_t = + extern "C" fn(env: *mut std::ffi::c_void, out_len: &mut usize) -> *mut u8; + +#[repr(C)] +pub struct wasmtime_stack_memory_t { + env: *mut std::ffi::c_void, + get_stack_memory: wasmtime_stack_memory_get_callback_t, + finalizer: Option, +} + +struct CHostStackMemory { + foreign: crate::ForeignData, + get_memory: wasmtime_stack_memory_get_callback_t, +} +unsafe impl Send for CHostStackMemory {} +unsafe impl Sync for CHostStackMemory {} +unsafe impl StackMemory for CHostStackMemory { + fn top(&self) -> *mut u8 { + let mut len = 0; + let cb = self.get_memory; + cb(self.foreign.data, &mut len) + } + fn range(&self) -> Range { + let mut len = 0; + let cb = self.get_memory; + let top = cb(self.foreign.data, &mut len); + let base = unsafe { top.sub(len) as usize }; + base..base + len + } +} + +pub type wasmtime_new_stack_memory_callback_t = extern "C" fn( + env: *mut std::ffi::c_void, + size: usize, + stack_ret: &mut wasmtime_stack_memory_t, +) -> Option>; + +#[repr(C)] +pub struct wasmtime_stack_creator_t { + env: *mut std::ffi::c_void, + new_stack: wasmtime_new_stack_memory_callback_t, + finalizer: Option, +} + +struct CHostStackCreator { + foreign: crate::ForeignData, + new_stack: wasmtime_new_stack_memory_callback_t, +} +unsafe impl Send for CHostStackCreator {} +unsafe impl Sync for CHostStackCreator {} +unsafe impl StackCreator for CHostStackCreator { + fn new_stack(&self, size: usize) -> Result> { + extern "C" fn panic_callback(_env: *mut std::ffi::c_void, _out_len: &mut usize) -> *mut u8 { + panic!("a callback must be set"); + } + let mut out = wasmtime_stack_memory_t { + env: ptr::null_mut(), + get_stack_memory: panic_callback, + finalizer: None, + }; + let cb = self.new_stack; + let result = cb(self.foreign.data, size, &mut out); + match result { + Some(error) => Err((*error).into()), + None => Ok(Box::new(CHostStackMemory { + foreign: crate::ForeignData { + data: out.env, + finalizer: out.finalizer, + }, + get_memory: out.get_stack_memory, + })), + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_config_host_stack_creator_set( + c: &mut wasm_config_t, + creator: &wasmtime_stack_creator_t, +) { + c.config.with_host_stack(Arc::new(CHostStackCreator { + foreign: crate::ForeignData { + data: creator.env, + finalizer: creator.finalizer, + }, + new_stack: creator.new_stack, + })); +} diff --git a/crates/c-api/src/config.rs b/crates/c-api/src/config.rs index 76fe4546b69b..4ce46768a7d1 100644 --- a/crates/c-api/src/config.rs +++ b/crates/c-api/src/config.rs @@ -3,9 +3,9 @@ #![cfg_attr(not(feature = "cache"), allow(unused_imports))] use crate::{handle_result, wasm_memorytype_t, wasmtime_error_t}; -use std::mem::MaybeUninit; use std::ops::Range; use std::os::raw::c_char; +use std::ptr; use std::{ffi::CStr, sync::Arc}; use wasmtime::{ Config, LinearMemory, MemoryCreator, OptLevel, ProfilingStrategy, Result, Strategy, @@ -71,15 +71,34 @@ pub extern "C" fn wasmtime_config_max_wasm_stack_set(c: &mut wasm_config_t, size } #[no_mangle] +#[cfg(feature = "threads")] pub extern "C" fn wasmtime_config_wasm_threads_set(c: &mut wasm_config_t, enable: bool) { c.config.wasm_threads(enable); } +#[no_mangle] +pub extern "C" fn wasmtime_config_wasm_tail_call_set(c: &mut wasm_config_t, enable: bool) { + c.config.wasm_tail_call(enable); +} + #[no_mangle] pub extern "C" fn wasmtime_config_wasm_reference_types_set(c: &mut wasm_config_t, enable: bool) { c.config.wasm_reference_types(enable); } +#[no_mangle] +pub extern "C" fn wasmtime_config_wasm_function_references_set( + c: &mut wasm_config_t, + enable: bool, +) { + c.config.wasm_function_references(enable); +} + +#[no_mangle] +pub extern "C" fn wasmtime_config_wasm_gc_set(c: &mut wasm_config_t, enable: bool) { + c.config.wasm_gc(enable); +} + #[no_mangle] pub extern "C" fn wasmtime_config_wasm_simd_set(c: &mut wasm_config_t, enable: bool) { c.config.wasm_simd(enable); @@ -119,6 +138,7 @@ pub extern "C" fn wasmtime_config_wasm_memory64_set(c: &mut wasm_config_t, enabl } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub extern "C" fn wasmtime_config_strategy_set( c: &mut wasm_config_t, strategy: wasmtime_strategy_t, @@ -137,6 +157,7 @@ pub extern "C" fn wasmtime_config_parallel_compilation_set(c: &mut wasm_config_t } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub extern "C" fn wasmtime_config_cranelift_debug_verifier_set( c: &mut wasm_config_t, enable: bool, @@ -145,6 +166,7 @@ pub extern "C" fn wasmtime_config_cranelift_debug_verifier_set( } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub extern "C" fn wasmtime_config_cranelift_nan_canonicalization_set( c: &mut wasm_config_t, enable: bool, @@ -153,6 +175,7 @@ pub extern "C" fn wasmtime_config_cranelift_nan_canonicalization_set( } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub extern "C" fn wasmtime_config_cranelift_opt_level_set( c: &mut wasm_config_t, opt_level: wasmtime_opt_level_t, @@ -232,6 +255,7 @@ pub extern "C" fn wasmtime_config_native_unwind_info_set(c: &mut wasm_config_t, } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub unsafe extern "C" fn wasmtime_config_target_set( c: &mut wasm_config_t, target: *const c_char, @@ -241,6 +265,12 @@ pub unsafe extern "C" fn wasmtime_config_target_set( } #[no_mangle] +pub extern "C" fn wasmtime_config_macos_use_mach_ports_set(c: &mut wasm_config_t, enabled: bool) { + c.config.macos_use_mach_ports(enabled); +} + +#[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub unsafe extern "C" fn wasmtime_config_cranelift_flag_enable( c: &mut wasm_config_t, flag: *const c_char, @@ -250,6 +280,7 @@ pub unsafe extern "C" fn wasmtime_config_cranelift_flag_enable( } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub unsafe extern "C" fn wasmtime_config_cranelift_flag_set( c: &mut wasm_config_t, flag: *const c_char, @@ -362,7 +393,25 @@ unsafe impl MemoryCreator for CHostMemoryCreator { reserved_size_in_bytes: Option, guard_size_in_bytes: usize, ) -> Result, String> { - let mut memory = MaybeUninit::uninit(); + extern "C" fn panic_get_callback( + _env: *mut std::ffi::c_void, + _byte_size: &mut usize, + _maximum_byte_size: &mut usize, + ) -> *mut u8 { + panic!("a callback must be set"); + } + extern "C" fn panic_grow_callback( + _env: *mut std::ffi::c_void, + _size: usize, + ) -> Option> { + panic!("a callback must be set"); + } + let mut memory = wasmtime_linear_memory_t { + env: ptr::null_mut(), + get_memory: panic_get_callback, + grow_memory: panic_grow_callback, + finalizer: None, + }; let cb = self.new_memory; let error = cb( self.foreign.data, @@ -371,11 +420,10 @@ unsafe impl MemoryCreator for CHostMemoryCreator { maximum.unwrap_or(usize::MAX), reserved_size_in_bytes.unwrap_or(0), guard_size_in_bytes, - memory.as_mut_ptr(), + &mut memory, ); match error { None => { - let memory = unsafe { memory.assume_init() }; let foreign = crate::ForeignData { data: memory.env, finalizer: memory.finalizer, @@ -388,7 +436,7 @@ unsafe impl MemoryCreator for CHostMemoryCreator { } Some(err) => { let err: anyhow::Error = (*err).into(); - Err(format!("{}", err)) + Err(format!("{err}")) } } } @@ -407,3 +455,8 @@ pub unsafe extern "C" fn wasmtime_config_host_memory_creator_set( new_memory: creator.new_memory, })); } + +#[no_mangle] +pub extern "C" fn wasmtime_config_memory_init_cow_set(c: &mut wasm_config_t, enable: bool) { + c.config.memory_init_cow(enable); +} diff --git a/crates/c-api/src/engine.rs b/crates/c-api/src/engine.rs index 67785fc03fc5..e8cea19e7571 100644 --- a/crates/c-api/src/engine.rs +++ b/crates/c-api/src/engine.rs @@ -19,6 +19,7 @@ pub extern "C" fn wasm_engine_new() -> Box { // Note that we `drop` the result here since this fails after the first // initialization attempt. We don't mind that though because this function // can be called multiple times, so we just ignore the result. + #[cfg(feature = "logging")] drop(env_logger::try_init()); Box::new(wasm_engine_t { @@ -28,12 +29,20 @@ pub extern "C" fn wasm_engine_new() -> Box { #[no_mangle] pub extern "C" fn wasm_engine_new_with_config(c: Box) -> Box { + #[cfg(feature = "logging")] + drop(env_logger::try_init()); + let config = c.config; Box::new(wasm_engine_t { engine: Engine::new(&config).unwrap(), }) } +#[no_mangle] +pub extern "C" fn wasmtime_engine_clone(engine: &wasm_engine_t) -> Box { + Box::new(engine.clone()) +} + #[no_mangle] pub extern "C" fn wasmtime_engine_increment_epoch(engine: &wasm_engine_t) { engine.engine.increment_epoch(); diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs index 516e21b28467..77aba7818d5c 100644 --- a/crates/c-api/src/extern.rs +++ b/crates/c-api/src/extern.rs @@ -1,13 +1,13 @@ use crate::{ wasm_externkind_t, wasm_externtype_t, wasm_func_t, wasm_global_t, wasm_memory_t, wasm_table_t, - CStoreContext, StoreRef, + WasmStoreRef, WasmtimeStoreContext, }; use std::mem::ManuallyDrop; -use wasmtime::{Extern, Func, Global, Memory, Table}; +use wasmtime::{Extern, Func, Global, Memory, SharedMemory, Table}; #[derive(Clone)] pub struct wasm_extern_t { - pub(crate) store: StoreRef, + pub(crate) store: WasmStoreRef, pub(crate) which: Extern, } @@ -26,7 +26,9 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t { #[no_mangle] pub unsafe extern "C" fn wasm_extern_type(e: &wasm_extern_t) -> Box { - Box::new(wasm_externtype_t::new(e.which.ty(&e.store.context()))) + Box::new(wasm_externtype_t::from_extern_type( + e.which.ty(&e.store.context()), + )) } #[no_mangle] @@ -80,6 +82,7 @@ pub const WASMTIME_EXTERN_FUNC: wasmtime_extern_kind_t = 0; 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; #[repr(C)] pub union wasmtime_extern_union { @@ -87,6 +90,17 @@ pub union wasmtime_extern_union { pub table: Table, pub global: Global, pub memory: Memory, + pub sharedmemory: ManuallyDrop>, +} + +impl Drop for wasmtime_extern_t { + fn drop(&mut self) { + if self.kind == WASMTIME_EXTERN_SHAREDMEMORY { + unsafe { + ManuallyDrop::drop(&mut self.of.sharedmemory); + } + } + } } impl wasmtime_extern_t { @@ -96,7 +110,8 @@ impl wasmtime_extern_t { WASMTIME_EXTERN_GLOBAL => Extern::Global(self.of.global), WASMTIME_EXTERN_TABLE => Extern::Table(self.of.table), WASMTIME_EXTERN_MEMORY => Extern::Memory(self.of.memory), - other => panic!("unknown wasm_extern_kind_t: {}", other), + WASMTIME_EXTERN_SHAREDMEMORY => Extern::SharedMemory((**self.of.sharedmemory).clone()), + other => panic!("unknown wasmtime_extern_kind_t: {other}"), } } } @@ -120,7 +135,12 @@ impl From for wasmtime_extern_t { kind: WASMTIME_EXTERN_MEMORY, of: wasmtime_extern_union { memory }, }, - Extern::SharedMemory(_memory) => todo!(), + Extern::SharedMemory(sharedmemory) => wasmtime_extern_t { + kind: WASMTIME_EXTERN_SHAREDMEMORY, + of: wasmtime_extern_union { + sharedmemory: ManuallyDrop::new(Box::new(sharedmemory)), + }, + }, } } } @@ -132,8 +152,8 @@ pub unsafe extern "C" fn wasmtime_extern_delete(e: &mut ManuallyDrop, + store: WasmtimeStoreContext<'_>, e: &wasmtime_extern_t, ) -> Box { - Box::new(wasm_externtype_t::new(e.to_extern().ty(store))) + Box::new(wasm_externtype_t::from_extern_type(e.to_extern().ty(store))) } diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 8fbec441da29..0e85ee9b90c9 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -1,8 +1,9 @@ -use crate::wasm_trap_t; use crate::{ wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t, wasmtime_error_t, - wasmtime_extern_t, wasmtime_val_t, wasmtime_val_union, CStoreContext, CStoreContextMut, + wasmtime_extern_t, wasmtime_val_t, wasmtime_val_union, WasmtimeStoreContext, + WasmtimeStoreContextMut, }; +use crate::{wasm_trap_t, WasmtimeCaller, WasmtimeStoreData}; use anyhow::{Error, Result}; use std::any::Any; use std::ffi::c_void; @@ -10,7 +11,10 @@ use std::mem::{self, MaybeUninit}; use std::panic::{self, AssertUnwindSafe}; use std::ptr; use std::str; -use wasmtime::{AsContextMut, Caller, Extern, Func, Trap, Val, ValRaw}; +use wasmtime::{ + AsContext, AsContextMut, Extern, Func, RootScope, StoreContext, StoreContextMut, Trap, Val, + ValRaw, +}; #[derive(Clone)] #[repr(transparent)] @@ -55,7 +59,7 @@ unsafe fn create_function( + Sync + 'static, ) -> Box { - let ty = ty.ty().ty.clone(); + let ty = ty.ty().ty(store.store.context().engine()); let func = Func::new( store.store.context_mut(), ty, @@ -122,7 +126,7 @@ pub(crate) fn translate_args<'a>( let num_args = args.len(); dst.reserve(args.len() + results_size); dst.extend(args); - dst.extend((0..results_size).map(|_| Val::null())); + dst.extend((0..results_size).map(|_| Val::null_func_ref())); let (a, b) = dst.split_at_mut(num_args); (a, b) } @@ -200,7 +204,21 @@ pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t { #[repr(C)] pub struct wasmtime_caller_t<'a> { - pub(crate) caller: Caller<'a, crate::StoreData>, + pub(crate) caller: WasmtimeCaller<'a>, +} + +impl AsContext for wasmtime_caller_t<'_> { + type Data = WasmtimeStoreData; + + fn as_context(&self) -> StoreContext<'_, WasmtimeStoreData> { + self.caller.as_context() + } +} + +impl AsContextMut for wasmtime_caller_t<'_> { + fn as_context_mut(&mut self) -> StoreContextMut<'_, WasmtimeStoreData> { + self.caller.as_context_mut() + } } pub type wasmtime_func_callback_t = extern "C" fn( @@ -221,14 +239,14 @@ pub type wasmtime_func_unchecked_callback_t = extern "C" fn( #[no_mangle] pub unsafe extern "C" fn wasmtime_func_new( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, ty: &wasm_functype_t, callback: wasmtime_func_callback_t, data: *mut c_void, finalizer: Option, func: &mut Func, ) { - let ty = ty.ty().ty.clone(); + let ty = ty.ty().ty(store.engine()); let cb = c_callback_to_rust_fn(callback, data, finalizer); let f = Func::new(store, ty, cb); *func = f; @@ -238,7 +256,7 @@ pub(crate) unsafe fn c_callback_to_rust_fn( callback: wasmtime_func_callback_t, data: *mut c_void, finalizer: Option, -) -> impl Fn(Caller<'_, crate::StoreData>, &[Val], &mut [Val]) -> Result<()> { +) -> impl Fn(WasmtimeCaller<'_>, &[Val], &mut [Val]) -> Result<()> { let foreign = crate::ForeignData { data, finalizer }; move |mut caller, params, results| { let _ = &foreign; // move entire foreign into this closure @@ -249,7 +267,12 @@ pub(crate) unsafe fn c_callback_to_rust_fn( let mut vals = mem::take(&mut caller.data_mut().hostcall_val_storage); debug_assert!(vals.is_empty()); vals.reserve(params.len() + results.len()); - vals.extend(params.iter().cloned().map(|p| wasmtime_val_t::from_val(p))); + vals.extend( + params + .iter() + .cloned() + .map(|p| wasmtime_val_t::from_val_unscoped(&mut caller, p)), + ); vals.extend((0..results.len()).map(|_| wasmtime_val_t { kind: crate::WASMTIME_I32, of: wasmtime_val_union { i32: 0 }, @@ -272,7 +295,7 @@ pub(crate) unsafe fn c_callback_to_rust_fn( // Translate the `wasmtime_val_t` results into the `results` space for (i, result) in out_results.iter().enumerate() { - results[i] = result.to_val(); + results[i] = result.to_val_unscoped(&mut caller); } // Move our `vals` storage back into the store now that we no longer @@ -286,14 +309,14 @@ pub(crate) unsafe fn c_callback_to_rust_fn( #[no_mangle] pub unsafe extern "C" fn wasmtime_func_new_unchecked( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, ty: &wasm_functype_t, callback: wasmtime_func_unchecked_callback_t, data: *mut c_void, finalizer: Option, func: &mut Func, ) { - let ty = ty.ty().ty.clone(); + let ty = ty.ty().ty(store.engine()); let cb = c_unchecked_callback_to_rust_fn(callback, data, finalizer); *func = Func::new_unchecked(store, ty, cb); } @@ -302,7 +325,7 @@ pub(crate) unsafe fn c_unchecked_callback_to_rust_fn( callback: wasmtime_func_unchecked_callback_t, data: *mut c_void, finalizer: Option, -) -> impl Fn(Caller<'_, crate::StoreData>, &mut [ValRaw]) -> Result<()> { +) -> impl Fn(WasmtimeCaller<'_>, &mut [ValRaw]) -> Result<()> { let foreign = crate::ForeignData { data, finalizer }; move |caller, values| { let _ = &foreign; // move entire foreign into this closure @@ -316,7 +339,7 @@ pub(crate) unsafe fn c_unchecked_callback_to_rust_fn( #[no_mangle] pub unsafe extern "C" fn wasmtime_func_call( - mut store: CStoreContextMut<'_>, + mut store: WasmtimeStoreContextMut<'_>, func: &Func, args: *const wasmtime_val_t, nargs: usize, @@ -324,13 +347,13 @@ pub unsafe extern "C" fn wasmtime_func_call( nresults: usize, trap_ret: &mut *mut wasm_trap_t, ) -> Option> { - let mut store = store.as_context_mut(); - let mut params = mem::take(&mut store.data_mut().wasm_val_storage); + let mut scope = RootScope::new(&mut store); + let mut params = mem::take(&mut scope.as_context_mut().data_mut().wasm_val_storage); let (wt_params, wt_results) = translate_args( &mut params, crate::slice_from_raw_parts(args, nargs) .iter() - .map(|i| i.to_val()), + .map(|i| i.to_val(&mut scope)), nresults, ); @@ -339,16 +362,16 @@ pub unsafe extern "C" fn wasmtime_func_call( // can. As a result we catch panics here and transform them to traps to // allow the caller to have any insulation possible against Rust panics. let result = panic::catch_unwind(AssertUnwindSafe(|| { - func.call(&mut store, wt_params, wt_results) + func.call(&mut scope, wt_params, wt_results) })); match result { Ok(Ok(())) => { let results = crate::slice_from_raw_parts_mut(results, nresults); for (slot, val) in results.iter_mut().zip(wt_results.iter()) { - crate::initialize(slot, wasmtime_val_t::from_val(val.clone())); + crate::initialize(slot, wasmtime_val_t::from_val(&mut scope, *val)); } params.truncate(0); - store.data_mut().wasm_val_storage = params; + scope.as_context_mut().data_mut().wasm_val_storage = params; None } Ok(Err(trap)) => store_err(trap, trap_ret), @@ -362,7 +385,7 @@ pub unsafe extern "C" fn wasmtime_func_call( #[no_mangle] pub unsafe extern "C" fn wasmtime_func_call_unchecked( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, func: &Func, args_and_results: *mut ValRaw, args_and_results_len: usize, @@ -385,7 +408,7 @@ fn store_err(err: Error, trap_ret: &mut *mut wasm_trap_t) -> Option, + store: WasmtimeStoreContext<'_>, func: &Func, ) -> Box { Box::new(wasm_functype_t::new(func.ty(store))) @@ -394,7 +417,7 @@ pub extern "C" fn wasmtime_func_type( #[no_mangle] pub extern "C" fn wasmtime_caller_context<'a>( caller: &'a mut wasmtime_caller_t, -) -> CStoreContextMut<'a> { +) -> WasmtimeStoreContextMut<'a> { caller.caller.as_context_mut() } @@ -419,7 +442,7 @@ pub unsafe extern "C" fn wasmtime_caller_export_get( #[no_mangle] pub unsafe extern "C" fn wasmtime_func_from_raw( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, raw: *mut c_void, func: &mut Func, ) { @@ -428,7 +451,7 @@ pub unsafe extern "C" fn wasmtime_func_from_raw( #[no_mangle] pub unsafe extern "C" fn wasmtime_func_to_raw( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, func: &Func, ) -> *mut c_void { func.to_raw(store) diff --git a/crates/c-api/src/global.rs b/crates/c-api/src/global.rs index 653855b52894..073dfd2172cb 100644 --- a/crates/c-api/src/global.rs +++ b/crates/c-api/src/global.rs @@ -1,9 +1,9 @@ use crate::{ handle_result, wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t, wasmtime_error_t, - wasmtime_val_t, CStoreContext, CStoreContextMut, + wasmtime_val_t, WasmtimeStoreContext, WasmtimeStoreContextMut, }; use std::mem::MaybeUninit; -use wasmtime::{Extern, Global}; +use wasmtime::{Extern, Global, RootScope}; #[derive(Clone)] #[repr(transparent)] @@ -79,12 +79,14 @@ pub unsafe extern "C" fn wasm_global_set(g: &mut wasm_global_t, val: &wasm_val_t #[no_mangle] pub unsafe extern "C" fn wasmtime_global_new( - store: CStoreContextMut<'_>, + mut store: WasmtimeStoreContextMut<'_>, gt: &wasm_globaltype_t, val: &wasmtime_val_t, ret: &mut Global, ) -> Option> { - let global = Global::new(store, gt.ty().ty.clone(), val.to_val()); + let mut scope = RootScope::new(&mut store); + let val = val.to_val(&mut scope); + let global = Global::new(scope, gt.ty().ty.clone(), val); handle_result(global, |global| { *ret = global; }) @@ -92,7 +94,7 @@ pub unsafe extern "C" fn wasmtime_global_new( #[no_mangle] pub extern "C" fn wasmtime_global_type( - store: CStoreContext<'_>, + store: WasmtimeStoreContext<'_>, global: &Global, ) -> Box { Box::new(wasm_globaltype_t::new(global.ty(store))) @@ -100,18 +102,22 @@ pub extern "C" fn wasmtime_global_type( #[no_mangle] pub extern "C" fn wasmtime_global_get( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, global: &Global, val: &mut MaybeUninit, ) { - crate::initialize(val, wasmtime_val_t::from_val(global.get(store))) + let mut scope = RootScope::new(store); + let gval = global.get(&mut scope); + crate::initialize(val, wasmtime_val_t::from_val(&mut scope, gval)) } #[no_mangle] pub unsafe extern "C" fn wasmtime_global_set( - store: CStoreContextMut<'_>, + mut store: WasmtimeStoreContextMut<'_>, global: &Global, val: &wasmtime_val_t, ) -> Option> { - handle_result(global.set(store, val.to_val()), |()| {}) + let mut scope = RootScope::new(&mut store); + let val = val.to_val(&mut scope); + handle_result(global.set(scope, val), |()| {}) } diff --git a/crates/c-api/src/instance.rs b/crates/c-api/src/instance.rs index b8ae5bc82341..f38d151d36f3 100644 --- a/crates/c-api/src/instance.rs +++ b/crates/c-api/src/instance.rs @@ -1,20 +1,20 @@ use crate::{ wasm_extern_t, wasm_extern_vec_t, wasm_module_t, wasm_store_t, wasm_trap_t, wasmtime_error_t, - wasmtime_extern_t, wasmtime_module_t, CStoreContextMut, StoreData, StoreRef, + wasmtime_extern_t, wasmtime_module_t, WasmStoreRef, WasmtimeStoreContextMut, WasmtimeStoreData, }; use std::mem::MaybeUninit; use wasmtime::{Instance, InstancePre, Trap}; #[derive(Clone)] pub struct wasm_instance_t { - store: StoreRef, + store: WasmStoreRef, instance: Instance, } wasmtime_c_api_macros::declare_ref!(wasm_instance_t); impl wasm_instance_t { - pub(crate) fn new(store: StoreRef, instance: Instance) -> wasm_instance_t { + pub(crate) fn new(store: WasmStoreRef, instance: Instance) -> wasm_instance_t { wasm_instance_t { store, instance } } } @@ -70,7 +70,7 @@ pub unsafe extern "C" fn wasm_instance_exports( #[no_mangle] pub unsafe extern "C" fn wasmtime_instance_new( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, module: &wasmtime_module_t, imports: *const wasmtime_extern_t, nimports: usize, @@ -111,7 +111,7 @@ pub(crate) fn handle_instantiate( #[no_mangle] pub unsafe extern "C" fn wasmtime_instance_export_get( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, instance: &Instance, name: *const u8, name_len: usize, @@ -133,7 +133,7 @@ pub unsafe extern "C" fn wasmtime_instance_export_get( #[no_mangle] pub unsafe extern "C" fn wasmtime_instance_export_nth( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, instance: &Instance, index: usize, name_ptr: &mut *const u8, @@ -153,7 +153,7 @@ pub unsafe extern "C" fn wasmtime_instance_export_nth( #[repr(transparent)] pub struct wasmtime_instance_pre_t { - pub(crate) underlying: InstancePre, + pub(crate) underlying: InstancePre, } #[no_mangle] @@ -163,10 +163,18 @@ pub unsafe extern "C" fn wasmtime_instance_pre_delete(_instance_pre: Box, + store: WasmtimeStoreContextMut<'_>, instance_ptr: &mut Instance, trap_ptr: &mut *mut wasm_trap_t, ) -> Option> { let result = instance_pre.underlying.instantiate(store); handle_instantiate(result, instance_ptr, trap_ptr) } + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_instance_pre_module( + instance_pre: &wasmtime_instance_pre_t, +) -> Box { + let module = instance_pre.underlying.module().clone(); + Box::new(wasmtime_module_t { module }) +} diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index ea4696e9387e..652bcf83e51e 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -1,8 +1,11 @@ //! This crate is the implementation of Wasmtime's C API. //! -//! This crate is not intended to be used from Rust itself, for that see the -//! `wasmtime` crate. Otherwise this is typically compiled as a -//! cdylib/staticlib. Documentation for this crate largely lives in the header +//! This crate is normally not intended to be used from Rust itself. For that, +//! see the `wasmtime` crate. It is possible to use this crate via Cargo, for +//! Rust crates that wrap C libraries that use wasmtime. Most often, this crate +//! is compiled as a cdylib or staticlib, via the `wasmtime-c-api` crate. +//! +//! Documentation for this crate largely lives in the header //! files of the `include` directory for this crate. //! //! At a high level this crate implements the `wasm.h` API with some gymnastics, @@ -11,7 +14,7 @@ #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] -pub use wasmtime::*; +pub use wasmtime; mod config; mod engine; @@ -23,7 +26,10 @@ mod instance; mod linker; mod memory; mod module; +#[cfg(feature = "profiling")] +mod profiling; mod r#ref; +mod sharedmemory; mod store; mod table; mod trap; @@ -114,3 +120,8 @@ unsafe fn slice_from_raw_parts_mut<'a, T>(ptr: *mut T, len: usize) -> &'a mut [T std::slice::from_raw_parts_mut(ptr, len) } } + +pub(crate) fn abort(name: &str) -> ! { + eprintln!("`{name}` is not implemented"); + std::process::abort(); +} diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 7cf9391d8f8a..b20104abcc3a 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -1,6 +1,7 @@ use crate::{ bad_utf8, handle_result, wasm_engine_t, wasm_functype_t, wasm_trap_t, wasmtime_error_t, - wasmtime_extern_t, wasmtime_instance_pre_t, wasmtime_module_t, CStoreContext, CStoreContextMut, + wasmtime_extern_t, wasmtime_instance_pre_t, wasmtime_module_t, WasmtimeStoreContext, + WasmtimeStoreContextMut, }; use std::ffi::c_void; use std::mem::MaybeUninit; @@ -9,7 +10,7 @@ use wasmtime::{Func, Instance, Linker}; #[repr(C)] pub struct wasmtime_linker_t { - pub(crate) linker: Linker, + pub(crate) linker: Linker, } wasmtime_c_api_macros::declare_own!(wasmtime_linker_t); @@ -21,6 +22,13 @@ pub extern "C" fn wasmtime_linker_new(engine: &wasm_engine_t) -> Box Box { + Box::new(wasmtime_linker_t { + linker: linker.linker.clone(), + }) +} + #[no_mangle] pub extern "C" fn wasmtime_linker_allow_shadowing( linker: &mut wasmtime_linker_t, @@ -43,7 +51,7 @@ pub(crate) use to_str; #[no_mangle] pub unsafe extern "C" fn wasmtime_linker_define( linker: &mut wasmtime_linker_t, - store: CStoreContext<'_>, + store: WasmtimeStoreContext<'_>, module: *const u8, module_len: usize, name: *const u8, @@ -69,7 +77,7 @@ pub unsafe extern "C" fn wasmtime_linker_define_func( data: *mut c_void, finalizer: Option, ) -> Option> { - let ty = ty.ty().ty.clone(); + let ty = ty.ty().ty(linker.linker.engine()); let module = to_str!(module, module_len); let name = to_str!(name, name_len); let cb = crate::func::c_callback_to_rust_fn(callback, data, finalizer); @@ -88,7 +96,7 @@ pub unsafe extern "C" fn wasmtime_linker_define_func_unchecked( data: *mut c_void, finalizer: Option, ) -> Option> { - let ty = ty.ty().ty.clone(); + let ty = ty.ty().ty(linker.linker.engine()); let module = to_str!(module, module_len); let name = to_str!(name, name_len); let cb = crate::func::c_unchecked_callback_to_rust_fn(callback, data, finalizer); @@ -104,10 +112,8 @@ pub extern "C" fn wasmtime_linker_define_wasi( linker: &mut wasmtime_linker_t, ) -> Option> { handle_result( - wasmtime_wasi::add_to_linker(&mut linker.linker, |cx| { - cx.wasi.as_mut().expect( - "failed to define WASI on linker; did you set a WASI configuration in the store?", - ) + wasmtime_wasi::preview1::add_to_linker_sync(&mut linker.linker, |ctx| { + ctx.wasi.as_mut().expect("wasi context must be populated") }), |_linker| (), ) @@ -116,7 +122,7 @@ pub extern "C" fn wasmtime_linker_define_wasi( #[no_mangle] pub unsafe extern "C" fn wasmtime_linker_define_instance( linker: &mut wasmtime_linker_t, - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, name: *const u8, name_len: usize, instance: &Instance, @@ -129,7 +135,7 @@ pub unsafe extern "C" fn wasmtime_linker_define_instance( #[no_mangle] pub extern "C" fn wasmtime_linker_instantiate( linker: &wasmtime_linker_t, - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, module: &wasmtime_module_t, instance_ptr: &mut Instance, trap_ptr: &mut *mut wasm_trap_t, @@ -154,7 +160,7 @@ pub unsafe extern "C" fn wasmtime_linker_instantiate_pre( #[no_mangle] pub unsafe extern "C" fn wasmtime_linker_module( linker: &mut wasmtime_linker_t, - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, name: *const u8, name_len: usize, module: &wasmtime_module_t, @@ -167,7 +173,7 @@ pub unsafe extern "C" fn wasmtime_linker_module( #[no_mangle] pub unsafe extern "C" fn wasmtime_linker_get_default( linker: &wasmtime_linker_t, - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, name: *const u8, name_len: usize, func: &mut Func, @@ -180,7 +186,7 @@ pub unsafe extern "C" fn wasmtime_linker_get_default( #[no_mangle] pub unsafe extern "C" fn wasmtime_linker_get( linker: &wasmtime_linker_t, - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, module: *const u8, module_len: usize, name: *const u8, diff --git a/crates/c-api/src/memory.rs b/crates/c-api/src/memory.rs index 299dbae7bb68..4d2cb82e9b98 100644 --- a/crates/c-api/src/memory.rs +++ b/crates/c-api/src/memory.rs @@ -1,6 +1,6 @@ use crate::{ - handle_result, wasm_extern_t, wasm_memorytype_t, wasm_store_t, wasmtime_error_t, CStoreContext, - CStoreContextMut, + handle_result, wasm_extern_t, wasm_memorytype_t, wasm_store_t, wasmtime_error_t, + WasmtimeStoreContext, WasmtimeStoreContextMut, }; use std::convert::TryFrom; use wasmtime::{Extern, Memory}; @@ -88,7 +88,7 @@ pub unsafe extern "C" fn wasm_memory_grow( #[no_mangle] pub extern "C" fn wasmtime_memory_new( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, ty: &wasm_memorytype_t, ret: &mut Memory, ) -> Option> { @@ -97,30 +97,33 @@ pub extern "C" fn wasmtime_memory_new( #[no_mangle] pub extern "C" fn wasmtime_memory_type( - store: CStoreContext<'_>, + store: WasmtimeStoreContext<'_>, mem: &Memory, ) -> Box { Box::new(wasm_memorytype_t::new(mem.ty(store))) } #[no_mangle] -pub extern "C" fn wasmtime_memory_data(store: CStoreContext<'_>, mem: &Memory) -> *const u8 { +pub extern "C" fn wasmtime_memory_data(store: WasmtimeStoreContext<'_>, mem: &Memory) -> *const u8 { mem.data(store).as_ptr() } #[no_mangle] -pub extern "C" fn wasmtime_memory_data_size(store: CStoreContext<'_>, mem: &Memory) -> usize { +pub extern "C" fn wasmtime_memory_data_size( + store: WasmtimeStoreContext<'_>, + mem: &Memory, +) -> usize { mem.data(store).len() } #[no_mangle] -pub extern "C" fn wasmtime_memory_size(store: CStoreContext<'_>, mem: &Memory) -> u64 { +pub extern "C" fn wasmtime_memory_size(store: WasmtimeStoreContext<'_>, mem: &Memory) -> u64 { mem.size(store) } #[no_mangle] pub extern "C" fn wasmtime_memory_grow( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, mem: &Memory, delta: u64, prev_size: &mut u64, diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs index 3a1a6e6664cc..47250fb710d4 100644 --- a/crates/c-api/src/module.rs +++ b/crates/c-api/src/module.rs @@ -1,6 +1,6 @@ use crate::{ handle_result, wasm_byte_vec_t, wasm_engine_t, wasm_exporttype_t, wasm_exporttype_vec_t, - wasm_importtype_t, wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t, + wasm_importtype_t, wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t, CExternType, }; use anyhow::Context; use std::ffi::CStr; @@ -29,6 +29,7 @@ pub struct wasm_shared_module_t { wasmtime_c_api_macros::declare_own!(wasm_shared_module_t); #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub unsafe extern "C" fn wasm_module_new( store: &mut wasm_store_t, binary: &wasm_byte_vec_t, @@ -40,6 +41,7 @@ pub unsafe extern "C" fn wasm_module_new( } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub unsafe extern "C" fn wasm_module_validate( store: &mut wasm_store_t, binary: &wasm_byte_vec_t, @@ -53,7 +55,7 @@ fn fill_exports(module: &Module, out: &mut wasm_exporttype_vec_t) { .map(|e| { Some(Box::new(wasm_exporttype_t::new( e.name().to_owned(), - e.ty(), + CExternType::new(e.ty()), ))) }) .collect::>(); @@ -67,7 +69,7 @@ fn fill_imports(module: &Module, out: &mut wasm_importtype_vec_t) { Some(Box::new(wasm_importtype_t::new( i.module().to_owned(), i.name().to_owned(), - i.ty(), + CExternType::new(i.ty()), ))) }) .collect::>(); @@ -105,6 +107,7 @@ pub unsafe extern "C" fn wasm_module_obtain( } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub extern "C" fn wasm_module_serialize(module: &wasm_module_t, ret: &mut wasm_byte_vec_t) { if let Ok(buf) = module.module.serialize() { ret.set_buffer(buf); @@ -130,6 +133,7 @@ pub struct wasmtime_module_t { wasmtime_c_api_macros::declare_own!(wasmtime_module_t); #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub unsafe extern "C" fn wasmtime_module_new( engine: &wasm_engine_t, wasm: *const u8, @@ -166,6 +170,7 @@ pub extern "C" fn wasmtime_module_imports( } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub unsafe extern "C" fn wasmtime_module_validate( engine: &wasm_engine_t, wasm: *const u8, @@ -176,6 +181,7 @@ pub unsafe extern "C" fn wasmtime_module_validate( } #[no_mangle] +#[cfg(any(feature = "cranelift", feature = "winch"))] pub extern "C" fn wasmtime_module_serialize( module: &wasmtime_module_t, ret: &mut wasm_byte_vec_t, @@ -186,8 +192,8 @@ pub extern "C" fn wasmtime_module_serialize( #[no_mangle] pub extern "C" fn wasmtime_module_image_range( module: &wasmtime_module_t, - start: &mut usize, - end: &mut usize, + start: &mut *const u8, + end: &mut *const u8, ) { let range = module.module.image_range(); *start = range.start; diff --git a/crates/c-api/src/profiling.rs b/crates/c-api/src/profiling.rs new file mode 100644 index 000000000000..1b59b6c2e50a --- /dev/null +++ b/crates/c-api/src/profiling.rs @@ -0,0 +1,67 @@ +use crate::{wasm_byte_vec_t, wasm_name_t, wasmtime_error_t, wasmtime_module_t, wasmtime_store_t}; +use std::slice; +use std::str::from_utf8; +use std::time::Duration; +use wasmtime::GuestProfiler; + +pub struct wasmtime_guestprofiler_t { + guest_profiler: GuestProfiler, +} + +wasmtime_c_api_macros::declare_own!(wasmtime_guestprofiler_t); + +#[repr(C)] +pub struct wasmtime_guestprofiler_modules_t<'a> { + name: &'a wasm_name_t, + module: &'a wasmtime_module_t, +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_guestprofiler_new( + module_name: &wasm_name_t, + interval_nanos: u64, + modules: *const wasmtime_guestprofiler_modules_t, + modules_len: usize, +) -> Box { + let module_name = from_utf8(&module_name.as_slice()).expect("not valid utf-8"); + let list = slice::from_raw_parts(modules, modules_len) + .iter() + .map(|entry| { + ( + from_utf8(entry.name.as_slice()) + .expect("not valid utf-8") + .to_owned(), + entry.module.module.clone(), + ) + }) + .collect(); + Box::new(wasmtime_guestprofiler_t { + guest_profiler: GuestProfiler::new(module_name, Duration::from_nanos(interval_nanos), list), + }) +} + +#[no_mangle] +pub extern "C" fn wasmtime_guestprofiler_sample( + guestprofiler: &mut wasmtime_guestprofiler_t, + store: &wasmtime_store_t, + delta_nanos: u64, +) { + guestprofiler + .guest_profiler + .sample(&store.store, Duration::from_nanos(delta_nanos)); +} + +#[no_mangle] +pub extern "C" fn wasmtime_guestprofiler_finish( + guestprofiler: Box, + out: &mut wasm_byte_vec_t, +) -> Option> { + let mut buf = vec![]; + match guestprofiler.guest_profiler.finish(&mut buf) { + Ok(()) => { + out.set_buffer(buf); + None + } + Err(e) => Some(Box::new(e.into())), + } +} diff --git a/crates/c-api/src/ref.rs b/crates/c-api/src/ref.rs index 7e1d3912b52d..44f14c3f9945 100644 --- a/crates/c-api/src/ref.rs +++ b/crates/c-api/src/ref.rs @@ -1,5 +1,6 @@ -use std::os::raw::c_void; -use wasmtime::{ExternRef, Func, Val}; +use crate::{abort, WasmtimeStoreContextMut}; +use std::{mem::MaybeUninit, num::NonZeroU64, os::raw::c_void, ptr}; +use wasmtime::{AnyRef, ExternRef, ManuallyRooted, Ref, RootScope, Val, I31}; /// `*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 @@ -15,34 +16,23 @@ use wasmtime::{ExternRef, Func, Val}; /// regular, non-`repr(C)` `enum` to define `WasmRefInner`. #[derive(Clone)] pub struct wasm_ref_t { - pub(crate) r: WasmRefInner, -} - -#[derive(Clone)] -pub(crate) enum WasmRefInner { - ExternRef(ExternRef), - FuncRef(Func), + pub(crate) r: Ref, } wasmtime_c_api_macros::declare_own!(wasm_ref_t); -pub(crate) fn ref_to_val(r: &wasm_ref_t) -> Val { - match &r.r { - WasmRefInner::ExternRef(x) => Val::ExternRef(Some(x.clone())), - WasmRefInner::FuncRef(f) => Val::FuncRef(Some(f.clone())), +impl wasm_ref_t { + pub(crate) fn new(r: Ref) -> Option> { + if r.is_null() || !r.is_func() { + None + } else { + Some(Box::new(wasm_ref_t { r })) + } } } -pub(crate) fn val_into_ref(val: Val) -> Option> { - match val { - Val::ExternRef(Some(x)) => Some(Box::new(wasm_ref_t { - r: WasmRefInner::ExternRef(x), - })), - Val::FuncRef(Some(f)) => Some(Box::new(wasm_ref_t { - r: WasmRefInner::FuncRef(f), - })), - _ => None, - } +pub(crate) fn ref_to_val(r: &wasm_ref_t) -> Val { + Val::from(r.r.clone()) } #[no_mangle] @@ -50,20 +40,10 @@ pub extern "C" fn wasm_ref_copy(r: Option<&wasm_ref_t>) -> Option ! { - eprintln!("`{}` is not implemented", name); - std::process::abort(); -} - #[no_mangle] -pub extern "C" fn wasm_ref_same(a: Option<&wasm_ref_t>, b: Option<&wasm_ref_t>) -> bool { - match (a.map(|a| &a.r), b.map(|b| &b.r)) { - (Some(WasmRefInner::ExternRef(a)), Some(WasmRefInner::ExternRef(b))) => a.ptr_eq(b), - (None, None) => true, - // Note: we don't support equality for `Func`, so we always return - // `false` for `funcref`s. - _ => false, - } +pub extern "C" fn wasm_ref_same(_a: Option<&wasm_ref_t>, _b: Option<&wasm_ref_t>) -> bool { + // We need a store to determine whether these are the same reference or not. + abort("wasm_ref_same") } #[no_mangle] @@ -201,3 +181,215 @@ wasmtime_c_api_macros::declare_ref!(wasm_foreign_t); pub extern "C" fn wasm_foreign_new(_store: &crate::wasm_store_t) -> Box { abort("wasm_foreign_new") } + +/// C-API representation of `anyref`. +/// +/// This represented differently in the C API from the header to handle how +/// this is dispatched internally. Null anyref values are represented with a +/// `store_id` of zero, and otherwise the `rooted` field is valid. +/// +/// Note that this relies on the Wasmtime definition of `ManuallyRooted` to have +/// a 64-bit store_id first. +macro_rules! ref_wrapper { + ($wasmtime:ident => $c:ident) => { + pub struct $c { + store_id: u64, + a: u32, + b: u32, + } + + impl $c { + pub unsafe fn as_wasmtime(&self) -> Option> { + let store_id = NonZeroU64::new(self.store_id)?; + Some(ManuallyRooted::from_raw_parts_for_c_api( + store_id, self.a, self.b, + )) + } + } + + impl From>> for $c { + fn from(rooted: Option>) -> $c { + let mut ret = $c { + store_id: 0, + a: 0, + b: 0, + }; + if let Some(rooted) = rooted { + let (store_id, a, b) = rooted.into_parts_for_c_api(); + ret.store_id = store_id.get(); + ret.a = a; + ret.b = b; + } + ret + } + } + }; +} + +ref_wrapper!(AnyRef => wasmtime_anyref_t); +ref_wrapper!(ExternRef => wasmtime_externref_t); + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_anyref_clone( + cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, + out: &mut MaybeUninit, +) { + let anyref = anyref.and_then(|a| a.as_wasmtime()).map(|a| a.clone(cx)); + crate::initialize(out, anyref.into()); +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_anyref_unroot( + cx: WasmtimeStoreContextMut<'_>, + val: Option<&mut MaybeUninit>, +) { + if let Some(val) = val.and_then(|v| v.assume_init_read().as_wasmtime()) { + val.unroot(cx); + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_anyref_to_raw( + cx: WasmtimeStoreContextMut<'_>, + val: Option<&wasmtime_anyref_t>, +) -> u32 { + val.and_then(|v| v.as_wasmtime()) + .and_then(|e| e.to_raw(cx).ok()) + .unwrap_or_default() +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_anyref_from_raw( + cx: WasmtimeStoreContextMut<'_>, + raw: u32, + val: &mut MaybeUninit, +) { + let mut scope = RootScope::new(cx); + let anyref = AnyRef::from_raw(&mut scope, raw) + .map(|a| a.to_manually_rooted(&mut scope).expect("in scope")); + crate::initialize(val, anyref.into()); +} + +#[no_mangle] +pub extern "C" fn wasmtime_anyref_from_i31( + cx: WasmtimeStoreContextMut<'_>, + val: u32, + out: &mut MaybeUninit, +) { + let mut scope = RootScope::new(cx); + let anyref = AnyRef::from_i31(&mut scope, I31::wrapping_u32(val)); + let anyref = anyref.to_manually_rooted(&mut scope).expect("in scope"); + crate::initialize(out, Some(anyref).into()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_anyref_i31_get_u( + cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, + dst: &mut MaybeUninit, +) -> bool { + match anyref.and_then(|a| a.as_wasmtime()) { + Some(anyref) if anyref.is_i31(&cx).expect("ManuallyRooted always in scope") => { + let val = anyref + .unwrap_i31(&cx) + .expect("ManuallyRooted always in scope") + .get_u32(); + crate::initialize(dst, val); + true + } + _ => false, + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_anyref_i31_get_s( + cx: WasmtimeStoreContextMut<'_>, + anyref: Option<&wasmtime_anyref_t>, + dst: &mut MaybeUninit, +) -> bool { + match anyref.and_then(|a| a.as_wasmtime()) { + Some(anyref) if anyref.is_i31(&cx).expect("ManuallyRooted always in scope") => { + let val = anyref + .unwrap_i31(&cx) + .expect("ManuallyRooted always in scope") + .get_i32(); + crate::initialize(dst, val); + true + } + _ => false, + } +} + +#[no_mangle] +pub extern "C" fn wasmtime_externref_new( + cx: WasmtimeStoreContextMut<'_>, + data: *mut c_void, + finalizer: Option, + out: &mut MaybeUninit, +) -> bool { + let mut scope = RootScope::new(cx); + let e = match ExternRef::new(&mut scope, crate::ForeignData { data, finalizer }) { + Ok(e) => e, + Err(_) => return false, + }; + let e = e.to_manually_rooted(&mut scope).expect("in scope"); + crate::initialize(out, Some(e).into()); + true +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_externref_data( + cx: WasmtimeStoreContextMut<'_>, + externref: Option<&wasmtime_externref_t>, +) -> *mut c_void { + externref + .and_then(|e| e.as_wasmtime()) + .and_then(|e| { + let data = e.data(cx).ok()?; + Some(data.downcast_ref::().unwrap().data) + }) + .unwrap_or(ptr::null_mut()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_externref_clone( + cx: WasmtimeStoreContextMut<'_>, + externref: Option<&wasmtime_externref_t>, + out: &mut MaybeUninit, +) { + let externref = externref.and_then(|e| e.as_wasmtime()).map(|e| e.clone(cx)); + crate::initialize(out, externref.into()); +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_externref_unroot( + cx: WasmtimeStoreContextMut<'_>, + val: Option<&mut MaybeUninit>, +) { + if let Some(val) = val.and_then(|v| v.assume_init_read().as_wasmtime()) { + val.unroot(cx); + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_externref_to_raw( + cx: WasmtimeStoreContextMut<'_>, + val: Option<&wasmtime_externref_t>, +) -> u32 { + val.and_then(|e| e.as_wasmtime()) + .and_then(|e| e.to_raw(cx).ok()) + .unwrap_or_default() +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_externref_from_raw( + cx: WasmtimeStoreContextMut<'_>, + raw: u32, + val: &mut MaybeUninit, +) { + let mut scope = RootScope::new(cx); + let rooted = ExternRef::from_raw(&mut scope, raw) + .map(|e| e.to_manually_rooted(&mut scope).expect("in scope")); + crate::initialize(val, rooted.into()); +} diff --git a/crates/c-api/src/sharedmemory.rs b/crates/c-api/src/sharedmemory.rs new file mode 100644 index 000000000000..43d5076a935b --- /dev/null +++ b/crates/c-api/src/sharedmemory.rs @@ -0,0 +1,60 @@ +use crate::{handle_result, wasm_memorytype_t, wasmtime_error_t}; +use std::cell::UnsafeCell; +use wasmtime::SharedMemory; + +type wasmtime_sharedmemory_t = SharedMemory; + +wasmtime_c_api_macros::declare_own!(wasmtime_sharedmemory_t); + +#[no_mangle] +#[cfg(feature = "threads")] +pub extern "C" fn wasmtime_sharedmemory_new( + engine: &crate::wasm_engine_t, + ty: &wasm_memorytype_t, + ret: &mut *mut wasmtime_sharedmemory_t, +) -> Option> { + handle_result( + SharedMemory::new(&engine.engine, ty.ty().ty.clone()), + |mem| *ret = Box::::into_raw(Box::new(mem)), + ) +} + +#[no_mangle] +pub extern "C" fn wasmtime_sharedmemory_clone( + mem: &wasmtime_sharedmemory_t, +) -> Box { + Box::new(mem.clone()) +} + +#[no_mangle] +pub extern "C" fn wasmtime_sharedmemory_type( + mem: &wasmtime_sharedmemory_t, +) -> Box { + Box::new(wasm_memorytype_t::new(mem.ty())) +} + +#[no_mangle] +pub extern "C" fn wasmtime_sharedmemory_data( + mem: &wasmtime_sharedmemory_t, +) -> *const UnsafeCell { + mem.data().as_ptr() +} + +#[no_mangle] +pub extern "C" fn wasmtime_sharedmemory_data_size(mem: &wasmtime_sharedmemory_t) -> usize { + mem.data().len() +} + +#[no_mangle] +pub extern "C" fn wasmtime_sharedmemory_size(mem: &wasmtime_sharedmemory_t) -> u64 { + mem.size() +} + +#[no_mangle] +pub extern "C" fn wasmtime_sharedmemory_grow( + mem: &wasmtime_sharedmemory_t, + delta: u64, + prev_size: &mut u64, +) -> Option> { + handle_result(mem.grow(delta), |prev| *prev_size = prev) +} diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs index d56977619888..bffe3c9a3463 100644 --- a/crates/c-api/src/store.rs +++ b/crates/c-api/src/store.rs @@ -3,11 +3,19 @@ use std::cell::UnsafeCell; use std::ffi::c_void; use std::sync::Arc; use wasmtime::{ - AsContext, AsContextMut, Store, StoreContext, StoreContextMut, StoreLimits, StoreLimitsBuilder, - UpdateDeadline, Val, + AsContext, AsContextMut, Caller, Store, StoreContext, StoreContextMut, StoreLimits, + StoreLimitsBuilder, UpdateDeadline, Val, }; -/// This representation of a `Store` is used to implement the `wasm.h` API. +// Store-related type aliases for `wasm.h` APIs. Not for use with `wasmtime.h` +// APIs! +pub type WasmStoreData = (); +pub type WasmStore = Store; +pub type WasmStoreContext<'a> = StoreContext<'a, WasmStoreData>; +pub type WasmStoreContextMut<'a> = StoreContextMut<'a, WasmStoreData>; + +/// This representation of a `Store` is used to implement the `wasm.h` API (and +/// *not* the `wasmtime.h` API!) /// /// This is stored alongside `Func` and such for `wasm.h` so each object is /// independently owned. The usage of `Arc` here is mostly to just get it to be @@ -18,16 +26,16 @@ use wasmtime::{ /// The aliasing requirements are documented in the C API `wasm.h` itself (at /// least Wasmtime's implementation). #[derive(Clone)] -pub struct StoreRef { - store: Arc>>, +pub struct WasmStoreRef { + store: Arc>, } -impl StoreRef { - pub unsafe fn context(&self) -> StoreContext<'_, ()> { +impl WasmStoreRef { + pub unsafe fn context(&self) -> WasmStoreContext<'_> { (*self.store.get()).as_context() } - pub unsafe fn context_mut(&mut self) -> StoreContextMut<'_, ()> { + pub unsafe fn context_mut(&mut self) -> WasmStoreContextMut<'_> { (*self.store.get()).as_context_mut() } } @@ -35,7 +43,7 @@ impl StoreRef { #[repr(C)] #[derive(Clone)] pub struct wasm_store_t { - pub(crate) store: StoreRef, + pub(crate) store: WasmStoreRef, } wasmtime_c_api_macros::declare_own!(wasm_store_t); @@ -45,33 +53,37 @@ pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box { let engine = &engine.engine; let store = Store::new(engine, ()); Box::new(wasm_store_t { - store: StoreRef { + store: WasmStoreRef { store: Arc::new(UnsafeCell::new(store)), }, }) } +// Store-related type aliases for `wasmtime.h` APIs. Not for use with `wasm.h` +// APIs! +pub type WasmtimeStore = Store; +pub type WasmtimeStoreContext<'a> = StoreContext<'a, WasmtimeStoreData>; +pub type WasmtimeStoreContextMut<'a> = StoreContextMut<'a, WasmtimeStoreData>; +pub type WasmtimeCaller<'a> = Caller<'a, WasmtimeStoreData>; + /// Representation of a `Store` for `wasmtime.h` This notably tries to move more /// burden of aliasing on the caller rather than internally, allowing for a more /// raw representation of contexts and such that requires less `unsafe` in the /// implementation. /// -/// Note that this notably carries `StoreData` as a payload which allows storing -/// foreign data and configuring WASI as well. +/// Note that this notably carries `WasmtimeStoreData` as a payload which allows +/// storing foreign data and configuring WASI as well. #[repr(C)] pub struct wasmtime_store_t { - pub(crate) store: Store, + pub(crate) store: WasmtimeStore, } wasmtime_c_api_macros::declare_own!(wasmtime_store_t); -pub type CStoreContext<'a> = StoreContext<'a, StoreData>; -pub type CStoreContextMut<'a> = StoreContextMut<'a, StoreData>; - -pub struct StoreData { +pub struct WasmtimeStoreData { foreign: crate::ForeignData, #[cfg(feature = "wasi")] - pub(crate) wasi: Option, + pub(crate) wasi: Option, /// Temporary storage for usage during a wasm->host call to store values /// in a slice we pass to the C API. @@ -94,7 +106,7 @@ pub extern "C" fn wasmtime_store_new( Box::new(wasmtime_store_t { store: Store::new( &engine.engine, - StoreData { + WasmtimeStoreData { foreign: ForeignData { data, finalizer }, #[cfg(feature = "wasi")] wasi: None, @@ -106,50 +118,51 @@ pub extern "C" fn wasmtime_store_new( }) } -// Internal structure to add Send/Sync to the c_void member. -#[derive(Debug)] -pub struct CallbackDataPtr { - pub ptr: *mut c_void, -} - -impl CallbackDataPtr { - fn as_mut_ptr(&self) -> *mut c_void { - self.ptr - } -} - -unsafe impl Send for CallbackDataPtr {} -unsafe impl Sync for CallbackDataPtr {} +pub type wasmtime_update_deadline_kind_t = u8; +pub const WASMTIME_UPDATE_DEADLINE_CONTINUE: wasmtime_update_deadline_kind_t = 0; +pub const WASMTIME_UPDATE_DEADLINE_YIELD: wasmtime_update_deadline_kind_t = 1; #[no_mangle] pub extern "C" fn wasmtime_store_epoch_deadline_callback( store: &mut wasmtime_store_t, func: extern "C" fn( - CStoreContextMut<'_>, + WasmtimeStoreContextMut<'_>, *mut c_void, *mut u64, + *mut wasmtime_update_deadline_kind_t, ) -> Option>, data: *mut c_void, + finalizer: Option, ) { - let sendable = CallbackDataPtr { ptr: data }; + let foreign = crate::ForeignData { data, finalizer }; store.store.epoch_deadline_callback(move |mut store_ctx| { + let _ = &foreign; // Move foreign into this closure let mut delta: u64 = 0; + let mut kind = WASMTIME_UPDATE_DEADLINE_CONTINUE; let result = (func)( store_ctx.as_context_mut(), - sendable.as_mut_ptr(), + foreign.data, &mut delta as *mut u64, + &mut kind as *mut wasmtime_update_deadline_kind_t, ); match result { Some(err) => Err(wasmtime::Error::from(>::into(*err))), - None => Ok(UpdateDeadline::Continue(delta)), + None if kind == WASMTIME_UPDATE_DEADLINE_CONTINUE => { + Ok(UpdateDeadline::Continue(delta)) + } + #[cfg(feature = "async")] + None if kind == WASMTIME_UPDATE_DEADLINE_YIELD => Ok(UpdateDeadline::Yield(delta)), + _ => panic!("unknown wasmtime_update_deadline_kind_t: {kind}"), } }); } #[no_mangle] -pub extern "C" fn wasmtime_store_context(store: &mut wasmtime_store_t) -> CStoreContextMut<'_> { +pub extern "C" fn wasmtime_store_context( + store: &mut wasmtime_store_t, +) -> WasmtimeStoreContextMut<'_> { store.store.as_context_mut() } @@ -183,19 +196,22 @@ pub extern "C" fn wasmtime_store_limiter( } #[no_mangle] -pub extern "C" fn wasmtime_context_get_data(store: CStoreContext<'_>) -> *mut c_void { +pub extern "C" fn wasmtime_context_get_data(store: WasmtimeStoreContext<'_>) -> *mut c_void { store.data().foreign.data } #[no_mangle] -pub extern "C" fn wasmtime_context_set_data(mut store: CStoreContextMut<'_>, data: *mut c_void) { +pub extern "C" fn wasmtime_context_set_data( + mut store: WasmtimeStoreContextMut<'_>, + data: *mut c_void, +) { store.data_mut().foreign.data = data; } #[cfg(feature = "wasi")] #[no_mangle] pub extern "C" fn wasmtime_context_set_wasi( - mut context: CStoreContextMut<'_>, + mut context: WasmtimeStoreContextMut<'_>, wasi: Box, ) -> Option> { crate::handle_result(wasi.into_wasi_ctx(), |wasi| { @@ -204,57 +220,31 @@ pub extern "C" fn wasmtime_context_set_wasi( } #[no_mangle] -pub extern "C" fn wasmtime_context_gc(mut context: CStoreContextMut<'_>) { +pub extern "C" fn wasmtime_context_gc(mut context: WasmtimeStoreContextMut<'_>) { context.gc(); } #[no_mangle] -pub extern "C" fn wasmtime_context_add_fuel( - mut store: CStoreContextMut<'_>, +pub extern "C" fn wasmtime_context_set_fuel( + mut store: WasmtimeStoreContextMut<'_>, fuel: u64, ) -> Option> { - crate::handle_result(store.add_fuel(fuel), |()| {}) + crate::handle_result(store.set_fuel(fuel), |()| {}) } #[no_mangle] -pub extern "C" fn wasmtime_context_fuel_consumed(store: CStoreContext<'_>, fuel: &mut u64) -> bool { - match store.fuel_consumed() { - Some(amt) => { - *fuel = amt; - true - } - None => false, - } -} - -#[no_mangle] -pub extern "C" fn wasmtime_context_fuel_remaining( - store: CStoreContextMut<'_>, +pub extern "C" fn wasmtime_context_get_fuel( + store: WasmtimeStoreContext<'_>, fuel: &mut u64, -) -> bool { - match store.fuel_remaining() { - Some(remaining) => { - *fuel = remaining; - true - } - None => false, - } -} - -#[no_mangle] -pub extern "C" fn wasmtime_context_consume_fuel( - mut store: CStoreContextMut<'_>, - fuel: u64, - remaining_fuel: &mut u64, ) -> Option> { - crate::handle_result(store.consume_fuel(fuel), |remaining| { - *remaining_fuel = remaining; + crate::handle_result(store.get_fuel(), |amt| { + *fuel = amt; }) } #[no_mangle] pub extern "C" fn wasmtime_context_set_epoch_deadline( - mut store: CStoreContextMut<'_>, + mut store: WasmtimeStoreContextMut<'_>, ticks_beyond_current: u64, ) { store.set_epoch_deadline(ticks_beyond_current); diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs index 304754fd757f..d7ff324580b8 100644 --- a/crates/c-api/src/table.rs +++ b/crates/c-api/src/table.rs @@ -1,10 +1,10 @@ -use crate::r#ref::{ref_to_val, val_into_ref}; use crate::{ handle_result, wasm_extern_t, wasm_ref_t, wasm_store_t, wasm_tabletype_t, wasmtime_error_t, - wasmtime_val_t, CStoreContext, CStoreContextMut, + wasmtime_val_t, WasmtimeStoreContext, WasmtimeStoreContextMut, }; +use anyhow::anyhow; use std::mem::MaybeUninit; -use wasmtime::{Extern, Table, TableType, Val, ValType}; +use wasmtime::{Extern, Ref, RootScope, Table, TableType}; #[derive(Clone)] #[repr(transparent)] @@ -32,15 +32,9 @@ impl wasm_table_t { } } -fn ref_to_val_for_table(r: Option<&wasm_ref_t>, table_ty: &TableType) -> Val { - r.map_or_else( - || match table_ty.element() { - ValType::FuncRef => Val::FuncRef(None), - ValType::ExternRef => Val::ExternRef(None), - ty => panic!("unsupported table element type: {:?}", ty), - }, - |r| ref_to_val(r), - ) +fn option_wasm_ref_t_to_ref(r: Option<&wasm_ref_t>, table_ty: &TableType) -> Ref { + r.map(|r| r.r.clone()) + .unwrap_or_else(|| Ref::null(table_ty.element().heap_type())) } #[no_mangle] @@ -49,8 +43,9 @@ pub unsafe extern "C" fn wasm_table_new( tt: &wasm_tabletype_t, init: Option<&wasm_ref_t>, ) -> Option> { - let init = ref_to_val_for_table(init, &tt.ty().ty); - let table = Table::new(store.store.context_mut(), tt.ty().ty.clone(), init).ok()?; + let tt = tt.ty().ty.clone(); + let init = option_wasm_ref_t_to_ref(init, &tt); + let table = Table::new(store.store.context_mut(), tt, init).ok()?; Some(Box::new(wasm_table_t { ext: wasm_extern_t { store: store.store.clone(), @@ -72,8 +67,8 @@ pub unsafe extern "C" fn wasm_table_get( index: wasm_table_size_t, ) -> Option> { let table = t.table(); - let val = table.get(t.ext.store.context_mut(), index)?; - val_into_ref(val) + let r = table.get(t.ext.store.context_mut(), index)?; + wasm_ref_t::new(r) } #[no_mangle] @@ -83,7 +78,7 @@ pub unsafe extern "C" fn wasm_table_set( r: Option<&wasm_ref_t>, ) -> bool { let table = t.table(); - let val = ref_to_val_for_table(r, &table.ty(t.ext.store.context())); + let val = option_wasm_ref_t_to_ref(r, &table.ty(t.ext.store.context())); table.set(t.ext.store.context_mut(), index, val).is_ok() } @@ -101,7 +96,7 @@ pub unsafe extern "C" fn wasm_table_grow( init: Option<&wasm_ref_t>, ) -> bool { let table = t.table(); - let init = ref_to_val_for_table(init, &table.ty(t.ext.store.context())); + let init = option_wasm_ref_t_to_ref(init, &table.ty(t.ext.store.context())); table.grow(t.ext.store.context_mut(), delta, init).is_ok() } @@ -117,20 +112,24 @@ pub extern "C" fn wasm_table_as_extern_const(t: &wasm_table_t) -> &wasm_extern_t #[no_mangle] pub unsafe extern "C" fn wasmtime_table_new( - store: CStoreContextMut<'_>, + mut store: WasmtimeStoreContextMut<'_>, tt: &wasm_tabletype_t, init: &wasmtime_val_t, out: &mut Table, ) -> Option> { + let mut scope = RootScope::new(&mut store); handle_result( - Table::new(store, tt.ty().ty.clone(), init.to_val()), + init.to_val(&mut scope) + .ref_() + .ok_or_else(|| anyhow!("wasmtime_table_new init value is not a reference")) + .and_then(|init| Table::new(scope, tt.ty().ty.clone(), init)), |table| *out = table, ) } #[no_mangle] pub unsafe extern "C" fn wasmtime_table_type( - store: CStoreContext<'_>, + store: WasmtimeStoreContext<'_>, table: &Table, ) -> Box { Box::new(wasm_tabletype_t::new(table.ty(store))) @@ -138,14 +137,15 @@ pub unsafe extern "C" fn wasmtime_table_type( #[no_mangle] pub extern "C" fn wasmtime_table_get( - store: CStoreContextMut<'_>, + store: WasmtimeStoreContextMut<'_>, table: &Table, index: u32, ret: &mut MaybeUninit, ) -> bool { - match table.get(store, index) { - Some(val) => { - crate::initialize(ret, wasmtime_val_t::from_val(val)); + let mut scope = RootScope::new(store); + match table.get(&mut scope, index) { + Some(r) => { + crate::initialize(ret, wasmtime_val_t::from_val(&mut scope, r.into())); true } None => false, @@ -154,28 +154,40 @@ pub extern "C" fn wasmtime_table_get( #[no_mangle] pub unsafe extern "C" fn wasmtime_table_set( - store: CStoreContextMut<'_>, + mut store: WasmtimeStoreContextMut<'_>, table: &Table, index: u32, val: &wasmtime_val_t, ) -> Option> { - handle_result(table.set(store, index, val.to_val()), |()| {}) + let mut scope = RootScope::new(&mut store); + handle_result( + val.to_val(&mut scope) + .ref_() + .ok_or_else(|| anyhow!("wasmtime_table_set value is not a reference")) + .and_then(|val| table.set(scope, index, val)), + |()| {}, + ) } #[no_mangle] -pub extern "C" fn wasmtime_table_size(store: CStoreContext<'_>, table: &Table) -> u32 { +pub extern "C" fn wasmtime_table_size(store: WasmtimeStoreContext<'_>, table: &Table) -> u32 { table.size(store) } #[no_mangle] pub unsafe extern "C" fn wasmtime_table_grow( - store: CStoreContextMut<'_>, + mut store: WasmtimeStoreContextMut<'_>, table: &Table, delta: u32, val: &wasmtime_val_t, prev_size: &mut u32, ) -> Option> { - handle_result(table.grow(store, delta, val.to_val()), |prev| { - *prev_size = prev - }) + let mut scope = RootScope::new(&mut store); + handle_result( + val.to_val(&mut scope) + .ref_() + .ok_or_else(|| anyhow!("wasmtime_table_grow value is not a reference")) + .and_then(|val| table.grow(scope, delta, val)), + |prev| *prev_size = prev, + ) } diff --git a/crates/c-api/src/types/export.rs b/crates/c-api/src/types/export.rs index 3d83cc41faca..2eca463d82b2 100644 --- a/crates/c-api/src/types/export.rs +++ b/crates/c-api/src/types/export.rs @@ -1,12 +1,11 @@ -use crate::{wasm_externtype_t, wasm_name_t}; +use crate::{wasm_externtype_t, wasm_name_t, CExternType}; use once_cell::unsync::OnceCell; -use wasmtime::ExternType; #[repr(C)] #[derive(Clone)] pub struct wasm_exporttype_t { name: String, - ty: ExternType, + ty: CExternType, name_cache: OnceCell, type_cache: OnceCell, } @@ -14,7 +13,7 @@ pub struct wasm_exporttype_t { wasmtime_c_api_macros::declare_ty!(wasm_exporttype_t); impl wasm_exporttype_t { - pub(crate) fn new(name: String, ty: ExternType) -> wasm_exporttype_t { + pub(crate) fn new(name: String, ty: CExternType) -> wasm_exporttype_t { wasm_exporttype_t { name, ty, @@ -31,7 +30,7 @@ pub extern "C" fn wasm_exporttype_new( ) -> Option> { let name = name.take(); let name = String::from_utf8(name).ok()?; - Some(Box::new(wasm_exporttype_t::new(name, ty.ty()))) + Some(Box::new(wasm_exporttype_t::new(name, ty.which.clone()))) } #[no_mangle] @@ -43,5 +42,5 @@ pub extern "C" fn wasm_exporttype_name(et: &wasm_exporttype_t) -> &wasm_name_t { #[no_mangle] pub extern "C" fn wasm_exporttype_type(et: &wasm_exporttype_t) -> &wasm_externtype_t { et.type_cache - .get_or_init(|| wasm_externtype_t::new(et.ty.clone())) + .get_or_init(|| wasm_externtype_t::from_cextern_type(et.ty.clone())) } diff --git a/crates/c-api/src/types/extern.rs b/crates/c-api/src/types/extern.rs index 21278a25e722..c85dcbb25cfd 100644 --- a/crates/c-api/src/types/extern.rs +++ b/crates/c-api/src/types/extern.rs @@ -18,6 +18,17 @@ pub(crate) enum CExternType { Table(CTableType), } +impl CExternType { + pub(crate) fn new(ty: ExternType) -> CExternType { + match ty { + ExternType::Func(f) => CExternType::Func(CFuncType::new(f)), + ExternType::Global(f) => CExternType::Global(CGlobalType::new(f)), + ExternType::Memory(f) => CExternType::Memory(CMemoryType::new(f)), + ExternType::Table(f) => CExternType::Table(CTableType::new(f)), + } + } +} + pub type wasm_externkind_t = u8; pub const WASM_EXTERN_FUNC: wasm_externkind_t = 0; @@ -26,24 +37,14 @@ pub const WASM_EXTERN_TABLE: wasm_externkind_t = 2; pub const WASM_EXTERN_MEMORY: wasm_externkind_t = 3; impl wasm_externtype_t { - pub(crate) fn new(ty: ExternType) -> wasm_externtype_t { + pub(crate) fn from_extern_type(ty: ExternType) -> wasm_externtype_t { wasm_externtype_t { - which: match ty { - ExternType::Func(f) => CExternType::Func(CFuncType::new(f)), - ExternType::Global(f) => CExternType::Global(CGlobalType::new(f)), - ExternType::Memory(f) => CExternType::Memory(CMemoryType::new(f)), - ExternType::Table(f) => CExternType::Table(CTableType::new(f)), - }, + which: CExternType::new(ty), } } - pub(crate) fn ty(&self) -> ExternType { - match &self.which { - CExternType::Func(f) => ExternType::Func(f.ty.clone()), - CExternType::Table(f) => ExternType::Table(f.ty.clone()), - CExternType::Global(f) => ExternType::Global(f.ty.clone()), - CExternType::Memory(f) => ExternType::Memory(f.ty.clone()), - } + pub(crate) fn from_cextern_type(ty: CExternType) -> wasm_externtype_t { + wasm_externtype_t { which: ty } } } diff --git a/crates/c-api/src/types/func.rs b/crates/c-api/src/types/func.rs index 1b4ca752febf..c2158902b2de 100644 --- a/crates/c-api/src/types/func.rs +++ b/crates/c-api/src/types/func.rs @@ -1,6 +1,10 @@ use crate::{wasm_externtype_t, wasm_valtype_t, wasm_valtype_vec_t, CExternType}; use once_cell::unsync::OnceCell; -use wasmtime::FuncType; +use std::{ + mem, + sync::{Arc, Mutex}, +}; +use wasmtime::{Engine, FuncType, ValType}; #[repr(transparent)] #[derive(Clone)] @@ -10,9 +14,75 @@ pub struct wasm_functype_t { wasmtime_c_api_macros::declare_ty!(wasm_functype_t); +#[derive(Clone)] +enum LazyFuncType { + Lazy { + params: Vec, + results: Vec, + }, + FuncType(FuncType), +} + +impl LazyFuncType { + pub(crate) fn force(&mut self, engine: &Engine) -> FuncType { + match self { + LazyFuncType::FuncType(ty) => ty.clone(), + LazyFuncType::Lazy { params, results } => { + let params = mem::take(params); + let results = mem::take(results); + let ty = FuncType::new(engine, params, results); + *self = LazyFuncType::FuncType(ty.clone()); + ty + } + } + } + + fn params(&self) -> impl ExactSizeIterator + '_ { + match self { + LazyFuncType::Lazy { params, .. } => LazyFuncTypeIter::Lazy(params.iter()), + LazyFuncType::FuncType(f) => LazyFuncTypeIter::FuncType(f.params()), + } + } + + fn results(&self) -> impl ExactSizeIterator + '_ { + match self { + LazyFuncType::Lazy { results, .. } => LazyFuncTypeIter::Lazy(results.iter()), + LazyFuncType::FuncType(f) => LazyFuncTypeIter::FuncType(f.results()), + } + } +} + +enum LazyFuncTypeIter<'a, T> { + Lazy(std::slice::Iter<'a, ValType>), + FuncType(T), +} + +impl<'a, T> Iterator for LazyFuncTypeIter<'a, T> +where + T: Iterator, +{ + type Item = ValType; + + fn next(&mut self) -> Option { + match self { + LazyFuncTypeIter::FuncType(i) => i.next(), + LazyFuncTypeIter::Lazy(i) => i.next().cloned(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match self { + LazyFuncTypeIter::FuncType(i) => i.size_hint(), + LazyFuncTypeIter::Lazy(i) => i.size_hint(), + } + } +} + +impl<'a, T> ExactSizeIterator for LazyFuncTypeIter<'a, T> where T: ExactSizeIterator {} + #[derive(Clone)] pub(crate) struct CFuncType { - pub(crate) ty: FuncType, + ty: Arc>, params_cache: OnceCell, returns_cache: OnceCell, } @@ -20,7 +90,15 @@ pub(crate) struct CFuncType { impl wasm_functype_t { pub(crate) fn new(ty: FuncType) -> wasm_functype_t { wasm_functype_t { - ext: wasm_externtype_t::new(ty.into()), + ext: wasm_externtype_t::from_extern_type(ty.into()), + } + } + + pub(crate) fn lazy(params: Vec, results: Vec) -> wasm_functype_t { + wasm_functype_t { + ext: wasm_externtype_t::from_cextern_type(CExternType::Func(CFuncType::lazy( + params, results, + ))), } } @@ -42,11 +120,24 @@ impl wasm_functype_t { impl CFuncType { pub(crate) fn new(ty: FuncType) -> CFuncType { CFuncType { - ty, + ty: Arc::new(Mutex::new(LazyFuncType::FuncType(ty))), params_cache: OnceCell::new(), returns_cache: OnceCell::new(), } } + + pub(crate) fn lazy(params: Vec, results: Vec) -> CFuncType { + CFuncType { + ty: Arc::new(Mutex::new(LazyFuncType::Lazy { params, results })), + params_cache: OnceCell::new(), + returns_cache: OnceCell::new(), + } + } + + pub(crate) fn ty(&self, engine: &Engine) -> FuncType { + let mut ty = self.ty.lock().unwrap(); + ty.force(engine) + } } #[no_mangle] @@ -54,18 +145,25 @@ pub extern "C" fn wasm_functype_new( params: &mut wasm_valtype_vec_t, results: &mut wasm_valtype_vec_t, ) -> Box { - let params = params.take().into_iter().map(|vt| vt.unwrap().ty.clone()); - let results = results.take().into_iter().map(|vt| vt.unwrap().ty.clone()); - let functype = FuncType::new(params, results); - Box::new(wasm_functype_t::new(functype)) + let params = params + .take() + .into_iter() + .map(|vt| vt.unwrap().ty.clone()) + .collect(); + let results = results + .take() + .into_iter() + .map(|vt| vt.unwrap().ty.clone()) + .collect(); + Box::new(wasm_functype_t::lazy(params, results)) } #[no_mangle] pub extern "C" fn wasm_functype_params(ft: &wasm_functype_t) -> &wasm_valtype_vec_t { let ft = ft.ty(); ft.params_cache.get_or_init(|| { - ft.ty - .params() + let ty = ft.ty.lock().unwrap(); + ty.params() .map(|p| Some(Box::new(wasm_valtype_t { ty: p.clone() }))) .collect::>() .into() @@ -76,8 +174,8 @@ pub extern "C" fn wasm_functype_params(ft: &wasm_functype_t) -> &wasm_valtype_ve pub extern "C" fn wasm_functype_results(ft: &wasm_functype_t) -> &wasm_valtype_vec_t { let ft = ft.ty(); ft.returns_cache.get_or_init(|| { - ft.ty - .results() + let ty = ft.ty.lock().unwrap(); + ty.results() .map(|p| Some(Box::new(wasm_valtype_t { ty: p.clone() }))) .collect::>() .into() diff --git a/crates/c-api/src/types/global.rs b/crates/c-api/src/types/global.rs index 9fde1f304dc1..176c41865036 100644 --- a/crates/c-api/src/types/global.rs +++ b/crates/c-api/src/types/global.rs @@ -24,7 +24,7 @@ pub(crate) struct CGlobalType { impl wasm_globaltype_t { pub(crate) fn new(ty: GlobalType) -> wasm_globaltype_t { wasm_globaltype_t { - ext: wasm_externtype_t::new(ty.into()), + ext: wasm_externtype_t::from_extern_type(ty.into()), } } diff --git a/crates/c-api/src/types/import.rs b/crates/c-api/src/types/import.rs index f9e97a287074..9484ab528070 100644 --- a/crates/c-api/src/types/import.rs +++ b/crates/c-api/src/types/import.rs @@ -1,13 +1,12 @@ -use crate::{wasm_externtype_t, wasm_name_t}; +use crate::{wasm_externtype_t, wasm_name_t, CExternType}; use once_cell::unsync::OnceCell; -use wasmtime::ExternType; #[repr(C)] #[derive(Clone)] pub struct wasm_importtype_t { pub(crate) module: String, pub(crate) name: String, - pub(crate) ty: ExternType, + pub(crate) ty: CExternType, module_cache: OnceCell, name_cache: OnceCell, type_cache: OnceCell, @@ -16,7 +15,7 @@ pub struct wasm_importtype_t { wasmtime_c_api_macros::declare_ty!(wasm_importtype_t); impl wasm_importtype_t { - pub(crate) fn new(module: String, name: String, ty: ExternType) -> wasm_importtype_t { + pub(crate) fn new(module: String, name: String, ty: CExternType) -> wasm_importtype_t { wasm_importtype_t { module, name, @@ -38,7 +37,11 @@ pub extern "C" fn wasm_importtype_new( let name = name.take(); let module = String::from_utf8(module).ok()?; let name = String::from_utf8(name).ok()?; - Some(Box::new(wasm_importtype_t::new(module, name, ty.ty()))) + Some(Box::new(wasm_importtype_t::new( + module, + name, + ty.which.clone(), + ))) } #[no_mangle] @@ -56,5 +59,5 @@ pub extern "C" fn wasm_importtype_name(it: &wasm_importtype_t) -> &wasm_name_t { #[no_mangle] pub extern "C" fn wasm_importtype_type(it: &wasm_importtype_t) -> &wasm_externtype_t { it.type_cache - .get_or_init(|| wasm_externtype_t::new(it.ty.clone())) + .get_or_init(|| wasm_externtype_t::from_cextern_type(it.ty.clone())) } diff --git a/crates/c-api/src/types/memory.rs b/crates/c-api/src/types/memory.rs index 45f9a5c19f99..bcdb933d7c56 100644 --- a/crates/c-api/src/types/memory.rs +++ b/crates/c-api/src/types/memory.rs @@ -20,7 +20,7 @@ pub(crate) struct CMemoryType { impl wasm_memorytype_t { pub(crate) fn new(ty: MemoryType) -> wasm_memorytype_t { wasm_memorytype_t { - ext: wasm_externtype_t::new(ty.into()), + ext: wasm_externtype_t::from_extern_type(ty.into()), } } @@ -108,6 +108,11 @@ pub extern "C" fn wasmtime_memorytype_is64(mt: &wasm_memorytype_t) -> bool { mt.ty().ty.is_64() } +#[no_mangle] +pub extern "C" fn wasmtime_memorytype_isshared(mt: &wasm_memorytype_t) -> bool { + mt.ty().ty.is_shared() +} + #[no_mangle] pub extern "C" fn wasm_memorytype_as_externtype(ty: &wasm_memorytype_t) -> &wasm_externtype_t { &ty.ext diff --git a/crates/c-api/src/types/table.rs b/crates/c-api/src/types/table.rs index 6599baecd10d..a31b5ef10720 100644 --- a/crates/c-api/src/types/table.rs +++ b/crates/c-api/src/types/table.rs @@ -1,6 +1,6 @@ use crate::{wasm_externtype_t, wasm_limits_t, wasm_valtype_t, CExternType}; use once_cell::unsync::OnceCell; -use wasmtime::TableType; +use wasmtime::{TableType, ValType}; #[repr(transparent)] #[derive(Clone)] @@ -20,7 +20,7 @@ pub(crate) struct CTableType { impl wasm_tabletype_t { pub(crate) fn new(ty: TableType) -> wasm_tabletype_t { wasm_tabletype_t { - ext: wasm_externtype_t::new(ty.into()), + ext: wasm_externtype_t::from_extern_type(ty.into()), } } @@ -53,19 +53,20 @@ impl CTableType { pub extern "C" fn wasm_tabletype_new( ty: Box, limits: &wasm_limits_t, -) -> Box { - Box::new(wasm_tabletype_t::new(TableType::new( - ty.ty, +) -> Option> { + let ty = ty.ty.as_ref()?.clone(); + Some(Box::new(wasm_tabletype_t::new(TableType::new( + ty, limits.min, limits.max(), - ))) + )))) } #[no_mangle] pub extern "C" fn wasm_tabletype_element(tt: &wasm_tabletype_t) -> &wasm_valtype_t { let tt = tt.ty(); tt.element_cache.get_or_init(|| wasm_valtype_t { - ty: tt.ty.element().clone(), + ty: ValType::Ref(tt.ty.element().clone()), }) } diff --git a/crates/c-api/src/types/val.rs b/crates/c-api/src/types/val.rs index 7508170e6c30..def5c556c0ba 100644 --- a/crates/c-api/src/types/val.rs +++ b/crates/c-api/src/types/val.rs @@ -1,4 +1,4 @@ -use wasmtime::ValType; +use wasmtime::{HeapType, ValType}; #[repr(C)] #[derive(Clone)] @@ -34,10 +34,10 @@ pub(crate) fn into_valtype(kind: wasm_valkind_t) -> ValType { WASM_I64 => ValType::I64, WASM_F32 => ValType::F32, WASM_F64 => ValType::F64, - WASM_EXTERNREF => ValType::ExternRef, - WASM_FUNCREF => ValType::FuncRef, + WASM_EXTERNREF => ValType::EXTERNREF, + WASM_FUNCREF => ValType::FUNCREF, WASMTIME_V128 => ValType::V128, - _ => panic!("unexpected kind: {}", kind), + _ => panic!("unexpected kind: {kind}"), } } @@ -47,9 +47,12 @@ pub(crate) fn from_valtype(ty: &ValType) -> wasm_valkind_t { ValType::I64 => WASM_I64, ValType::F32 => WASM_F32, ValType::F64 => WASM_F64, - ValType::ExternRef => WASM_EXTERNREF, - ValType::FuncRef => WASM_FUNCREF, ValType::V128 => WASMTIME_V128, + ValType::Ref(r) => match (r.is_nullable(), r.heap_type()) { + (true, HeapType::Extern) => WASM_EXTERNREF, + (true, HeapType::Func) => WASM_FUNCREF, + _ => crate::abort("support for non-externref and non-funcref references"), + }, } } @@ -61,3 +64,4 @@ pub const WASMTIME_F64: wasmtime_valkind_t = 3; 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; diff --git a/crates/c-api/src/val.rs b/crates/c-api/src/val.rs index 85165bf0719a..c7304e3dc985 100644 --- a/crates/c-api/src/val.rs +++ b/crates/c-api/src/val.rs @@ -1,12 +1,11 @@ -use crate::r#ref::{ref_to_val, WasmRefInner}; +use crate::r#ref::ref_to_val; use crate::{ - from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, wasmtime_valkind_t, CStoreContextMut, - WASM_I32, + from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, wasmtime_anyref_t, + wasmtime_externref_t, wasmtime_valkind_t, WasmtimeStoreContextMut, WASM_I32, }; -use std::ffi::c_void; -use std::mem::{self, ManuallyDrop, MaybeUninit}; +use std::mem::{ManuallyDrop, MaybeUninit}; use std::ptr; -use wasmtime::{ExternRef, Func, Val, ValType}; +use wasmtime::{AsContextMut, Func, HeapType, Ref, RootScope, Val, ValType}; #[repr(C)] pub struct wasm_val_t { @@ -29,7 +28,7 @@ pub union wasm_val_union { impl Drop for wasm_val_t { fn drop(&mut self) { match into_valtype(self.kind) { - ValType::FuncRef | ValType::ExternRef => unsafe { + ValType::Ref(_) => unsafe { if !self.of.ref_.is_null() { drop(Box::from_raw(self.of.ref_)); } @@ -47,7 +46,7 @@ impl Clone for wasm_val_t { }; unsafe { match into_valtype(self.kind) { - ValType::ExternRef | ValType::FuncRef if !self.of.ref_.is_null() => { + ValType::Ref(_) if !self.of.ref_.is_null() => { ret.of.ref_ = Box::into_raw(Box::new((*self.of.ref_).clone())); } _ => {} @@ -85,35 +84,19 @@ impl wasm_val_t { kind: from_valtype(&ValType::F64), of: wasm_val_union { u64: f }, }, - Val::ExternRef(None) => wasm_val_t { - kind: from_valtype(&ValType::ExternRef), + Val::FuncRef(f) => wasm_val_t { + kind: from_valtype(&ValType::FUNCREF), of: wasm_val_union { - ref_: ptr::null_mut(), + ref_: f.map_or(ptr::null_mut(), |f| { + Box::into_raw(Box::new(wasm_ref_t { + r: Ref::Func(Some(f)), + })) + }), }, }, - Val::ExternRef(Some(r)) => wasm_val_t { - kind: from_valtype(&ValType::ExternRef), - of: wasm_val_union { - ref_: Box::into_raw(Box::new(wasm_ref_t { - r: WasmRefInner::ExternRef(r), - })), - }, - }, - Val::FuncRef(None) => wasm_val_t { - kind: from_valtype(&ValType::FuncRef), - of: wasm_val_union { - ref_: ptr::null_mut(), - }, - }, - Val::FuncRef(Some(f)) => wasm_val_t { - kind: from_valtype(&ValType::FuncRef), - of: wasm_val_union { - ref_: Box::into_raw(Box::new(wasm_ref_t { - r: WasmRefInner::FuncRef(f), - })), - }, - }, - _ => unimplemented!("wasm_val_t::from_val {:?}", val), + Val::AnyRef(_) => crate::abort("creating a wasm_val_t from an anyref"), + Val::ExternRef(_) => crate::abort("creating a wasm_val_t from an externref"), + Val::V128(_) => crate::abort("creating a wasm_val_t from a v128"), } } @@ -123,21 +106,18 @@ impl wasm_val_t { ValType::I64 => Val::from(unsafe { self.of.i64 }), ValType::F32 => Val::from(unsafe { self.of.f32 }), ValType::F64 => Val::from(unsafe { self.of.f64 }), - ValType::ExternRef => unsafe { - if self.of.ref_.is_null() { - Val::ExternRef(None) - } else { - ref_to_val(&*self.of.ref_) - } - }, - ValType::FuncRef => unsafe { - if self.of.ref_.is_null() { - Val::FuncRef(None) - } else { - ref_to_val(&*self.of.ref_) - } + ValType::Ref(r) => match r.heap_type() { + HeapType::Func => unsafe { + if self.of.ref_.is_null() { + assert!(r.is_nullable()); + Val::FuncRef(None) + } else { + ref_to_val(&*self.of.ref_) + } + }, + _ => unreachable!("wasm_val_t cannot contain non-function reference values"), }, - _ => unimplemented!("wasm_val_t::val {:?}", self.kind), + ValType::V128 => unimplemented!("wasm_val_t: v128"), } } } @@ -164,20 +144,76 @@ pub union wasmtime_val_union { pub i64: i64, pub f32: u32, pub f64: u64, + pub anyref: ManuallyDrop, + pub externref: ManuallyDrop, pub funcref: wasmtime_func_t, - pub externref: ManuallyDrop>, pub v128: [u8; 16], } +const _: () = { + assert!(std::mem::size_of::() == 16); + assert!(std::mem::align_of::() == 8); +}; + +// The raw pointers are actually optional boxes. +unsafe impl Send for wasmtime_val_union +where + Option>: Send, + Option>: Send, +{ +} +unsafe impl Sync for wasmtime_val_union +where + Option>: Sync, + Option>: Sync, +{ +} + #[repr(C)] #[derive(Clone, Copy)] -pub struct wasmtime_func_t { - pub store_id: u64, - pub index: usize, +pub union wasmtime_func_t { + store_id: u64, + func: Func, +} + +impl wasmtime_func_t { + unsafe fn as_wasmtime(&self) -> Option { + if self.store_id == 0 { + None + } else { + Some(self.func) + } + } +} + +impl From> for wasmtime_func_t { + fn from(func: Option) -> wasmtime_func_t { + match func { + Some(func) => wasmtime_func_t { func }, + None => wasmtime_func_t { store_id: 0 }, + } + } } impl wasmtime_val_t { - pub fn from_val(val: Val) -> wasmtime_val_t { + /// Creates a new `wasmtime_val_t` from a `wasmtime::Val`. + /// + /// Note that this requires a `RootScope` to be present to serve as proof + /// that `val` is not require to be rooted in the store itself which would + /// prevent GC. Callers should prefer this API where possible, creating a + /// temporary `RootScope` when needed. + pub fn from_val(cx: &mut RootScope, val: Val) -> wasmtime_val_t { + Self::from_val_unscoped(cx, val) + } + + /// Equivalent of [`wasmtime_val_t::from_val`] except that a `RootScope` + /// is not required. + /// + /// This method should only be used when a `RootScope` is known to be + /// elsewhere on the stack. For example this is used when we call back out + /// to the embedder. In such a situation we know we previously entered with + /// some other call so the root scope is on the stack there. + pub fn from_val_unscoped(cx: impl AsContextMut, val: Val) -> wasmtime_val_t { match val { Val::I32(i) => wasmtime_val_t { kind: crate::WASMTIME_I32, @@ -195,22 +231,24 @@ impl wasmtime_val_t { kind: crate::WASMTIME_F64, of: wasmtime_val_union { f64: i }, }, - Val::ExternRef(i) => wasmtime_val_t { + Val::AnyRef(a) => wasmtime_val_t { + kind: crate::WASMTIME_ANYREF, + of: wasmtime_val_union { + anyref: ManuallyDrop::new(a.and_then(|a| a.to_manually_rooted(cx).ok()).into()), + }, + }, + Val::ExternRef(e) => wasmtime_val_t { kind: crate::WASMTIME_EXTERNREF, of: wasmtime_val_union { - externref: ManuallyDrop::new(i), + externref: ManuallyDrop::new( + e.and_then(|e| e.to_manually_rooted(cx).ok()).into(), + ), }, }, - Val::FuncRef(i) => wasmtime_val_t { + Val::FuncRef(func) => wasmtime_val_t { kind: crate::WASMTIME_FUNCREF, of: wasmtime_val_union { - funcref: match i { - Some(func) => unsafe { mem::transmute::(func) }, - None => wasmtime_func_t { - store_id: 0, - index: 0, - }, - }, + funcref: func.into(), }, }, Val::V128(val) => wasmtime_val_t { @@ -222,91 +260,68 @@ impl wasmtime_val_t { } } - pub unsafe fn to_val(&self) -> Val { + /// Convert this `wasmtime_val_t` into a `wasmtime::Val`. + /// + /// See [`wasmtime_val_t::from_val`] for notes on the `RootScope` + /// requirement here. Note that this is particularly meaningful for this + /// API as the `Val` returned may contain a `Rooted` which requires a + /// `RootScope` if we don't want the value to live for the entire lifetime + /// of the `Store`. + pub unsafe fn to_val(&self, cx: &mut RootScope) -> Val { + self.to_val_unscoped(cx) + } + + /// Equivalent of `to_val` except doesn't require a `RootScope`. + /// + /// See notes on [`wasmtime_val_t::from_val_unscoped`] for notes on when to + /// use this. + pub unsafe fn to_val_unscoped(&self, cx: impl AsContextMut) -> Val { match self.kind { crate::WASMTIME_I32 => Val::I32(self.of.i32), crate::WASMTIME_I64 => Val::I64(self.of.i64), crate::WASMTIME_F32 => Val::F32(self.of.f32), crate::WASMTIME_F64 => Val::F64(self.of.f64), crate::WASMTIME_V128 => Val::V128(u128::from_le_bytes(self.of.v128).into()), - crate::WASMTIME_FUNCREF => { - let store = self.of.funcref.store_id; - let index = self.of.funcref.index; - Val::FuncRef(if store == 0 && index == 0 { - None - } else { - Some(mem::transmute::(self.of.funcref)) - }) + crate::WASMTIME_ANYREF => { + Val::AnyRef(self.of.anyref.as_wasmtime().map(|a| a.to_rooted(cx))) } - crate::WASMTIME_EXTERNREF => Val::ExternRef((*self.of.externref).clone()), - other => panic!("unknown wasmtime_valkind_t: {}", other), + crate::WASMTIME_EXTERNREF => { + Val::ExternRef(self.of.externref.as_wasmtime().map(|e| e.to_rooted(cx))) + } + crate::WASMTIME_FUNCREF => Val::FuncRef(self.of.funcref.as_wasmtime()), + other => panic!("unknown wasmtime_valkind_t: {other}"), } } } -impl Drop for wasmtime_val_t { - fn drop(&mut self) { - if self.kind == crate::WASMTIME_EXTERNREF { - unsafe { - ManuallyDrop::drop(&mut self.of.externref); +#[no_mangle] +pub unsafe extern "C" fn wasmtime_val_unroot( + cx: WasmtimeStoreContextMut<'_>, + val: &mut MaybeUninit, +) { + let val = val.assume_init_read(); + match val.kind { + crate::WASMTIME_ANYREF => { + if let Some(val) = ManuallyDrop::into_inner(val.of.anyref).as_wasmtime() { + val.unroot(cx); + } + } + crate::WASMTIME_EXTERNREF => { + if let Some(val) = ManuallyDrop::into_inner(val.of.externref).as_wasmtime() { + val.unroot(cx); } } + _ => {} } } #[no_mangle] -pub unsafe extern "C" fn wasmtime_val_delete(val: &mut ManuallyDrop) { - ManuallyDrop::drop(val) -} - -#[no_mangle] -pub unsafe extern "C" fn wasmtime_val_copy( - dst: &mut MaybeUninit, +pub unsafe extern "C" fn wasmtime_val_clone( + cx: WasmtimeStoreContextMut<'_>, src: &wasmtime_val_t, + dst: &mut MaybeUninit, ) { - crate::initialize(dst, wasmtime_val_t::from_val(src.to_val())) -} - -#[no_mangle] -pub extern "C" fn wasmtime_externref_new( - data: *mut c_void, - finalizer: Option, -) -> ExternRef { - ExternRef::new(crate::ForeignData { data, finalizer }) -} - -#[no_mangle] -pub extern "C" fn wasmtime_externref_data(externref: ManuallyDrop) -> *mut c_void { - externref - .data() - .downcast_ref::() - .unwrap() - .data -} - -#[no_mangle] -pub extern "C" fn wasmtime_externref_clone(externref: ManuallyDrop) -> ExternRef { - (*externref).clone() -} - -#[no_mangle] -pub extern "C" fn wasmtime_externref_delete(_val: Option) {} - -#[no_mangle] -pub unsafe extern "C" fn wasmtime_externref_to_raw( - cx: CStoreContextMut<'_>, - val: Option>, -) -> *mut c_void { - match val { - Some(ptr) => ptr.to_raw(cx), - None => ptr::null_mut(), - } -} - -#[no_mangle] -pub unsafe extern "C" fn wasmtime_externref_from_raw( - _cx: CStoreContextMut<'_>, - val: *mut c_void, -) -> Option { - ExternRef::from_raw(val) + let mut scope = RootScope::new(cx); + let val = src.to_val(&mut scope); + crate::initialize(dst, wasmtime_val_t::from_val(&mut scope, val)) } diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs index 848409bcc866..3f638cd5bfe1 100644 --- a/crates/c-api/src/wasi.rs +++ b/crates/c-api/src/wasi.rs @@ -2,18 +2,11 @@ use crate::wasm_byte_vec_t; use anyhow::Result; -use cap_std::ambient_authority; -use std::collections::HashMap; -use std::ffi::CStr; +use std::ffi::{c_char, CStr}; use std::fs::File; -use std::os::raw::{c_char, c_int}; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::slice; -use wasi_common::pipe::ReadPipe; -use wasmtime_wasi::{ - sync::{Dir, TcpListener, WasiCtxBuilder}, - WasiCtx, -}; +use wasmtime_wasi::{preview1::WasiP1Ctx, WasiCtxBuilder}; unsafe fn cstr_to_path<'a>(path: *const c_char) -> Option<&'a Path> { CStr::from_ptr(path).to_str().map(Path::new).ok() @@ -32,164 +25,73 @@ unsafe fn create_file(path: *const c_char) -> Option { } #[repr(C)] -#[derive(Default)] pub struct wasi_config_t { - args: Vec>, - env: Vec<(Vec, Vec)>, - stdin: WasiConfigReadPipe, - stdout: WasiConfigWritePipe, - stderr: WasiConfigWritePipe, - preopen_dirs: Vec<(Dir, PathBuf)>, - preopen_sockets: HashMap, - inherit_args: bool, - inherit_env: bool, -} - -#[repr(C)] -#[derive(Default)] -pub enum WasiConfigReadPipe { - #[default] - None, - Inherit, - File(File), - Bytes(Vec), -} - -#[repr(C)] -#[derive(Default)] -pub enum WasiConfigWritePipe { - #[default] - None, - Inherit, - File(File), + builder: WasiCtxBuilder, } wasmtime_c_api_macros::declare_own!(wasi_config_t); impl wasi_config_t { - pub fn into_wasi_ctx(self) -> Result { - let mut builder = WasiCtxBuilder::new(); - if self.inherit_args { - builder.inherit_args()?; - } else if !self.args.is_empty() { - let args = self - .args - .into_iter() - .map(|bytes| Ok(String::from_utf8(bytes)?)) - .collect::>>()?; - builder.args(&args)?; - } - if self.inherit_env { - builder.inherit_env()?; - } else if !self.env.is_empty() { - let env = self - .env - .into_iter() - .map(|(kbytes, vbytes)| { - let k = String::from_utf8(kbytes)?; - let v = String::from_utf8(vbytes)?; - Ok((k, v)) - }) - .collect::>>()?; - builder.envs(&env)?; - } - match self.stdin { - WasiConfigReadPipe::None => {} - WasiConfigReadPipe::Inherit => { - builder.inherit_stdin(); - } - WasiConfigReadPipe::File(file) => { - let file = cap_std::fs::File::from_std(file); - let file = wasi_cap_std_sync::file::File::from_cap_std(file); - builder.stdin(Box::new(file)); - } - WasiConfigReadPipe::Bytes(binary) => { - let binary = ReadPipe::from(binary); - builder.stdin(Box::new(binary)); - } - }; - match self.stdout { - WasiConfigWritePipe::None => {} - WasiConfigWritePipe::Inherit => { - builder.inherit_stdout(); - } - WasiConfigWritePipe::File(file) => { - let file = cap_std::fs::File::from_std(file); - let file = wasi_cap_std_sync::file::File::from_cap_std(file); - builder.stdout(Box::new(file)); - } - }; - match self.stderr { - WasiConfigWritePipe::None => {} - WasiConfigWritePipe::Inherit => { - builder.inherit_stderr(); - } - WasiConfigWritePipe::File(file) => { - let file = cap_std::fs::File::from_std(file); - let file = wasi_cap_std_sync::file::File::from_cap_std(file); - builder.stderr(Box::new(file)); - } - }; - for (dir, path) in self.preopen_dirs { - builder.preopened_dir(dir, path)?; - } - for (fd_num, listener) in self.preopen_sockets { - builder.preopened_socket(fd_num, listener)?; - } - Ok(builder.build()) + pub fn into_wasi_ctx(mut self) -> Result { + Ok(self.builder.build_p1()) } } #[no_mangle] pub extern "C" fn wasi_config_new() -> Box { - Box::new(wasi_config_t::default()) + Box::new(wasi_config_t { + builder: WasiCtxBuilder::new(), + }) } #[no_mangle] pub unsafe extern "C" fn wasi_config_set_argv( config: &mut wasi_config_t, - argc: c_int, + argc: usize, argv: *const *const c_char, -) { - config.args = slice::from_raw_parts(argv, argc as usize) - .iter() - .map(|p| CStr::from_ptr(*p).to_bytes().to_owned()) - .collect(); - config.inherit_args = false; +) -> bool { + for arg in slice::from_raw_parts(argv, argc) { + let arg = match CStr::from_ptr(*arg).to_str() { + Ok(s) => s, + Err(_) => return false, + }; + config.builder.arg(arg); + } + true } #[no_mangle] pub extern "C" fn wasi_config_inherit_argv(config: &mut wasi_config_t) { - config.args.clear(); - config.inherit_args = true; + config.builder.inherit_args(); } #[no_mangle] pub unsafe extern "C" fn wasi_config_set_env( config: &mut wasi_config_t, - envc: c_int, + envc: usize, names: *const *const c_char, values: *const *const c_char, -) { - let names = slice::from_raw_parts(names, envc as usize); - let values = slice::from_raw_parts(values, envc as usize); +) -> bool { + let names = slice::from_raw_parts(names, envc); + let values = slice::from_raw_parts(values, envc); - config.env = names - .iter() - .map(|p| CStr::from_ptr(*p).to_bytes().to_owned()) - .zip( - values - .iter() - .map(|p| CStr::from_ptr(*p).to_bytes().to_owned()), - ) - .collect(); - config.inherit_env = false; + for (k, v) in names.iter().zip(values) { + let k = match cstr_to_str(*k) { + Some(s) => s, + None => return false, + }; + let v = match cstr_to_str(*v) { + Some(s) => s, + None => return false, + }; + config.builder.env(k, v); + } + true } #[no_mangle] pub extern "C" fn wasi_config_inherit_env(config: &mut wasi_config_t) { - config.env.clear(); - config.inherit_env = true; + config.builder.inherit_env(); } #[no_mangle] @@ -202,7 +104,10 @@ pub unsafe extern "C" fn wasi_config_set_stdin_file( None => return false, }; - config.stdin = WasiConfigReadPipe::File(file); + let file = tokio::fs::File::from_std(file); + let stdin_stream = + wasmtime_wasi::AsyncStdinStream::new(wasmtime_wasi::pipe::AsyncReadStream::new(file)); + config.builder.stdin(stdin_stream); true } @@ -213,13 +118,13 @@ pub unsafe extern "C" fn wasi_config_set_stdin_bytes( binary: &mut wasm_byte_vec_t, ) { let binary = binary.take(); - - config.stdin = WasiConfigReadPipe::Bytes(binary); + let binary = wasmtime_wasi::pipe::MemoryInputPipe::new(binary); + config.builder.stdin(binary); } #[no_mangle] pub extern "C" fn wasi_config_inherit_stdin(config: &mut wasi_config_t) { - config.stdin = WasiConfigReadPipe::Inherit; + config.builder.inherit_stdin(); } #[no_mangle] @@ -232,14 +137,14 @@ pub unsafe extern "C" fn wasi_config_set_stdout_file( None => return false, }; - config.stdout = WasiConfigWritePipe::File(file); + config.builder.stdout(wasmtime_wasi::OutputFile::new(file)); true } #[no_mangle] pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) { - config.stdout = WasiConfigWritePipe::Inherit; + config.builder.inherit_stdout(); } #[no_mangle] @@ -252,14 +157,14 @@ pub unsafe extern "C" fn wasi_config_set_stderr_file( None => return false, }; - config.stderr = WasiConfigWritePipe::File(file); + config.builder.stderr(wasmtime_wasi::OutputFile::new(file)); true } #[no_mangle] pub extern "C" fn wasi_config_inherit_stderr(config: &mut wasi_config_t) { - config.stderr = WasiConfigWritePipe::Inherit; + config.builder.inherit_stderr(); } #[no_mangle] @@ -268,51 +173,23 @@ pub unsafe extern "C" fn wasi_config_preopen_dir( path: *const c_char, guest_path: *const c_char, ) -> bool { - let guest_path = match cstr_to_path(guest_path) { + let guest_path = match cstr_to_str(guest_path) { Some(p) => p, None => return false, }; - let dir = match cstr_to_path(path) { - Some(p) => match Dir::open_ambient_dir(p, ambient_authority()) { - Ok(d) => d, - Err(_) => return false, - }, - None => return false, - }; - - (*config).preopen_dirs.push((dir, guest_path.to_owned())); - - true -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_config_preopen_socket( - config: &mut wasi_config_t, - fd_num: u32, - host_port: *const c_char, -) -> bool { - let address = match cstr_to_str(host_port) { - Some(s) => s, + let host_path = match cstr_to_path(path) { + Some(p) => p, None => return false, }; - let listener = match std::net::TcpListener::bind(address) { - Ok(listener) => listener, - Err(_) => return false, - }; - - if let Err(_) = listener.set_nonblocking(true) { - return false; - } - // Caller cannot call in more than once with the same FD number so return an error. - if (*config).preopen_sockets.contains_key(&fd_num) { - return false; - } - - (*config) - .preopen_sockets - .insert(fd_num, TcpListener::from_std(listener)); - - true + config + .builder + .preopened_dir( + host_path, + guest_path, + wasmtime_wasi::DirPerms::all(), + wasmtime_wasi::FilePerms::all(), + ) + .is_ok() } diff --git a/crates/c-api/wasm-c-api b/crates/c-api/wasm-c-api deleted file mode 160000 index c9d31284651b..000000000000 --- a/crates/c-api/wasm-c-api +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c9d31284651b975f05ac27cee0bab1377560b87e diff --git a/crates/cache/Cargo.toml b/crates/cache/Cargo.toml index 0b72cd6b8828..a1510d9ecbe5 100644 --- a/crates/cache/Cargo.toml +++ b/crates/cache/Cargo.toml @@ -7,18 +7,22 @@ license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" documentation = "https://docs.rs/wasmtime-cache/" edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [dependencies] -anyhow = { workspace = true } +anyhow = { workspace = true, features = ['std'] } base64 = "0.21.0" -bincode = "1.1.4" +postcard = { workspace = true } directories-next = "2.0" log = { workspace = true } -serde = "1.0.188" -serde_derive = "1.0.188" +serde = { workspace = true } +serde_derive = { workspace = true } sha2 = "0.10.2" -toml = "0.5.5" -zstd = { version = "0.11.1", default-features = false } +toml = { workspace = true } +zstd = { version = "0.13.0", default-features = false } [target.'cfg(target_os = "windows")'.dependencies.windows-sys] workspace = true diff --git a/crates/cache/build.rs b/crates/cache/build.rs index b57b1ac48833..2d7a7437d3b9 100644 --- a/crates/cache/build.rs +++ b/crates/cache/build.rs @@ -6,5 +6,5 @@ fn main() { Ok(output) => str::from_utf8(&output.stdout).unwrap().trim().to_string(), Err(_) => env!("CARGO_PKG_VERSION").to_string(), }; - println!("cargo:rustc-env=GIT_REV={}", git_rev); + println!("cargo:rustc-env=GIT_REV={git_rev}"); } diff --git a/crates/cache/src/config.rs b/crates/cache/src/config.rs index d7b8f3d4f019..62fc6e936f8e 100644 --- a/crates/cache/src/config.rs +++ b/crates/cache/src/config.rs @@ -153,11 +153,11 @@ const ZSTD_COMPRESSION_LEVELS: std::ops::RangeInclusive = 0..=21; // Default settings, you're welcome to tune them! // TODO: what do we want to warn users about? -// At the moment of writing, the modules couldn't depend on anothers, +// At the moment of writing, the modules couldn't depend on another, // so we have at most one module per wasmtime instance // if changed, update cli-cache.md const DEFAULT_WORKER_EVENT_QUEUE_SIZE: u64 = 0x10; -const WORKER_EVENT_QUEUE_SIZE_WARNING_TRESHOLD: u64 = 3; +const WORKER_EVENT_QUEUE_SIZE_WARNING_THRESHOLD: u64 = 3; // should be quick and provide good enough compression // if changed, update cli-cache.md const DEFAULT_BASELINE_COMPRESSION_LEVEL: i32 = zstd::DEFAULT_COMPRESSION_LEVEL; @@ -407,11 +407,11 @@ impl CacheConfig { match (entity_exists, user_custom_file) { (false, false) => Ok(Self::new_cache_enabled_template()), _ => { - let bytes = fs::read(&config_file).context(format!( + let contents = fs::read_to_string(&config_file).context(format!( "failed to read config file: {}", config_file.display() ))?; - let config = toml::from_slice::(&bytes[..]).context(format!( + let config = toml::from_str::(&contents).context(format!( "failed to parse config file: {}", config_file.display() ))?; @@ -460,7 +460,7 @@ impl CacheConfig { self.worker_event_queue_size = Some(DEFAULT_WORKER_EVENT_QUEUE_SIZE); } - if self.worker_event_queue_size.unwrap() < WORKER_EVENT_QUEUE_SIZE_WARNING_TRESHOLD { + if self.worker_event_queue_size.unwrap() < WORKER_EVENT_QUEUE_SIZE_WARNING_THRESHOLD { warn!("Detected small worker event queue size. Some messages might be lost."); } } diff --git a/crates/cache/src/config/tests.rs b/crates/cache/src/config/tests.rs index 0c1dc873fb69..d361caa4871e 100644 --- a/crates/cache/src/config/tests.rs +++ b/crates/cache/src/config/tests.rs @@ -2,7 +2,7 @@ use super::CacheConfig; use std::fs; use std::path::PathBuf; use std::time::Duration; -use tempfile::{self, TempDir}; +use tempfile::TempDir; // note: config loading during validation creates cache directory to canonicalize its path, // that's why these function and macro always use custom cache directory @@ -19,10 +19,7 @@ pub fn test_prolog() -> (TempDir, PathBuf, PathBuf) { macro_rules! load_config { ($config_path:ident, $content_fmt:expr, $cache_dir:ident) => {{ let config_path = &$config_path; - let content = format!( - $content_fmt, - cache_dir = toml::to_string_pretty(&format!("{}", $cache_dir.display())).unwrap() - ); + let content = format!($content_fmt, cache_dir = $cache_dir.display()); fs::write(config_path, content).expect("Failed to write test config file"); CacheConfig::from_file(Some(config_path)).unwrap() }}; @@ -31,10 +28,7 @@ macro_rules! load_config { macro_rules! bad_config { ($config_path:ident, $content_fmt:expr, $cache_dir:ident) => {{ let config_path = &$config_path; - let content = format!( - $content_fmt, - cache_dir = toml::to_string_pretty(&format!("{}", $cache_dir.display())).unwrap() - ); + let content = format!($content_fmt, cache_dir = $cache_dir.display()); fs::write(config_path, content).expect("Failed to write test config file"); assert!(CacheConfig::from_file(Some(config_path)).is_err()); }}; @@ -60,7 +54,7 @@ fn test_unrecognized_settings() { "unrecognized-setting = 42\n\ [cache]\n\ enabled = true\n\ - directory = {cache_dir}", + directory = '{cache_dir}'", cd ); @@ -68,7 +62,7 @@ fn test_unrecognized_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ unrecognized-setting = 42", cd ); @@ -81,7 +75,7 @@ fn test_all_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'\n\ baseline-compression-level = 3\n\ optimized-compression-level = 20\n\ @@ -102,7 +96,7 @@ fn test_all_settings() { // added some white spaces "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = ' 16\t'\n\ baseline-compression-level = 3\n\ optimized-compression-level =\t 20\n\ @@ -151,7 +145,7 @@ fn test_compression_level_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ baseline-compression-level = 1\n\ optimized-compression-level = 21", cd @@ -164,7 +158,7 @@ fn test_compression_level_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ baseline-compression-level = -1\n\ optimized-compression-level = 21", cd @@ -174,7 +168,7 @@ fn test_compression_level_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ baseline-compression-level = 15\n\ optimized-compression-level = 10", cd @@ -188,7 +182,7 @@ fn test_si_prefix_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '42'\n\ optimized-compression-usage-counter-threshold = '4K'\n\ file-count-soft-limit = '3M'", @@ -203,7 +197,7 @@ fn test_si_prefix_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '2K'\n\ optimized-compression-usage-counter-threshold = '4444T'\n\ file-count-soft-limit = '1P'", @@ -222,7 +216,7 @@ fn test_si_prefix_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '2g'", cd ); @@ -231,7 +225,7 @@ fn test_si_prefix_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ file-count-soft-limit = 1", cd ); @@ -240,7 +234,7 @@ fn test_si_prefix_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ file-count-soft-limit = '-31337'", cd ); @@ -249,7 +243,7 @@ fn test_si_prefix_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ file-count-soft-limit = '3.14M'", cd ); @@ -262,7 +256,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = '76'", cd ); @@ -273,7 +267,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = '42 Mi'", cd ); @@ -284,7 +278,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = '2 Gi'", cd ); @@ -295,7 +289,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = '31337 Ti'", cd ); @@ -306,7 +300,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = '7 Pi'", cd ); @@ -317,7 +311,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = '7M'", cd ); @@ -329,7 +323,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = '7 mi'", cd ); @@ -338,7 +332,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = 1", cd ); @@ -347,7 +341,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = '-31337'", cd ); @@ -356,7 +350,7 @@ fn test_disk_space_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-soft-limit = '3.14Ki'", cd ); @@ -369,7 +363,7 @@ fn test_duration_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ cleanup-interval = '100s'\n\ optimizing-compression-task-timeout = '3m'\n\ allowed-clock-drift-for-files-from-future = '4h'", @@ -390,7 +384,7 @@ fn test_duration_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ cleanup-interval = '2d'\n\ optimizing-compression-task-timeout = '333 m'", cd @@ -410,7 +404,7 @@ fn test_duration_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ optimizing-compression-task-timeout = '333'", cd ); @@ -419,7 +413,7 @@ fn test_duration_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ optimizing-compression-task-timeout = 333", cd ); @@ -428,7 +422,7 @@ fn test_duration_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ optimizing-compression-task-timeout = '10 M'", cd ); @@ -437,7 +431,7 @@ fn test_duration_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ optimizing-compression-task-timeout = '10 min'", cd ); @@ -446,7 +440,7 @@ fn test_duration_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ optimizing-compression-task-timeout = '-10s'", cd ); @@ -455,7 +449,7 @@ fn test_duration_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ optimizing-compression-task-timeout = '1.5m'", cd ); @@ -468,7 +462,7 @@ fn test_percent_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ file-count-limit-percent-if-deleting = '62%'\n\ files-total-size-limit-percent-if-deleting = '23 %'", cd @@ -482,7 +476,7 @@ fn test_percent_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-limit-percent-if-deleting = '23'", cd ); @@ -491,7 +485,7 @@ fn test_percent_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-limit-percent-if-deleting = '22.5%'", cd ); @@ -500,7 +494,7 @@ fn test_percent_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-limit-percent-if-deleting = '0.5'", cd ); @@ -509,7 +503,7 @@ fn test_percent_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-limit-percent-if-deleting = '-1%'", cd ); @@ -518,7 +512,7 @@ fn test_percent_settings() { cp, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ files-total-size-limit-percent-if-deleting = '101%'", cd ); diff --git a/crates/cache/src/lib.rs b/crates/cache/src/lib.rs index a997846c1af1..88dbcfd953e1 100644 --- a/crates/cache/src/lib.rs +++ b/crates/cache/src/lib.rs @@ -2,11 +2,11 @@ use base64::Engine; use log::{debug, trace, warn}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; -use std::fs; use std::hash::Hash; use std::hash::Hasher; use std::io::Write; use std::path::{Path, PathBuf}; +use std::{fs, io}; #[macro_use] // for tests mod config; @@ -27,7 +27,7 @@ struct Sha256Hasher(Sha256); impl<'config> ModuleCacheEntry<'config> { /// Create the cache entry. - pub fn new<'data>(compiler_name: &str, cache_config: &'config CacheConfig) -> Self { + pub fn new(compiler_name: &str, cache_config: &'config CacheConfig) -> Self { if cache_config.enabled() { Self(Some(ModuleCacheEntryInner::new( compiler_name, @@ -54,8 +54,8 @@ impl<'config> ModuleCacheEntry<'config> { self.get_data_raw( &state, compute, - |_state, data| bincode::serialize(data).ok(), - |_state, data| bincode::deserialize(&data).ok(), + |_state, data| postcard::to_allocvec(data).ok(), + |_state, data| postcard::from_bytes(&data).ok(), ) } @@ -108,7 +108,7 @@ impl<'config> ModuleCacheEntry<'config> { } impl<'config> ModuleCacheEntryInner<'config> { - fn new<'data>(compiler_name: &str, cache_config: &'config CacheConfig) -> Self { + fn new(compiler_name: &str, cache_config: &'config CacheConfig) -> Self { // If debug assertions are enabled then assume that we're some sort of // local build. We don't want local builds to stomp over caches between // builds, so just use a separate cache directory based on the mtime of @@ -172,7 +172,7 @@ impl<'config> ModuleCacheEntryInner<'config> { // Optimize syscalls: first, try writing to disk. It should succeed in most cases. // Otherwise, try creating the cache directory and retry writing to the file. - if fs_write_atomic(&mod_cache_path, "mod", &compressed_data) { + if fs_write_atomic(&mod_cache_path, "mod", &compressed_data).is_ok() { return Some(()); } @@ -193,10 +193,16 @@ impl<'config> ModuleCacheEntryInner<'config> { }) .ok()?; - if fs_write_atomic(&mod_cache_path, "mod", &compressed_data) { - Some(()) - } else { - None + match fs_write_atomic(&mod_cache_path, "mod", &compressed_data) { + Ok(_) => Some(()), + Err(err) => { + warn!( + "Failed to write file with rename, target path: {}, err: {}", + mod_cache_path.display(), + err + ); + None + } } } } @@ -214,8 +220,8 @@ impl Hasher for Sha256Hasher { // Assumption: path inside cache directory. // Then, we don't have to use sound OS-specific exclusive file access. // Note: there's no need to remove temporary file here - cleanup task will do it later. -fn fs_write_atomic(path: &Path, reason: &str, contents: &[u8]) -> bool { - let lock_path = path.with_extension(format!("wip-atomic-write-{}", reason)); +fn fs_write_atomic(path: &Path, reason: &str, contents: &[u8]) -> io::Result<()> { + let lock_path = path.with_extension(format!("wip-atomic-write-{reason}")); fs::OpenOptions::new() .create_new(true) // atomic file creation (assumption: no one will open it without this flag) .write(true) @@ -223,15 +229,6 @@ fn fs_write_atomic(path: &Path, reason: &str, contents: &[u8]) -> bool { .and_then(|mut file| file.write_all(contents)) // file should go out of scope and be closed at this point .and_then(|()| fs::rename(&lock_path, &path)) // atomic file rename - .map_err(|err| { - warn!( - "Failed to write file with rename, lock path: {}, target path: {}, err: {}", - lock_path.display(), - path.display(), - err - ) - }) - .is_ok() } #[cfg(test)] diff --git a/crates/cache/src/tests.rs b/crates/cache/src/tests.rs index 4362aaba2222..f901a009cd1f 100644 --- a/crates/cache/src/tests.rs +++ b/crates/cache/src/tests.rs @@ -1,8 +1,7 @@ use super::config::tests::test_prolog; use super::*; -use std::fs; -// Since cache system is a global thing, each test needs to be run in seperate process. +// Since cache system is a global thing, each test needs to be run in separate process. // So, init() tests are run as integration tests. // However, caching is a private thing, an implementation detail, and needs to be tested // from the inside of the module. @@ -15,9 +14,9 @@ fn test_cache_init() { let config_content = format!( "[cache]\n\ enabled = true\n\ - directory = {}\n\ + directory = '{}'\n\ baseline-compression-level = {}\n", - toml::to_string_pretty(&format!("{}", cache_dir.display())).unwrap(), + cache_dir.display(), baseline_compression_level, ); fs::write(&config_path, config_content).expect("Failed to write test config file"); @@ -47,7 +46,7 @@ fn test_write_read_cache() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ baseline-compression-level = 3\n", cache_dir ); diff --git a/crates/cache/src/worker.rs b/crates/cache/src/worker.rs index 72a53f94c192..78561c104eca 100644 --- a/crates/cache/src/worker.rs +++ b/crates/cache/src/worker.rs @@ -259,13 +259,13 @@ impl WorkerThread { } /// Increases the usage counter and recompresses the file - /// if the usage counter reached configurable treshold. + /// if the usage counter reached configurable threshold. fn handle_on_cache_get(&self, path: PathBuf) { trace!("handle_on_cache_get() for path: {}", path.display()); // construct .stats file path let filename = path.file_name().unwrap().to_str().unwrap(); - let stats_path = path.with_file_name(format!("{}.stats", filename)); + let stats_path = path.with_file_name(format!("{filename}.stats")); // load .stats file (default if none or error) let mut stats = read_stats_file(stats_path.as_ref()) @@ -399,7 +399,7 @@ impl WorkerThread { .expect("Expected valid cache file name") .to_str() .expect("Expected valid cache file name"); - let stats_path = path.with_file_name(format!("{}.stats", filename)); + let stats_path = path.with_file_name(format!("{filename}.stats")); // create and write stats file let mut stats = ModuleCacheStatistics::default(&self.cache_config); @@ -728,7 +728,7 @@ impl WorkerThread { } fn read_stats_file(path: &Path) -> Option { - fs::read(path) + fs::read_to_string(path) .map_err(|err| { trace!( "Failed to read stats file, path: {}, err: {}", @@ -736,8 +736,8 @@ fn read_stats_file(path: &Path) -> Option { err ) }) - .and_then(|bytes| { - toml::from_slice::(&bytes[..]).map_err(|err| { + .and_then(|contents| { + toml::from_str::(&contents).map_err(|err| { trace!( "Failed to parse stats file, path: {}, err: {}", path.display(), @@ -758,11 +758,7 @@ fn write_stats_file(path: &Path, stats: &ModuleCacheStatistics) -> bool { ) }) .and_then(|serialized| { - if fs_write_atomic(path, "stats", serialized.as_bytes()) { - Ok(()) - } else { - Err(()) - } + fs_write_atomic(path, "stats", serialized.as_bytes()).map_err(|_| ()) }) .is_ok() } @@ -851,7 +847,7 @@ fn acquire_task_fs_lock( // we have either both, or just path; dir entry is desirable since on some platforms we can get // metadata without extra syscalls -// futhermore: it's better to get a path if we have it instead of allocating a new one from the dir entry +// furthermore: it's better to get a path if we have it instead of allocating a new one from the dir entry fn is_fs_lock_expired( entry: Option<&fs::DirEntry>, path: &PathBuf, diff --git a/crates/cache/src/worker/tests.rs b/crates/cache/src/worker/tests.rs index d2652c6c2d28..7392962d77d0 100644 --- a/crates/cache/src/worker/tests.rs +++ b/crates/cache/src/worker/tests.rs @@ -15,7 +15,7 @@ fn test_on_get_create_stats_file() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}", + directory = '{cache_dir}'", cache_dir ); assert!(cache_config.enabled()); @@ -42,7 +42,7 @@ fn test_on_get_update_usage_counter() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'", cache_dir ); @@ -76,7 +76,7 @@ fn test_on_get_recompress_no_mod_file() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'\n\ baseline-compression-level = 3\n\ optimized-compression-level = 7\n\ @@ -118,7 +118,7 @@ fn test_on_get_recompress_with_mod_file() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'\n\ baseline-compression-level = 3\n\ optimized-compression-level = 7\n\ @@ -193,7 +193,7 @@ fn test_on_get_recompress_lock() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'\n\ baseline-compression-level = 3\n\ optimized-compression-level = 7\n\ @@ -263,7 +263,7 @@ fn test_on_update_fresh_stats_file() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'\n\ baseline-compression-level = 3\n\ optimized-compression-level = 7\n\ @@ -312,7 +312,7 @@ fn test_on_update_cleanup_limits_trash_locks() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'\n\ cleanup-interval = '30m'\n\ optimizing-compression-task-timeout = '30m'\n\ @@ -453,7 +453,7 @@ fn test_on_update_cleanup_lru_policy() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'\n\ file-count-soft-limit = '5'\n\ files-total-size-soft-limit = '30K'\n\ @@ -521,8 +521,8 @@ fn test_on_update_cleanup_lru_policy() { let filenames = (0..mods.len()) .map(|i| { ( - mods_files_dir.join(format!("mod-{}", i)), - mods_files_dir.join(format!("mod-{}.stats", i)), + mods_files_dir.join(format!("mod-{i}")), + mods_files_dir.join(format!("mod-{i}.stats")), ) }) .collect::>(); @@ -585,7 +585,7 @@ fn test_on_update_cleanup_future_files() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'\n\ allowed-clock-drift-for-files-from-future = '1d'\n\ file-count-soft-limit = '3'\n\ @@ -605,12 +605,12 @@ fn test_on_update_cleanup_future_files() { let worker_lock_file = cache_dir.join(format!(".cleanup.wip-{}", process::id())); let scenarios: [&[_]; 5] = [ - // NOT cleaning up, everythings ok + // NOT cleaning up, everything is ok &[ (Duration::from_secs(0), None, true), (Duration::from_secs(24 * 60 * 60), None, true), ], - // NOT cleaning up, everythings ok + // NOT cleaning up, everything is ok &[ (Duration::from_secs(0), None, true), (Duration::from_secs(24 * 60 * 60 + 1), None, true), @@ -646,8 +646,8 @@ fn test_on_update_cleanup_future_files() { let filenames = (0..mods.len()) .map(|i| { ( - mods_files_dir.join(format!("mod-{}", i)), - mods_files_dir.join(format!("mod-{}.stats", i)), + mods_files_dir.join(format!("mod-{i}")), + mods_files_dir.join(format!("mod-{i}.stats")), ) }) .collect::>(); @@ -693,7 +693,7 @@ fn test_on_update_cleanup_self_lock() { config_path, "[cache]\n\ enabled = true\n\ - directory = {cache_dir}\n\ + directory = '{cache_dir}'\n\ worker-event-queue-size = '16'\n\ cleanup-interval = '30m'\n\ allowed-clock-drift-for-files-from-future = '1d'", diff --git a/crates/cli-flags/Cargo.toml b/crates/cli-flags/Cargo.toml index e15b4d14f149..832b86214da8 100644 --- a/crates/cli-flags/Cargo.toml +++ b/crates/cli-flags/Cargo.toml @@ -7,23 +7,29 @@ license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" documentation = "https://docs.rs/wasmtime-cache/" edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [dependencies] -anyhow = { workspace = true } +anyhow = { workspace = true, features = ['std'] } clap = { workspace = true } -file-per-thread-logger = { workspace = true } -pretty_env_logger = { workspace = true } -rayon = "1.5.0" -wasmtime = { workspace = true } -humantime = "2.0.0" +file-per-thread-logger = { workspace = true, optional = true } +tracing-subscriber = { workspace = true, optional = true } +rayon = { version = "1.5.0", optional = true } +wasmtime = { workspace = true, features = ["gc"] } +humantime = { workspace = true } [features] -default = [ - "wasmtime/cache", - "wasmtime/cranelift", - "wasmtime/jitdump", - "wasmtime/vtune", - "wasmtime/parallel-compilation", -] -pooling-allocator = [] +async = ["wasmtime/async"] +pooling-allocator = ["wasmtime/pooling-allocator"] component-model = ["wasmtime/component-model"] +cache = ["wasmtime/cache"] +parallel-compilation = ["wasmtime/parallel-compilation", "dep:rayon"] +logging = ["dep:file-per-thread-logger", "dep:tracing-subscriber"] +cranelift = ["wasmtime/cranelift"] +coredump = ["wasmtime/coredump"] +gc = ["wasmtime/gc"] +threads = ["wasmtime/threads"] +memory-protection-keys = ["wasmtime/memory-protection-keys"] diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index db5ba3abc300..f9f51b6b7d3e 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -1,8 +1,5 @@ //! Contains the common Wasmtime command line interface (CLI) flags. -#![deny(trivial_numeric_casts, unused_extern_crates, unstable_features)] -#![warn(unused_import_braces)] - use anyhow::Result; use clap::Parser; use std::time::Duration; @@ -10,13 +7,16 @@ use wasmtime::Config; pub mod opt; -fn init_file_per_thread_logger(prefix: &'static str) { +#[cfg(feature = "logging")] +fn init_file_per_thread_logger(prefix: &str) { file_per_thread_logger::initialize(prefix); + file_per_thread_logger::allow_uninitialized(); // Extending behavior of default spawner: // https://docs.rs/rayon/1.1.0/rayon/struct.ThreadPoolBuilder.html#method.spawn_handler // Source code says DefaultSpawner is implementation detail and // shouldn't be used directly. + #[cfg(feature = "parallel-compilation")] rayon::ThreadPoolBuilder::new() .spawn_handler(move |thread| { let mut b = std::thread::Builder::new(); @@ -26,8 +26,9 @@ fn init_file_per_thread_logger(prefix: &'static str) { if let Some(stack_size) = thread.stack_size() { b = b.stack_size(stack_size); } + let prefix = prefix.to_string(); b.spawn(move || { - file_per_thread_logger::initialize(prefix); + file_per_thread_logger::initialize(&prefix); thread.run() })?; Ok(()) @@ -37,8 +38,9 @@ fn init_file_per_thread_logger(prefix: &'static str) { } wasmtime_option_group! { + #[derive(PartialEq, Clone)] pub struct OptimizeOptions { - /// Optimization level of generated code (0-2, s; default: 0) + /// Optimization level of generated code (0-2, s; default: 2) pub opt_level: Option, /// Byte size of the guard region after dynamic memories are allocated @@ -58,12 +60,70 @@ wasmtime_option_group! { /// memories. pub dynamic_memory_reserved_for_growth: Option, + /// Indicates whether an unmapped region of memory is placed before all + /// linear memories. + pub guard_before_linear_memory: Option, + + /// Whether to initialize tables lazily, so that instantiation is + /// fast but indirect calls are a little slower. If no, tables are + /// initialized eagerly from any active element segments that apply to + /// them during instantiation. (default: yes) + pub table_lazy_init: Option, + /// Enable the pooling allocator, in place of the on-demand allocator. pub pooling_allocator: Option, + /// The number of decommits to do per batch. A batch size of 1 + /// effectively disables decommit batching. (default: 1) + pub pooling_decommit_batch_size: Option, + + /// How many bytes to keep resident between instantiations for the + /// pooling allocator in linear memories. + pub pooling_memory_keep_resident: Option, + + /// How many bytes to keep resident between instantiations for the + /// pooling allocator in tables. + pub pooling_table_keep_resident: Option, + + /// Enable memory protection keys for the pooling allocator; this can + /// optimize the size of memory slots. + pub memory_protection_keys: Option, + /// Configure attempting to initialize linear memory via a /// copy-on-write mapping (default: yes) pub memory_init_cow: Option, + + /// The maximum number of WebAssembly instances which can be created + /// with the pooling allocator. + pub pooling_total_core_instances: Option, + + /// The maximum number of WebAssembly components which can be created + /// with the pooling allocator. + pub pooling_total_component_instances: Option, + + /// The maximum number of WebAssembly memories which can be created with + /// the pooling allocator. + pub pooling_total_memories: Option, + + /// The maximum number of WebAssembly tables which can be created with + /// the pooling allocator. + pub pooling_total_tables: Option, + + /// The maximum number of WebAssembly stacks which can be created with + /// the pooling allocator. + pub pooling_total_stacks: Option, + + /// The maximum runtime size of each linear memory in the pooling + /// allocator, in bytes. + pub pooling_max_memory_size: Option, + + /// The maximum table elements for any table defined in a module when + /// using the pooling allocator. + pub pooling_table_elements: Option, + + /// The maximum size, in bytes, allocated for a core instance's metadata + /// when using the pooling allocator. + pub pooling_max_core_instance_size: Option, } enum Optimize { @@ -72,6 +132,7 @@ wasmtime_option_group! { } wasmtime_option_group! { + #[derive(PartialEq, Clone)] pub struct CodegenOptions { /// Either `cranelift` or `winch`. /// @@ -86,6 +147,8 @@ 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, #[prefixed = "cranelift"] /// Set a cranelift-specific option. Use `wasmtime settings` to see @@ -99,6 +162,7 @@ wasmtime_option_group! { } wasmtime_option_group! { + #[derive(PartialEq, Clone)] pub struct DebugOptions { /// Enable generation of DWARF debug information in compiled code. pub debug_info: Option, @@ -108,6 +172,8 @@ wasmtime_option_group! { pub logging: Option, /// Configure whether logs are emitted to files pub log_to_files: Option, + /// Log file prefix + pub log_prefix: Option, /// Enable coredump generation to this file after a WebAssembly trap. pub coredump: Option, } @@ -118,6 +184,7 @@ wasmtime_option_group! { } wasmtime_option_group! { + #[derive(PartialEq, Clone)] pub struct WasmOptions { /// Enable canonicalization of all NaN values. pub nan_canonicalization: Option, @@ -198,8 +265,16 @@ wasmtime_option_group! { pub memory64: Option, /// Configure support for the component-model proposal. pub component_model: Option, + /// Configure support for 33+ flags in the component model. + pub component_model_more_flags: Option, + /// Component model support for more than one return value. + pub component_model_multiple_returns: Option, /// Configure support for the function-references proposal. pub function_references: Option, + /// Configure support for the GC proposal. + pub gc: Option, + /// Configure support for the custom-page-sizes proposal. + pub custom_page_sizes: Option, } enum Wasm { @@ -208,21 +283,28 @@ wasmtime_option_group! { } wasmtime_option_group! { + #[derive(PartialEq, Clone)] pub struct WasiOptions { - /// Enable support for WASI common APIs + /// Enable support for WASI CLI APIs, including filesystems, sockets, clocks, and random. + pub cli: Option, + /// Deprecated alias for `cli` pub common: Option, - /// Enable suport for WASI neural network API (experimental) + /// Enable support for WASI neural network API (experimental) pub nn: Option, - /// Enable suport for WASI threading API (experimental) + /// Enable support for WASI threading API (experimental) pub threads: Option, - /// Enable suport for WASI HTTP API (experimental) + /// Enable support for WASI HTTP API (experimental) pub http: Option, + /// Enable support for WASI runtime config API (experimental) + pub runtime_config: Option, + /// Enable support for WASI key-value API (experimental) + pub keyvalue: Option, /// Inherit environment variables and file descriptors following the /// systemd listen fd specification (UNIX only) pub listenfd: Option, /// Grant access to the given TCP listen socket pub tcplisten: Vec, - /// Implement WASI with preview2 primitives (experimental). + /// Implement WASI CLI APIs with preview2 primitives (experimental). /// /// Indicates that the implementation of WASI preview1 should be backed by /// the preview2 implementation for components. @@ -244,7 +326,20 @@ wasmtime_option_group! { pub inherit_network: Option, /// Indicates whether `wasi:sockets/ip-name-lookup` is enabled or not. pub allow_ip_name_lookup: Option, - + /// Indicates whether `wasi:sockets` TCP support is enabled or not. + pub tcp: Option, + /// Indicates whether `wasi:sockets` UDP support is enabled or not. + pub udp: Option, + /// Allows imports from the `wasi_unstable` core wasm module. + pub preview0: Option, + /// Inherit all environment variables from the parent process. + /// + /// This option can be further overwritten with `--env` flags. + pub inherit_env: Option, + /// Pass a wasi runtime config variable to the program. + pub runtime_config_var: Vec, + /// Preset data for the In-Memory provider of WASI key-value API. + pub keyvalue_in_memory_data: Vec, } enum Wasi { @@ -252,14 +347,20 @@ wasmtime_option_group! { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct WasiNnGraph { pub format: String, pub dir: String, } +#[derive(Debug, Clone, PartialEq)] +pub struct KeyValuePair { + pub key: String, + pub value: String, +} + /// Common options for commands that translate WebAssembly modules -#[derive(Parser)] +#[derive(Parser, Clone)] pub struct CommonOptions { // These options groups are used to parse `-O` and such options but aren't // the raw form consumed by the CLI. Instead they're pushed into the `pub` @@ -270,42 +371,63 @@ pub struct CommonOptions { // now. /// Optimization and tuning related options for wasm performance, `-O help` to /// see all. - #[clap(short = 'O', long = "optimize", value_name = "KEY[=VAL[,..]]")] + #[arg(short = 'O', long = "optimize", value_name = "KEY[=VAL[,..]]")] opts_raw: Vec>, /// Codegen-related configuration options, `-C help` to see all. - #[clap(short = 'C', long = "codegen", value_name = "KEY[=VAL[,..]]")] + #[arg(short = 'C', long = "codegen", value_name = "KEY[=VAL[,..]]")] codegen_raw: Vec>, /// Debug-related configuration options, `-D help` to see all. - #[clap(short = 'D', long = "debug", value_name = "KEY[=VAL[,..]]")] + #[arg(short = 'D', long = "debug", value_name = "KEY[=VAL[,..]]")] debug_raw: Vec>, /// Options for configuring semantic execution of WebAssembly, `-W help` to see /// all. - #[clap(short = 'W', long = "wasm", value_name = "KEY[=VAL[,..]]")] + #[arg(short = 'W', long = "wasm", value_name = "KEY[=VAL[,..]]")] wasm_raw: Vec>, /// Options for configuring WASI and its proposals, `-S help` to see all. - #[clap(short = 'S', long = "wasi", value_name = "KEY[=VAL[,..]]")] + #[arg(short = 'S', long = "wasi", value_name = "KEY[=VAL[,..]]")] wasi_raw: Vec>, // These fields are filled in by the `configure` method below via the // options parsed from the CLI above. This is what the CLI should use. - #[clap(skip)] + #[arg(skip)] configured: bool, - #[clap(skip)] + #[arg(skip)] pub opts: OptimizeOptions, - #[clap(skip)] + #[arg(skip)] pub codegen: CodegenOptions, - #[clap(skip)] + #[arg(skip)] pub debug: DebugOptions, - #[clap(skip)] + #[arg(skip)] pub wasm: WasmOptions, - #[clap(skip)] + #[arg(skip)] pub wasi: WasiOptions, } +macro_rules! match_feature { + ( + [$feat:tt : $config:expr] + $val:ident => $e:expr, + $p:pat => err, + ) => { + #[cfg(feature = $feat)] + { + if let Some($val) = $config { + $e; + } + } + #[cfg(not(feature = $feat))] + { + if let Some($p) = $config { + anyhow::bail!(concat!("support for ", $feat, " disabled at compile time")); + } + } + }; +} + impl CommonOptions { fn configure(&mut self) { if self.configured { @@ -319,51 +441,87 @@ impl CommonOptions { self.wasi.configure_with(&self.wasi_raw); } - pub fn init_logging(&mut self) { + pub fn init_logging(&mut self) -> Result<()> { self.configure(); if self.debug.logging == Some(false) { - return; + return Ok(()); } + #[cfg(feature = "logging")] if self.debug.log_to_files == Some(true) { - let prefix = "wasmtime.dbg."; + let default_prefix = "wasmtime.dbg."; + let prefix = self + .debug + .log_prefix + .as_ref() + .map_or(default_prefix, |p| p.as_str()); init_file_per_thread_logger(prefix); } else { - pretty_env_logger::init(); + use std::io::IsTerminal; + use tracing_subscriber::{EnvFilter, FmtSubscriber}; + let b = FmtSubscriber::builder() + .with_writer(std::io::stderr) + .with_env_filter(EnvFilter::from_env("WASMTIME_LOG")) + .with_ansi(std::io::stderr().is_terminal()); + b.init(); + } + #[cfg(not(feature = "logging"))] + if self.debug.log_to_files == Some(true) || self.debug.logging == Some(true) { + anyhow::bail!("support for logging disabled at compile time"); } + Ok(()) } - pub fn config(&mut self, target: Option<&str>) -> Result { + pub fn config( + &mut self, + target: Option<&str>, + pooling_allocator_default: Option, + ) -> Result { self.configure(); let mut config = Config::new(); - if let Some(strategy) = self.codegen.compiler { - config.strategy(strategy); + match_feature! { + ["cranelift" : self.codegen.compiler] + strategy => config.strategy(strategy), + _ => err, } - - // Set the target before setting any cranelift options, since the - // target will reset any target-specific options. - if let Some(target) = target { - config.target(target)?; + match_feature! { + ["cranelift" : target] + target => config.target(target)?, + _ => err, } - - if let Some(enable) = self.codegen.cranelift_debug_verifier { - config.cranelift_debug_verifier(enable); + match_feature! { + ["cranelift" : self.codegen.cranelift_debug_verifier] + enable => config.cranelift_debug_verifier(enable), + true => err, } if let Some(enable) = self.debug.debug_info { config.debug_info(enable); } if self.debug.coredump.is_some() { + #[cfg(feature = "coredump")] config.coredump_on_trap(true); + #[cfg(not(feature = "coredump"))] + anyhow::bail!("support for coredumps disabled at compile time"); + } + match_feature! { + ["cranelift" : self.opts.opt_level] + level => config.cranelift_opt_level(level), + _ => err, } - if let Some(level) = self.opts.opt_level { - config.cranelift_opt_level(level); + match_feature! { + ["cranelift" : self.wasm.nan_canonicalization] + enable => config.cranelift_nan_canonicalization(enable), + true => err, } - if let Some(enable) = self.wasm.nan_canonicalization { - config.cranelift_nan_canonicalization(enable); + match_feature! { + ["cranelift" : self.codegen.pcc] + enable => config.cranelift_pcc(enable), + true => err, } self.enable_wasm_features(&mut config)?; + #[cfg(feature = "cranelift")] for (name, value) in self.codegen.cranelift.iter() { let name = name.replace('-', "_"); unsafe { @@ -377,7 +535,12 @@ impl CommonOptions { } } } + #[cfg(not(feature = "cranelift"))] + if !self.codegen.cranelift.is_empty() { + anyhow::bail!("support for cranelift disabled at compile time"); + } + #[cfg(feature = "cache")] if self.codegen.cache != Some(false) { match &self.codegen.cache_config { Some(path) => { @@ -388,9 +551,15 @@ impl CommonOptions { } } } + #[cfg(not(feature = "cache"))] + if self.codegen.cache == Some(true) { + anyhow::bail!("support for caching disabled at compile time"); + } - if let Some(enable) = self.codegen.parallel_compilation { - config.parallel_compilation(enable); + match_feature! { + ["parallel-compilation" : self.codegen.parallel_compilation] + enable => config.parallel_compilation(enable), + true => err, } if let Some(max) = self.opts.static_memory_maximum_size { @@ -411,6 +580,12 @@ impl CommonOptions { if let Some(size) = self.opts.dynamic_memory_reserved_for_growth { config.dynamic_memory_reserved_for_growth(size); } + if let Some(enable) = self.opts.guard_before_linear_memory { + config.guard_before_linear_memory(enable); + } + if let Some(enable) = self.opts.table_lazy_init { + config.table_lazy_init(enable); + } // If fuel has been configured, set the `consume fuel` flag on the config. if self.wasm.fuel.is_some() { @@ -427,11 +602,62 @@ impl CommonOptions { config.memory_init_cow(enable); } - if self.opts.pooling_allocator == Some(true) { - #[cfg(feature = "pooling-allocator")] - config.allocation_strategy(wasmtime::InstanceAllocationStrategy::pooling()); - #[cfg(not(feature = "pooling-allocator"))] - anyhow::bail!("support for the pooling allocator was disabled at compile-time"); + match_feature! { + ["pooling-allocator" : self.opts.pooling_allocator.or(pooling_allocator_default)] + enable => { + if enable { + let mut cfg = wasmtime::PoolingAllocationConfig::default(); + if let Some(size) = self.opts.pooling_memory_keep_resident { + cfg.linear_memory_keep_resident(size); + } + if let Some(size) = self.opts.pooling_table_keep_resident { + cfg.table_keep_resident(size); + } + if let Some(limit) = self.opts.pooling_total_core_instances { + cfg.total_core_instances(limit); + } + if let Some(limit) = self.opts.pooling_total_component_instances { + cfg.total_component_instances(limit); + } + if let Some(limit) = self.opts.pooling_total_memories { + cfg.total_memories(limit); + } + if let Some(limit) = self.opts.pooling_total_tables { + cfg.total_tables(limit); + } + if let Some(limit) = self.opts.pooling_table_elements { + cfg.table_elements(limit); + } + if let Some(limit) = self.opts.pooling_max_core_instance_size { + cfg.max_core_instance_size(limit); + } + match_feature! { + ["async" : self.opts.pooling_total_stacks] + limit => cfg.total_stacks(limit), + _ => err, + } + if let Some(limit) = self.opts.pooling_max_memory_size { + cfg.max_memory_size(limit); + } + match_feature! { + ["memory-protection-keys" : self.opts.memory_protection_keys] + enable => cfg.memory_protection_keys(if enable { + wasmtime::MpkEnabled::Enable + } else { + wasmtime::MpkEnabled::Disable + }), + _ => err, + } + config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling(cfg)); + } + }, + true => err, + } + + if self.opts.memory_protection_keys.unwrap_or(false) + && !self.opts.pooling_allocator.unwrap_or(false) + { + anyhow::bail!("memory protection keys require the pooling allocator"); } if let Some(max) = self.wasm.max_wasm_stack { @@ -441,8 +667,10 @@ impl CommonOptions { if let Some(enable) = self.wasm.relaxed_simd_deterministic { config.relaxed_simd_deterministic(enable); } - if let Some(enable) = self.wasm.wmemcheck { - config.wmemcheck(enable); + match_feature! { + ["cranelift" : self.wasm.wmemcheck] + enable => config.wmemcheck(enable), + true => err, } Ok(config) @@ -460,35 +688,72 @@ impl CommonOptions { if let Some(enable) = self.wasm.bulk_memory.or(all) { config.wasm_bulk_memory(enable); } - if let Some(enable) = self.wasm.reference_types.or(all) { - config.wasm_reference_types(enable); - } - if let Some(enable) = self.wasm.function_references.or(all) { - config.wasm_function_references(enable); - } if let Some(enable) = self.wasm.multi_value.or(all) { config.wasm_multi_value(enable); } if let Some(enable) = self.wasm.tail_call.or(all) { config.wasm_tail_call(enable); } - if let Some(enable) = self.wasm.threads.or(all) { - config.wasm_threads(enable); - } if let Some(enable) = self.wasm.multi_memory.or(all) { config.wasm_multi_memory(enable); } if let Some(enable) = self.wasm.memory64.or(all) { config.wasm_memory64(enable); } - if let Some(enable) = self.wasm.component_model.or(all) { - #[cfg(feature = "component-model")] - config.wasm_component_model(enable); - #[cfg(not(feature = "component-model"))] - if enable && all.is_none() { - anyhow::bail!("support for the component model was disabled at compile-time"); - } + if let Some(enable) = self.wasm.custom_page_sizes.or(all) { + config.wasm_custom_page_sizes(enable); + } + + macro_rules! handle_conditionally_compiled { + ($(($feature:tt, $field:tt, $method:tt))*) => ($( + if let Some(enable) = self.wasm.$field.or(all) { + #[cfg(feature = $feature)] + config.$method(enable); + #[cfg(not(feature = $feature))] + if enable && all.is_none() { + anyhow::bail!("support for {} was disabled at compile-time", $feature); + } + } + )*) + } + + handle_conditionally_compiled! { + ("component-model", component_model, wasm_component_model) + ("component-model", component_model_more_flags, wasm_component_model_more_flags) + ("component-model", component_model_multiple_returns, wasm_component_model_multiple_returns) + ("threads", threads, wasm_threads) + ("gc", gc, wasm_gc) + ("gc", reference_types, wasm_reference_types) + ("gc", function_references, wasm_function_references) } Ok(()) } } + +impl PartialEq for CommonOptions { + fn eq(&self, other: &CommonOptions) -> bool { + let mut me = self.clone(); + me.configure(); + let mut other = other.clone(); + other.configure(); + let CommonOptions { + opts_raw: _, + codegen_raw: _, + debug_raw: _, + wasm_raw: _, + wasi_raw: _, + configured: _, + + opts, + codegen, + debug, + wasm, + wasi, + } = me; + opts == other.opts + && codegen == other.codegen + && debug == other.debug + && wasm == other.wasm + && wasi == other.wasi + } +} diff --git a/crates/cli-flags/src/opt.rs b/crates/cli-flags/src/opt.rs index 18a3622bbd58..f1d4af3e4a4c 100644 --- a/crates/cli-flags/src/opt.rs +++ b/crates/cli-flags/src/opt.rs @@ -5,7 +5,7 @@ //! specifying options in a struct-like syntax where all other boilerplate about //! option parsing is contained exclusively within this module. -use crate::WasiNnGraph; +use crate::{KeyValuePair, WasiNnGraph}; use anyhow::{bail, Result}; use clap::builder::{StringValueParser, TypedValueParser, ValueParserFactory}; use clap::error::{Error, ErrorKind}; @@ -15,6 +15,7 @@ use std::time::Duration; #[macro_export] macro_rules! wasmtime_option_group { ( + $(#[$attr:meta])* pub struct $opts:ident { $( $(#[doc = $doc:tt])* @@ -32,6 +33,7 @@ macro_rules! wasmtime_option_group { } ) => { #[derive(Default, Debug)] + $(#[$attr])* pub struct $opts { $( pub $opt: $container<$payload>, @@ -41,7 +43,7 @@ macro_rules! wasmtime_option_group { )? } - #[derive(Clone, Debug)] + #[derive(Clone, Debug,PartialEq)] #[allow(non_camel_case_types)] enum $option { $( @@ -106,7 +108,7 @@ macro_rules! wasmtime_option_group { } /// Parser registered with clap which handles parsing the `...` in `-O ...`. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct CommaSeparated(pub Vec); impl ValueParserFactory for CommaSeparated @@ -299,7 +301,10 @@ impl WasmtimeOptionValue for u32 { const VAL_HELP: &'static str = "=N"; fn parse(val: Option<&str>) -> Result { let val = String::parse(val)?; - Ok(val.parse()?) + match val.strip_prefix("0x") { + Some(hex) => Ok(u32::from_str_radix(hex, 16)?), + None => Ok(val.parse()?), + } } } @@ -307,7 +312,10 @@ impl WasmtimeOptionValue for u64 { const VAL_HELP: &'static str = "=N"; fn parse(val: Option<&str>) -> Result { let val = String::parse(val)?; - Ok(val.parse()?) + match val.strip_prefix("0x") { + Some(hex) => Ok(u64::from_str_radix(hex, 16)?), + None => Ok(val.parse()?), + } } } @@ -315,7 +323,10 @@ impl WasmtimeOptionValue for usize { const VAL_HELP: &'static str = "=N"; fn parse(val: Option<&str>) -> Result { let val = String::parse(val)?; - Ok(val.parse()?) + match val.strip_prefix("0x") { + Some(hex) => Ok(usize::from_str_radix(hex, 16)?), + None => Ok(val.parse()?), + } } } @@ -366,10 +377,7 @@ impl WasmtimeOptionValue for wasmtime::Strategy { match String::parse(val)?.as_str() { "cranelift" => Ok(wasmtime::Strategy::Cranelift), "winch" => Ok(wasmtime::Strategy::Winch), - other => bail!( - "unknown optimization level `{}`, only 0,1,2,s accepted", - other - ), + other => bail!("unknown compiler `{other}` only `cranelift` and `winch` accepted",), } } } @@ -388,3 +396,18 @@ impl WasmtimeOptionValue for WasiNnGraph { }) } } + +impl WasmtimeOptionValue for KeyValuePair { + const VAL_HELP: &'static str = "=="; + fn parse(val: Option<&str>) -> Result { + let val = String::parse(val)?; + let mut parts = val.splitn(2, "="); + Ok(KeyValuePair { + key: parts.next().unwrap().to_string(), + value: match parts.next() { + Some(part) => part.into(), + None => "".to_string(), + }, + }) + } +} diff --git a/crates/component-macro/Cargo.toml b/crates/component-macro/Cargo.toml index 069a560f5bb3..79dbc6a27353 100644 --- a/crates/component-macro/Cargo.toml +++ b/crates/component-macro/Cargo.toml @@ -9,6 +9,10 @@ documentation = "https://docs.rs/wasmtime-component-macro/" categories = ["wasm"] keywords = ["webassembly", "wasm"] edition.workspace = true +rust-version.workspace = true + +[lints] +workspace = true [lib] proc-macro = true @@ -28,6 +32,12 @@ wit-parser = { workspace = true } wasmtime = { path = '../wasmtime', features = ['component-model'] } component-macro-test-helpers = { path = 'test-helpers' } tracing = { workspace = true } +# For use with the custom attributes test +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +prettyplease = "0.2.19" +similar = { workspace = true } [features] async = [] +std = ['wasmtime-wit-bindgen/std'] diff --git a/crates/component-macro/build.rs b/crates/component-macro/build.rs new file mode 100644 index 000000000000..be0ad5bc97cb --- /dev/null +++ b/crates/component-macro/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let out_dir = std::env::var("OUT_DIR").unwrap(); + println!("cargo:rustc-env=DEBUG_OUTPUT_DIR={out_dir}"); +} diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs index a87616e55d82..9b0a6a1d90ad 100644 --- a/crates/component-macro/src/bindgen.rs +++ b/crates/component-macro/src/bindgen.rs @@ -1,18 +1,22 @@ use proc_macro2::{Span, TokenStream}; +use quote::ToTokens; use std::collections::HashMap; use std::collections::HashSet; +use std::env; use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::punctuated::Punctuated; -use syn::{braced, token, Ident, Token}; -use wasmtime_wit_bindgen::{AsyncConfig, Opts, Ownership, TrappableError}; -use wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId}; +use syn::{braced, token, Token}; +use wasmtime_wit_bindgen::{AsyncConfig, Opts, Ownership, TrappableError, TrappableImports}; +use wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId}; pub struct Config { opts: Opts, resolve: Resolve, world: WorldId, files: Vec, + include_generated_code_from_file: bool, } pub fn expand(input: &Config) -> Result { @@ -23,7 +27,38 @@ pub fn expand(input: &Config) -> Result { )); } - let src = input.opts.generate(&input.resolve, input.world); + let mut src = match input.opts.generate(&input.resolve, input.world) { + Ok(s) => s, + Err(e) => return Err(Error::new(Span::call_site(), e.to_string())), + }; + + if input.opts.stringify { + return Ok(quote::quote!(#src)); + } + + // If a magical `WASMTIME_DEBUG_BINDGEN` environment variable is set then + // place a formatted version of the expanded code into a file. This file + // will then show up in rustc error messages for any codegen issues and can + // be inspected manually. + if input.include_generated_code_from_file || std::env::var("WASMTIME_DEBUG_BINDGEN").is_ok() { + static INVOCATION: AtomicUsize = AtomicUsize::new(0); + let root = Path::new(env!("DEBUG_OUTPUT_DIR")); + let world_name = &input.resolve.worlds[input.world].name; + let n = INVOCATION.fetch_add(1, Relaxed); + let path = root.join(format!("{world_name}{n}.rs")); + + std::fs::write(&path, &src).unwrap(); + + // optimistically format the code but don't require success + drop( + std::process::Command::new("rustfmt") + .arg(&path) + .arg("--edition=2021") + .output(), + ); + + src = format!("include!({path:?});"); + } let mut contents = src.parse::().unwrap(); // Include a dummy `include_str!` for any files we read so rustc knows that @@ -47,6 +82,8 @@ impl Parse for Config { let mut inline = None; let mut path = None; let mut async_configured = false; + let mut features = Vec::new(); + let mut include_generated_code_from_file = false; if input.peek(token::Brace) { let content; @@ -81,6 +118,7 @@ impl Parse for Config { opts.async_ = val; } Opt::TrappableErrorType(val) => opts.trappable_error_type = val, + Opt::TrappableImports(val) => opts.trappable_imports = val, Opt::Ownership(val) => opts.ownership = val, Opt::Interfaces(s) => { if inline.is_some() { @@ -88,7 +126,7 @@ impl Parse for Config { } inline = Some(format!( " - package wasmtime:component-macro-synthesized + package wasmtime:component-macro-synthesized; world interfaces {{ {} @@ -108,6 +146,22 @@ impl Parse for Config { opts.only_interfaces = true; } Opt::With(val) => opts.with.extend(val), + Opt::AdditionalDerives(paths) => { + opts.additional_derive_attributes = paths + .into_iter() + .map(|p| p.into_token_stream().to_string()) + .collect() + } + Opt::Stringify(val) => opts.stringify = val, + Opt::SkipMutForwardingImpls(val) => opts.skip_mut_forwarding_impls = val, + Opt::Features(f) => { + features.extend(f.into_iter().map(|f| f.value())); + } + Opt::RequireStoreDataSend(val) => opts.require_store_data_send = val, + Opt::WasmtimeCrate(f) => { + opts.wasmtime_crate = Some(f.into_token_stream().to_string()) + } + Opt::IncludeGeneratedCodeFromFile(i) => include_generated_code_from_file = i, } } } else { @@ -116,7 +170,7 @@ impl Parse for Config { path = Some(input.parse::()?.value()); } } - let (resolve, pkg, files) = parse_source(&path, &inline) + let (resolve, pkg, files) = parse_source(&path, &inline, &features) .map_err(|err| Error::new(call_site, format!("{err:?}")))?; let world = resolve @@ -127,6 +181,7 @@ impl Parse for Config { resolve, world, files, + include_generated_code_from_file, }) } } @@ -134,40 +189,43 @@ impl Parse for Config { fn parse_source( path: &Option, inline: &Option, + features: &[String], ) -> anyhow::Result<(Resolve, PackageId, Vec)> { let mut resolve = Resolve::default(); + resolve.features.extend(features.iter().cloned()); let mut files = Vec::new(); let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); let mut parse = |resolve: &mut Resolve, path: &Path| -> anyhow::Result<_> { - if path.is_dir() { - let (pkg, sources) = resolve.push_dir(path)?; - files = sources; - Ok(pkg) - } else { - let pkg = UnresolvedPackage::parse_file(path)?; - files.extend(pkg.source_files().map(|s| s.to_owned())); - resolve.push(pkg) - } + // Try to normalize the path to make the error message more understandable when + // the path is not correct. Fallback to the original path if normalization fails + // (probably return an error somewhere else). + let normalized_path = match std::fs::canonicalize(path) { + Ok(p) => p, + Err(_) => path.to_path_buf(), + }; + let (pkg, sources) = resolve.push_path(normalized_path)?; + files.extend(sources); + Ok(pkg) }; let path_pkg = if let Some(path) = path { - Some(parse(&mut resolve, &root.join(&path))?) + Some(parse(&mut resolve, &root.join(path))?) } else { None }; - let inline_pkg = if let Some(inline) = inline { - Some(resolve.push(UnresolvedPackage::parse("macro-input".as_ref(), &inline)?)?) + let inline_pkgs = if let Some(inline) = inline { + Some(resolve.push_group(UnresolvedPackageGroup::parse("macro-input", inline)?)?) } else { None }; - let pkg = inline_pkg + let pkgs = inline_pkgs .or(path_pkg) .map_or_else(|| parse(&mut resolve, &root.join("wit")), Ok)?; - Ok((resolve, pkg, files)) + Ok((resolve, pkgs, files)) } mod kw { @@ -181,6 +239,14 @@ mod kw { syn::custom_keyword!(with); syn::custom_keyword!(except_imports); syn::custom_keyword!(only_imports); + syn::custom_keyword!(trappable_imports); + syn::custom_keyword!(additional_derives); + syn::custom_keyword!(stringify); + syn::custom_keyword!(skip_mut_forwarding_impls); + syn::custom_keyword!(features); + syn::custom_keyword!(require_store_data_send); + syn::custom_keyword!(wasmtime_crate); + syn::custom_keyword!(include_generated_code_from_file); } enum Opt { @@ -193,6 +259,14 @@ enum Opt { Ownership(Ownership), Interfaces(syn::LitStr), With(HashMap), + TrappableImports(TrappableImports), + AdditionalDerives(Vec), + Stringify(bool), + SkipMutForwardingImpls(bool), + Features(Vec), + RequireStoreDataSend(bool), + WasmtimeCrate(syn::Path), + IncludeGeneratedCodeFromFile(bool), } impl Parse for Opt { @@ -297,7 +371,7 @@ impl Parse for Opt { let _lbrace = braced!(contents in input); let fields: Punctuated<_, Token![,]> = contents.parse_terminated(trappable_error_field_parse, Token![,])?; - Ok(Opt::TrappableErrorType(Vec::from_iter(fields.into_iter()))) + Ok(Opt::TrappableErrorType(Vec::from_iter(fields))) } else if l.peek(kw::interfaces) { input.parse::()?; input.parse::()?; @@ -309,7 +383,63 @@ impl Parse for Opt { let _lbrace = braced!(contents in input); let fields: Punctuated<(String, String), Token![,]> = contents.parse_terminated(with_field_parse, Token![,])?; - Ok(Opt::With(HashMap::from_iter(fields.into_iter()))) + Ok(Opt::With(HashMap::from_iter(fields))) + } else if l.peek(kw::trappable_imports) { + input.parse::()?; + input.parse::()?; + let config = if input.peek(syn::LitBool) { + match input.parse::()?.value { + true => TrappableImports::All, + false => TrappableImports::None, + } + } else { + let contents; + syn::bracketed!(contents in input); + let fields: Punctuated = + contents.parse_terminated(Parse::parse, Token![,])?; + TrappableImports::Only(fields.iter().map(|s| s.value()).collect()) + }; + Ok(Opt::TrappableImports(config)) + } else if l.peek(kw::additional_derives) { + input.parse::()?; + input.parse::()?; + let contents; + syn::bracketed!(contents in input); + let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?; + Ok(Opt::AdditionalDerives(list.iter().cloned().collect())) + } else if l.peek(kw::stringify) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Stringify(input.parse::()?.value)) + } else if l.peek(kw::skip_mut_forwarding_impls) { + input.parse::()?; + input.parse::()?; + Ok(Opt::SkipMutForwardingImpls( + input.parse::()?.value, + )) + } else if l.peek(kw::features) { + input.parse::()?; + input.parse::()?; + let contents; + syn::bracketed!(contents in input); + let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?; + Ok(Opt::Features(list.into_iter().collect())) + } else if l.peek(kw::require_store_data_send) { + input.parse::()?; + input.parse::()?; + Ok(Opt::RequireStoreDataSend( + input.parse::()?.value, + )) + } else if l.peek(kw::wasmtime_crate) { + input.parse::()?; + input.parse::()?; + Ok(Opt::WasmtimeCrate(input.parse()?)) + } else if l.peek(kw::include_generated_code_from_file) { + input.parse::()?; + input.parse::()?; + Ok(Opt::IncludeGeneratedCodeFromFile( + input.parse::()?.value, + )) } else { Err(l.error()) } @@ -317,28 +447,11 @@ impl Parse for Opt { } fn trappable_error_field_parse(input: ParseStream<'_>) -> Result { - // Accept a Rust identifier or a string literal. This is required - // because not all wit identifiers are Rust identifiers, so we can - // smuggle the invalid ones inside quotes. - fn ident_or_str(input: ParseStream<'_>) -> Result { - let l = input.lookahead1(); - if l.peek(syn::LitStr) { - Ok(input.parse::()?.value()) - } else if l.peek(syn::Ident) { - Ok(input.parse::()?.to_string()) - } else { - Err(l.error()) - } - } - - let wit_package_path = input.parse::()?.value(); - input.parse::()?; - let wit_type_name = ident_or_str(input)?; - input.parse::()?; - let rust_type_name = input.parse::()?.to_string(); + let wit_path = input.parse::()?.value(); + input.parse::]>()?; + let rust_type_name = input.parse::()?.to_token_stream().to_string(); Ok(TrappableError { - wit_package_path, - wit_type_name, + wit_path, rust_type_name, }) } diff --git a/crates/component-macro/src/component.rs b/crates/component-macro/src/component.rs index 0f49ef07a819..a4c44e784607 100644 --- a/crates/component-macro/src/component.rs +++ b/crates/component-macro/src/component.rs @@ -1,10 +1,10 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use std::collections::HashSet; use std::fmt; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::{braced, parse_quote, Data, DeriveInput, Error, Result, Token}; +use syn::{braced, parse_quote, Data, DeriveInput, Error, Ident, Result, Token}; use wasmtime_component_util::{DiscriminantSize, FlagsSize}; mod kw { @@ -12,62 +12,48 @@ mod kw { syn::custom_keyword!(variant); syn::custom_keyword!(flags); syn::custom_keyword!(name); -} - -#[derive(Debug, Copy, Clone)] -pub enum VariantStyle { - Variant, - Enum, -} - -impl fmt::Display for VariantStyle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - Self::Variant => "variant", - Self::Enum => "enum", - }) - } + syn::custom_keyword!(wasmtime_crate); } #[derive(Debug, Copy, Clone)] enum Style { Record, - Variant(VariantStyle), + Enum, + Variant, } -fn find_style(input: &DeriveInput) -> Result